-
- Notifications
You must be signed in to change notification settings - Fork 178
Boot for Leiningen Users
All of the different abstractions used to control how Leiningen works (plugins, profiles, middleware, injections, etc.) can be implemented as boot tasks. This document shows some of the common patterns Leiningen users will be accustomed to and their boot equivalents.
The equivalent of Leiningen profiles and middleware in boot are tasks that modify the environment and return clojure.core/identity, the no-op task middleware.
For example, a task to add the test directory to the :source-paths when running tests, similar to the test profile in Leiningen:
(deftask testing "Profile setup for running tests." [] (set-env! :source-paths #(conj % "test")) identity)Profiles are "activated" simply by adding them to the pipeline, for example:
$ boot testing run-tests This is equivalent to Lein's
$ lein with-profile testing run-tests In lein in order to pass a profile specific environment variable, it can be specified as a -D param in :jvm-opts:
:profiles {:dev {:jvm-opts ["-Dparam=value"]}}While boot has several ways to set JVM Options, if all that is needed is -D, it can be easily done with a good old System/setProperty, since boot build configuration is just Clojure:
(deftask dev [] (System/setProperty "param" "value") ;; ...)It does not have to be a deftask, it can just be a defn that will be available in REPL. Or, if needed, (System/setProperty "param" "value") can be set globally (i.e. not task specific) per build.boot.
In boot dependencies are added to the class path via the set-env! or merge-env! functions in the boot.core namespace. These functions are normally called in the build.boot script.
There is no task specifically for fetching dependencies. Running boot with no options will accomplish this as the build.boot file is evaluated. If you want to have a deps task that will fetch dependencies you can define one:
(deftask deps [])The repl task is different from most other tasks because the nREPL server can't be run in a pod (because you want to have a REPL in the project context, not a fresh pod context). So the repl task calls set-env! itself to add its nREPL dependencies at runtime when it is actually used, and the project has no nREPL dependencies when the repl task isn't used.
This means that the method described above will not fetch dependencies needed to start the REPL. This could be an issue when building Docker images, for example. The solution is to do something like this:
boot repl -s or
(deftask deps [] (repl :server true))In boot the show task can print the dependency graph:
boot show -d You can also see dependency conflicts:
boot show -p The equivalent of lein pprint in boot is:
boot show -e The most trivial equivalent of lein run is a very simple task.
(require 'my.namespace) (deftask run [] (with-pass-thru _ (my.namespace/-main)))Obviously, you're not limited here to calling the -main function, and you can pick an arbitrary namespace. If you want to pass arguments to your main function, boot actually provides a nice benefit over lein by providing a very nice DSL for declaring task options. Thus you can now move your argument parsing code out of your library code and into the build.boot file. Of course that doesn't help you if you want to distribute a standalone jar that takes command-line arguments.
At first glance, it seems like lein trampoline and boot's pods mechanism serve a similar purpose. But really they serve very distinct purposes. lein trampoline is an optimization lein uses to cache your project's classpath so that the lein program can do less work before starting the project JVM.
Boot's pods mechanism on the other hand are intended to provide the classpath isolation that lein achieves by launching the project code in a separate JVM from the one lein itself is running in.
The difference is that boot pods are first-class and in-process. Since they're in the same process, and because they're first class you can have anonymous pods, you can put a pod in a future, etc. You can make functions that take pods as arguments and functions that return pods.
boot itself is a small java program distributed as an uberjar:
boot.sh. This uberjar can be executed directly because we exploit a nice property of jar files - you can put random garbage at the front of a jar file and the jvm will still load it without problems. So we inject a shebang and a call to java -jar there.
The java program
boot.shprovides a single class - boot.App. boot.App has static methods that provide amain()entry point for the jar, and methods to create new pods. Boot runs all clojure code in pods so your build.boot script is actually running in a pod but your pod can access boot.App and use it to create new pods that it can evaluate expressions in. All pods communicate with each other via boot.App.
AAAA: So far I've ported `lein uberjar` to a boot.build file. But I still don't know how to port `lein run` and `lein run -m my.non-main.namespace` and `lein speclj`. That's what's keeping me from switching from Leiningen to Boot. AAAA: I understand that `lein spec` needs a third party plugin to be created. AAAA: But it would be nice if the Boot website or FAQ or wiki had some instructions on porting `lein uberjar`, `lein run`, and `lein run -m some.custom.namespace` to Boot from Leiningen. ... AAAA: As it is, these are blockers so that I can't switch to Boot yet, even though I see that it would be more helpful for me than Leiningen. ... CCCC: AAAA: does 'boot uber jar' produce an uberjar for you similar to lein uberjar? AAAA: CCCC: I've gotten the `lein uberjar` part figured out (the exact details vary depending on project requirements); it's the rest I still don't have yet CCCC: i haven't used lein in a while, can you describe what lein run is supposed to do? BBBB: AAAA: boot does not have any dependencies of its own BBBB: AAAA: re: your question about boot's deps vs your project deps BBBB: AAAA: that's the point of having pods, it serves a similar purpose to lein's second jvm BBBB: but more granular and first-class AAAA: BBBB: At the very least it depends on clojure.core BBBB: AAAA: not really, it doesn't bring in a clojure dependency in your project AAAA: BBBB: but only if you're using Pods, right? otherwise it still does, right? BBBB: clojure.core is *provided* at runtime by boot, but that doesn't have any restriction on your project BBBB: AAAA: just do `boot pom -p foo -v 1.0` and inspect the pom.xml it generates BBBB: you'll see no dependencies at all BBBB: so if your project needs a clojure dependency you need to add it BBBB: AAAA: your question about uberjar and lein run is here: https://github.com/adzerk-oss/boot-uberjar-example BBBB: it addresses both of them BBBB: boot has an "uber" task that can be composed with any packaging task, like jar, war, zip, whatever BBBB: we've decoupled those BBBB: and lein run is just making your own task that runs something BBBB: AAAA: for more info about what gets packaged in your uberjar i recommend looking at `boot uber -h` AAAA: Great thanks. BBBB: AAAA: it would be very awesome if you would update the wiki with any information you think is helpful BBBB: AAAA: https://github.com/boot-clj/boot/wiki/Boot-for-Leiningen-Users BBBB: also, re: trampoline vs. pods BBBB: the lein "trampoline" is not the same thing as pods at all noprompt joined the chat room. BBBB: trampoline is an optimization lein uses to cache classpath stuff BBBB: running multiple jvms BBBB: pods are first-class and in-process BBBB: boot itself is a small java program BBBB: distributed as an uberjar (boot.sh) BBBB: this uberjar can be executed directly because we exploit a nice property of jar files---you can put random garbage at the front of a jar file and the jvm will still load it without problems BBBB: so we inject a shebang and a call to java -jar there BBBB: boot.sh, the java program, provides a single class, boot.App BBBB: boot.App has static methods that provide a main() entry point for the jar, and methods to create new pods BBBB: boot runs all clojure code in pods BBBB: so your build.boot script is actually running in a po BBBB: pod BBBB: but your pod can access boot.App and use it to create new pods that it can evaluate expressions in BBBB: all pods communicate with each other via boot.App BBBB: this is very different from lein trampoline BBBB: since they're in the same process, and becuase they're first class you can have anonymous pods, you can put a pod in a future, etc BBBB: you can make functions that take pods as arguments and functions that return pods You can find other developers and users in the #hoplon channel on freenode IRC or the boot slack channel. If you have questions or need help, please visit the Discourse site.

- Environments
- Boot environment
- Java environment
- Tasks
- Built-ins
- Third-party
- Tasks Options
- Filesets
- Target Directory
- Pods
- Boot Exceptions
- Configuring Boot
- Updating Boot
- Setting Clojure version
- JVM Options
- S3 Repositories
- Scripts
- Task Writer's Guide
- Require inside Tasks
- Boot for Leiningen Users
- Boot in Leiningen Projects
- Repl reloading
- Repository Credentials and Deploying
- Snippets
- Troubleshooting
- FAQ
- API docs
- Core
- Pod
- Util