Should I use boot or tools.deps to build command line applications?


I messed with this about a year ago. It runs clj twice, so it’s not ideal, but I like the way it looks.



I’ve been tweaking my implementation. Here it is!

It now includes arguments, a place for other options, and it doesn’t depend on bash. I’ve included it below, but you can also find the most recent version in this gist.


   #_DEPS is same format as deps.edn. Multiline is okay.
   {:deps {clj-time {:mvn/version "0.14.2"}}}

   #_You can put other options here
   -J-Xms256m -J-Xmx256m -J-client

exec clojure $OPTS -Sdeps "$DEPS" "$0" "[email protected]"


(println "Hello!")

(require '[clj-time.core :as t])

(prn (str (t/now)))

(prn *command-line-args*)

(println (.. (Runtime/getRuntime)


I’m using both boot and clj for command line scripts and I’ve also done some Haskell: I must say it’s a great fit for this.

This is a standalone script using clj: It uses a trick comparable to @ericnormand’s script above to include the deps configuration.

I later decided to port it to Haskell for fun but then it turned into more than one file. I’m still running that on my server:

Here’s an example of a standalone boot script that you can call with e.g. ./foobar -t:

#!/usr/bin/env boot

(set-env! :dependencies '[[clj-time "0.14.2"]])
(require '[clj-time.core :as time])

(deftask my-task [t time bool "Show time"]
  (when (:time *opts*)
    (println (time/now))))

(defn -main [& args]
  (apply boot "my-task" args))

If you already have boot installed and you want a single file standalone script, I’d say boot works well without crazy shell hacks. The startup time of clj might be a little better due to classpath caching. Lastly, Haskell probably has much better start times for scripts :slight_smile:.


That is a piece of art.


I wonder if @alexmiller has a plan to add “in-script” deps.edn support to tools.deps?


Also, to the people suggesting Haskel for scripts, how do you bring in the dependencies if you need any?



My take on self-containing scripts:

It allows for scripts like:

#!/usr/bin/env cljscript

(require '[clj-time.core :as t])

(defn -main [& args]
  (println (t/now)))

-main is invoked when present.


The new incarnation of this script is ohmyclj.
It adds a repl and an easy way to run tests in the script.