With the power of boot, it’s possible to go from “never used java before” to budding Clojure-ist cranking out jars like a pickle factory in record time. This post walks you through the process, and provides some post-‘hello world’ examples, with pointers to more information.

Dales la Bota (Give ’em The Boot) Boot is ‘installed‘ by simply downloading an executable file and putting it somewhere where you can execute it: $ wget https://github.com/boot-clj/boot/releases/download/2.0.0-rc13/boot.sh $ mv boot.sh boot && chmod a+x boot && sudo mv boot /usr/local/bin The real magic happens when boot is run. Boot sets everything up in a .boot directory in your home folder. Without having any code to execute yet, you can trigger this by simply asking boot for help: $ boot -h Let’s Play With Clojure Clojure utilizes a concept called a REPL (Read, Evaluate, Print, Loop). REPLs allow you to interactively run code and experiment. $ boot repl Boot then provides you with a prompt, where you can play around: boot.user=> (+ 1 2 3 4 5) 15 boot.user=> (/ 10 0) java.lang.ArithmeticException: Divide by zero Boot also works as a scripting platform – you can construct applications, specifying dependencies, and parse command-line arguments.

Here’s a simple Clojure function that prints the fibonacci sequence to a given number of digits: (defn fib ([n] (fib [0 1] n)) ([pair, n] (print (first pair) " ") (if (> n 0) (fib [(second pair) (apply + pair)] (- n 1)) (println))))

