Alan Dipert (@alandipert) and Micha Niskin (@micha)
for Boot, slides and help with Boot intel.
Juno Terepi (@deraen)
for slides and help with Lein pitfals
for being always on and always helpful
and now, fasten your seatbelts, let's begin...
## 3(!) JVMs
lein do cljsbuild, compile
JVM 1 | JVM 2 | JVM 3 |
---|---|---|
lein |
cljsbuild |
compile |
most lein commands would fork yet another JVM
- slow startup
- large memory footprint
It is possible to pass data from task to task, but you need to usually manually set the paths for both tasks
- "manually" ensure that task A's
target-path
is the same as task B'sinput-dir
- each plugin has to reimplement file watching logic
single tmp
dir called target
=> immutable? I think not.
anything weird is going on during development? first thing would be to:
lein clean
also don't forget to provide all the :clean-targets
or clean them manually after lein clean
In case there is:
- an
error
in a lein plugin or - it needs
customization
- or just
needs to be understood
internally + some undocumented features, etc.
the lein plugin implementation is implicit: i.e. hidden behind the lein config DSL which makes it hard(er) to understand / debug / customize
.
$ boot pom -- jar -- install
Writing pom.xml and pom.properties...
Writing cprop-0.1.7-SNAPSHOT.jar...
Installing cprop-0.1.7-SNAPSHOT.jar...
add some visual in between:
$ boot show -f -- pom -- jar -- show -f -- install
cprop
├── core.clj
├── source.clj
└── tools.clj
Writing pom.xml and pom.properties...
Writing cprop-0.1.7-SNAPSHOT.jar...
META-INF
└── maven
└── cprop
└── cprop
├── pom.properties
└── pom.xml
cprop
├── core.clj
├── source.clj
└── tools.clj
cprop-0.1.7-SNAPSHOT.jar
Installing cprop-0.1.7-SNAPSHOT.jar...
boot.user=> (boot (pom) (jar) (install))
Writing pom.xml and pom.properties...
Writing cprop-0.1.7-SNAPSHOT.jar...
Installing cprop-0.1.7-SNAPSHOT.jar...
boot.user=> (boot (show "-f") (pom) (jar) (install))
config.edn
cprop
├── core.clj
├── source.clj
├── test
│ └── core.clj
└── tools.clj
fill-me-in.edn
Writing pom.xml and pom.properties...
Writing cprop-0.1.7-SNAPSHOT.jar...
Installing cprop-0.1.7-SNAPSHOT.jar...
Process | Connective | |
---|---|---|
Unix Shell | program | text |
Boot | task | FileSet |
walkthorugh a cprop's build.boot as an example
talk about:
- env
- composing tasks
- boot libs
- tasks args
walkthorugh a mount's build.boot as an example
talk about:
(cljs-repl)
starts a cljs RPEL server(reload)
automatically reload resources in the browser when files in the project change(serve)
starts a webserver that will serve our compiled JS / CSS and anything else that is in "resources"(test-cljs)
composed custom task to run cljs tests
$ boot cljs-dev
$ boot repl -c
## start the Weasel server and attach this REPL client to running browser environment
boot.user=> (start-repl)
<< started Weasel server on ws://127.0.0.1:65190 >>
<< waiting for client to connect ...
Connection is ws://localhost:65190
Writing boot_cljs_repl.cljs...
connected! >>
To quit, type: :cljs/quit
nil
cljs.user=> (js/alert "boot: \"greetings clojuredelphians!\"")
$ boot watch speak test-cljs
change something in test/cljs/...
listen to the music of success
cljs packages example
walkthogh a simple boot-stripper task:
(deftask strip-deps-attr
"strips out an attribute (optionally identified by a value) from all the dependencies"
[a attr ATTR kw "the name of the attribute (i.e. \"classifier\") to strip out"
v value VALUE edn "the optional value of the attribute to strip out"]
(when-not attr
(boot.util/fail "The -a/--attr option is required!\n") (*usage*))
(set-env! :dependencies #(mapv (partial strip-out attr value) %))
identity)
- isolating dependencies into completely independent Clojure runtimes
- pod's templates via backtick
examples from boot-check:
(let [pod-pool (make-pod-pool (concat pod-deps eastwood-deps) bootstrap)]
(core/with-pre-wrap fileset
(eastwood/check pod-pool fileset) ;; TODO with args
fileset)))
(defn check [pod-pool fileset & args]
(let [worker-pod (pod-pool :refresh)]
(pod/with-eval-in worker-pod
(require '[eastwood.lint :as eastwood])
;; ~(boot.core/load-data-readers!)
(let [sources# #{~@(tmp-dir-paths fileset)}
_ (boot.util/dbug (str "eastwood is about to look at: -- " sources# " --"))
{:keys [some-warnings]} (eastwood/eastwood {:source-paths sources#
;; :debug #{:ns}
})]
(if some-warnings
(boot.util/warn (str "\nWARN: eastwood found some problems ^^^ \n\n"))
(boot.util/info "\nlatest report from eastwood.... [You Rock!]\n"))))))
see those :directories
:
[cprop]$ boot show -e
{:watcher-debounce 10,
:dependencies
[[org.clojure/clojure "1.8.0"]
[boot/core "2.5.1" :scope "provided"]
[adzerk/bootlaces "0.1.13" :scope "test"]
[adzerk/boot-test "1.0.6" :scope "test"]
[tolitius/boot-check "0.1.1" :scope "test"]],
:directories
#{"/Users/you/.boot/cache/tmp/Users/you/fun/cprop/cp8/f0sqpx"
"/Users/you/.boot/cache/tmp/Users/you/fun/cprop/cp8/mh5540"
"/Users/you/.boot/cache/tmp/Users/you/fun/cprop/cp8/mm3x96"
"/Users/you/.boot/cache/tmp/Users/you/fun/cprop/cp8/-rcsl8f"},
:source-paths #{"src"},
:resource-paths #{"src"},
:asset-paths #{},
:target-path "target",
:repositories
[["clojars" {:url "https://clojars.org/repo/"}]
["maven-central" {:url "https://repo1.maven.org/maven2"}]],
:config
{"BOOT_CLOJURE_NAME" "org.clojure/clojure", "BOOT_FILE" "build.boot", "BOOT_CLOJURE_VERSION" "1.7.0", "BOOT_VERSION" "2.5.5", "BOOT_EMIT_TARGET" "no"}}
isolated + immutable = reliable
show-conf.sh
#!/usr/bin/env boot
(set-env! :dependencies '[[cprop "0.1.6"]])
(require '[clojure.pprint :refer [pprint]]
'[cprop.core :refer [load-config]]
'[cprop.source :refer [from-env]])
(defn -main [& args]
(let [with-env? (some #{"+env"} (set args))
env (if with-env? (from-env) {})]
(pprint (load-config :file "config.edn"
:merge [{:args args} env]))))
with some args:
$ ./show-conf.sh foo bar baz
{:datomic
{:url
"datomic:sql://?jdbc:postgresql://localhost:5432/datomic?user=datomic&password=datomic"},
:source
{:account
{:rabbit
{:host "127.0.0.1",
:port 5672,
:vhost "/z-broker",
:username "guest",
:password "guest"}}},
:answer 42,
:args ("foo" "bar" "baz")}
with all ENV variables:
$ ./show-conf.clj +env
...
The big thing going for Boot CLI scripts are libraries such as those dealing with AWS, rabbitmq, etc, which are available in maven, and can be simply used in "boot" scripts.
show off boot-new
boot -d seancorfield/boot-new new -t app -n whatsapp
boot run
boot test
clean and simple:
# -p, --pedantic Print graph of dependency conflicts.
$ boot show -p
[!] clj-time
✔ 0.9.0
compojure
✘ 0.3.7
ring/ring-jetty-adapter
[!] commons-codec
✘ 1.6
compojure
ring/ring-jetty-adapter
✔ 1.5
com.datomic/datomic-free
[!] commons-fileupload
✔ 1.3.1
compojure
✘ 1.2.1
ring/ring-jetty-adapter
[!] commons-io
✔ 2.4
compojure
✘ 2.1
ring/ring-jetty-adapter
[!] joda-time
✔ 2.6
compojure
✘ 2.0
ring/ring-jetty-adapter
[!] com.fasterxml.jackson.core/jackson-core
✔ 2.5.3
cheshire
✘ 2.3.2
com.datomic/datomic-free
[✔] org.clojure/clojure
✘ 1.7.0-RC1
weasel
✔ 1.7.0
com.andrewmcveigh/cljs-time
org.clojure/clojure
org.clojure/clojurescript
✘ 1.6.0
com.cemerick/piggieback
com.datomic/datomic-free
✘ 1.5.1
cheshire
compojure
hiccups
✘ 1.4.0
org.clojure/tools.logging
org.clojure/tools.namespace
✘ 1.2.1
ring/ring-jetty-adapter
✘ 1.2.0
org.clojure/tools.nrepl
Boot always uses single JVM for all the work Boot + Tasks.
It uses Pods (separate classloaders) to prevent dependency hell and to isolate artifacts between tasks / runs.
Boot tasks are functions. Functions compose.
Boot tasks create their own temporaty directories for artifacts when they are needed, i.e.
:directories
#{"/Users/you/.boot/cache/tmp/Users/you/fun/cprop/cp8/f0sqpx"
"/Users/you/.boot/cache/tmp/Users/you/fun/cprop/cp8/mh5540"
"/Users/you/.boot/cache/tmp/Users/you/fun/cprop/cp8/mm3x96"
"/Users/you/.boot/cache/tmp/Users/you/fun/cprop/cp8/-rcsl8f"}
every time you run a task, you start from a clean set of artifacts
Boot tasks are functions. boot.build
is Clojure code.
Customizing / fixing / understanding a task is no different than doing it for any other Clojure library.
There is no mental gap of decyphering someone's DSL.