You can paste this into your REPL and try it out: boot.user=> (defn fib #_=> ([n] #_=> (fib [0 1] n)) #_=> ([pair, n] #_=> (print (first pair) " ") #_=> (if (> n 0) #_=> (fib [(second pair) (apply + pair)] (- n 1)) #_=> (println)))) #'boot.user/fib boot.user=> (fib 10) 0 1 1 2 3 5 8 13 21 34 55 nil

We can transform that function into a command-line tool using the power of boot scripting. Assume this file is called fib.boot :

#!/usr/bin/env boot (defn fib ([n] (fib [0 1] n)) ([pair, n] (print (first pair) " ") (if (> n 0) (fib [(second pair) (apply + pair)] (- n 1)) (println)))) (defn -main [& args] (let [limit (first args)] (println "Printing fibonacci sequence up to " limit "numbers") (fib (Integer/parseInt limit))))

Make the script executable:

$ chmod u+x fib.boot

Now you can run the script:

$ ./fib.boot 10 Printing fibonacci sequence up to 10 numbers 0 1 1 2 3 5 8 13 21 34

The script can declare dependencies, which will be downloaded as needed when the script is run. Here, we’ll show the use of an external dependency: we can write a new fibonacci sequence that utilizes the fact that numbers in the sequence are related to each other by approximately the golden ratio (ca 1.62). Rounding makes it all work, but rounding isn’t “baked in” to Clojure, so we’ll use an external library to do it for us, called math.numeric-tower. Ok, actually, it’s there, you just need to use some existing Java libraries to make it work – I admit this is a bit of a strain!

#!/usr/bin/env boot (set-env! :dependencies '[[org.clojure/math.numeric-tower "0.0.4"]]) (require '[clojure.math.numeric-tower :refer [floor ceil round]]) (defn fib [n] (loop [counter 0 x 0] (if (= counter 0) (do (print 0 " " 1 " " 1 " ") (recur 3 1)) (let [y (round (* x 1.62))] (print y " ") (if (< counter 9) (recur (+ counter 1) y)))))) (defn -main [& args] (let [limit (first args)] (println "Printing fibonacci sequence up to" limit "numbers") (fib (Integer/parseInt limit)) (println)))

When you run this code the first time, you’ll notice boot tells you that it’s downloaded some new jars:

$ ./fib.boot Retrieving clojure-1.4.0.jar from http://clojars.org/repo/ Retrieving math.numeric-tower-0.0.4.jar from http://repo1.maven.org/maven2/ Printing fibonacci sequence up to 10 numbers 0 1 1 2 3 5 8 13 21 34

The syntax to define our -main function and parse our command line options can be a bit tedious. Luckily, we can borrow a macro from boot.core that lets us specify CLI options using a robust syntax. For the full syntax, check out the documentation. Here, we’ll let the user choose which implementation they’d like to use, and utilize the task DSL to do some simple command line options:

#!/usr/bin/env boot (set-env! :dependencies '[[org.clojure/math.numeric-tower "0.0.4"]]) (require '[clojure.math.numeric-tower :refer [floor ceil round]]) (require '[boot.cli :as cli]) (defn fib ([n] (fib [0 1] n)) ([pair, n] (print (first pair) " ") (if (> n 1) (fib [(second pair) (apply + pair)] (- n 1))))) (defn fibgolden [n] (loop [counter 0 x 0] (if (= counter 0) (do (print (str 0 " " 1 " " 1 " ")) (recur 3 1)) (let [y (round (* x 1.62))] (print y " ") (if (< counter 9) (recur (+ counter 1) y)))))) (cli/defclifn -main "Print a fibonacci sequence to stdout using one of two algorithms." [g golden bool "Use the golden mean to calculate" n number NUMBER int "Quantity of numbers to generate. Defaults to 10"] (let [n (get :n *opts* 10) note (if golden "[golden]" "[recursive]")] (println note "Printing fibonacci sequence up to" n "numbers:") (if golden (fibgolden n) (fib n))) (println))

Now you can see what options are available, tell the script what to do:

$ boot fib.boot -h Print a fibonacci sequence to stdout using one of two algorithms. Options: -h, --help Print this help info. -g, --golden Use the golden mean to calculate -n, --number NUMBER Set quantity of numbers to generate. Defaults to 10 to NUMBER. $ boot fib.boot [recursive] Printing fibonacci sequence up to 10 numbers: 0 1 1 2 3 5 8 13 21 34 $ boot fib.boot -g -n 20 [recursive] Printing fibonacci sequence up to 20 numbers: 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181

Working At The Pickle Factory (Packing Java Jars and More Complex Projects)

Now that we’ve got a basic feel for Clojure and using boot, we can build a project, that creates a library with an entry point that we can use and distribute as a jar file. This opens the doors to being able to deploy web applications, build libraries to share, and distribute standalone applications. First, we need to create a project structure. This will help us keep things organized, and fit in with the way Clojure handles namespaces and files. We’ll put our source code in src , and create a new namespace, called fib.core :

$ mkdir -p src/fib

In src/fib/core.clj , we’ll declare our new namespace:

(ns fib.core (:require [clojure.math.numeric-tower :refer [floor ceil round]] [boot.cli :as cli]) (:gen-class)) (defn fib ([n] (fib [0 1] n)) ([pair, n] (print (first pair) " ") (if (> n 1) (fib [(second pair) (apply + pair)] (- n 1))))) (defn fibgolden [n] (loop [counter 0 x 0] (if (= counter 0) (do (print (str 0 " " 1 " " 1 " ")) (recur 3 1)) (let [y (round (* x 1.62))] (print y " ") (if (< counter 9) (recur (+ counter 1) y)))))) (cli/defclifn -main "Print a fibonacci sequence to stdout using one of two algorithms." [g golden bool "Use the golden mean to calculate" n number NUMBER int "Quantity of numbers to generate. Defaults to 10"] (let [n (if number number 10) note (if golden "[golden]" "[recursive]")] (println note "Printing fibonacci sequence up to" n "numbers:") (if golden (fibgolden n) (fib n))) (println))

To build our jar, there are a handful of steps:

Download our dependencies. Compile our clojure code ahead of time (aka AOT). Add a POM file describing our project and the version. Scan all of our dependencies and add them to the fileset to be put into the jar. Build the jar, specifying a module containing a -main function to run when the jar is invoked.

Helpfully, boot provides built-in functionality to do this for us. Each step is implemented as a boot task. Tasks act as a pipeline: the result of each can influence the next.

boot -d org.clojure/clojure:1.6.0 \ -d boot/core:2.0.0-rc8 \ -d org.clojure/math.numeric-tower:0.0.4 \ -s src/ \ aot -a \ pom -p fib -v 1.0.0 \ uber \ jar -m fib.core

A brief explanation of each task and command line options:

Line 1-3: the -d option specifies a dependency. Here we list Clojure itself, boot.core , and math.numeric-tower .

Line 4: -s specifies a source directory to look into for .clj files.

Line 5: this is the AOT task, that compiles all of the .clj files for us. The -a flag tells the task to compile everything it finds.

Line 6: the POM task. This task adds project information to the jar. The -p option specifies the project name, -v is the version.

Line 7: the uber task collects the dependencies so they can be baked into the jar file. This makes the jar big (huge really), but it ends up being self-contained.

Line 8: finally, the jar task. This is the task that actually generates the jar file. The -m option specifies which module has the -main function. Running the above command, produces output something like this:

$ boot -d org.clojure/clojure:1.6.0 \ > -d boot/core:2.0.0-rc8 \ > -d org.clojure/math.numeric-tower:0.0.4 \ > -s src/ \ > aot -a \ > pom -p fib -v 1.0.0 \ > uber \ > jar -m fib.core Compiling fib.core... Writing pom.xml and pom.properties... Adding uberjar entries... Writing fib-1.0.0.jar...

At this point, there is a file named fib-1.0.0.jar in the target directory. We can use the java command to run it:

$ java -jar target/fib-1.0.0.jar [recursive] Printing fibonacci sequence up to 10 numbers: 0 1 1 2 3 5 8 13 21 34

You can send this file to a friend, and they can use it too.



Introducing build.boot

At this point we have a project and can build a standalone jar file from it. This is great, but long command lines are prone to error. Boot provides a mechanism for defining your own tasks and setting the command line options in a single file, named build.boot. Here’s a build.boot that configures boot in a manner equivalent to the command line switches above:

(set-env! :dependencies '[[org.clojure/math.numeric-tower "0.0.4"] [boot/core "2.0.0-rc8"] [org.clojure/clojure "1.6.0"]] :source-paths #{"src/"}) (task-options! pom {:project 'fib :version "1.0.0"} jar {:main 'fib.core} aot {:all true})

With build.boot in the current directory, you can now run the tasks like this:

$ boot aot pom uber jar Compiling fib.core... Writing pom.xml and pom.properties... Adding uberjar entries... Writing fib-1.0.0.jar...

The convenience of build.boot one step further, we can chain the tasks we want to use into our own task, using the deftask macro:

(set-env! :dependencies '[[org.clojure/math.numeric-tower "0.0.4"] [boot/core "2.0.0-rc8"] [org.clojure/clojure "1.6.0"]] :source-paths #{"src/"}) (task-options! pom {:project 'fib :version "1.0.0"} jar {:main 'fib.core} aot {:all true}) (deftask build "Create a standalone jar file that computes fibonacci sequences." [] (comp (aot) (pom) (uber) (jar)))

Now, we can just run boot build to make our standalone jar file. You’ll also see your task show up in the help output:

$ boot -h ... build Create a standalone jar file that computes fibonacci sequences. ... $ boot build Compiling fib.core... Writing pom.xml and pom.properties... Adding uberjar entries... Writing fib-1.0.0.jar...

Where To Go From Here

At this point we’ve touched most of the awesomeness that boot gives us. With these basic tools, there’s all sorts of interesting things we can do next. Here are some ideas: