- Notifications
You must be signed in to change notification settings - Fork179
boot-clj/boot
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
change log |installation |getting started |documentation |API docs
Boot is a Clojure build framework and ad-hoc Clojure script evaluator. Bootprovides a runtime environment that includes all of the tools needed to buildClojure projects from scripts written in Clojure that run in the context ofthe project.
If you have questions or need help, pleasevisit the Discourse site.You can find other developers and users inthe
#boot
channel on Clojurians Slack.
Build processes for applications always end up being complex things. Asimple web application, for instance, may require manyintegrations–asset pipelines, deployment to different environments,the compilation of multiple artifacts with different compilers,packaging, etc.
The more complex the build process becomes, the more flexible the build toolneeds to be. Static build specifications become less and less useful as theproject moves toward completion. Being Lispers we know what to do: Lambda isthe ultimate declarative.
Instead of building the project based on a global configuration map, bootprovides a runtime environment in which a build script written in Clojurecan be evaluated. It is this script—a Turing-complete buildspecification—which builds the project.
- Write executable, self-contained scripts in Clojure and run them with orwithout a project context.
- Dynamically add dependencies from Maven repositories to the running script'sclass path.
- Managed filesystem tree provides a scoped, immutable, append-only interface.
- Fine-grained control of classloader isolation–run code in separate Clojureruntimes.
- Tasks are functions that return middleware which compose to form buildpipelines.
- Tasks are not coupled via hardcoded file paths or magical keys in a globalconfiguration map.
- Create new, ad-hoc tasks easily in the project, in the build script, or inthe REPL.
- Compose build pipelines in the project, in the build script, in the REPL, oron the command line.
- Artifacts can never be stale–there is no need for a
clean
task.
Binaries in executable format are available. Follow the instructions for youroperating system (note: boot requires the Java Development Kit (JDK) version1.8 or greater).
Package managers:
- Homebrew —
brew install boot-clj
- nix —
nix-env -i boot
- aur —
yaourt --noconfirm -Syy boot
- docker — Use
clojure
image withboot
tag.- CircleCI also maintains image withadditional tooling:
circleci/clojure
- CircleCI also maintains image withadditional tooling:
Otherwise:
- Downloadboot.sh and save as
boot
- Make it executable.
- Move it to somewhere in your
$PATH
.
Here is a one-liner to do the above:
$ sudo bash -c"cd /usr/local/bin && curl -fsSLo boot https://github.com/boot-clj/boot-bin/releases/download/latest/boot.sh && chmod 755 boot"
Package managers:
- Chocolatey —
choco install boot-clj
- Scoop —
scoop bucket add extras && scoop install boot-clj
Otherwise, downloadboot.exe, then:
:: Using %SystemRoot% here, but can be any folder on user's %PATH%C:\>move boot.exe%SystemRoot%
Note: Windows 10 is fully supported. For other versions please seethese outstanding issues for specific limitations.
The boot.sh/boot.exe wrapper is a very thin shim used to load "the real Boot"from Maven. With the wrapper installed you can update Boot's JAR files andkeep up to date with the following command:
boot -u
The boot.sh/boot.exe wrapper itself changes (and thus requires updating) muchless frequently, and will remain compatible with future versions of the JARfiles.
TheModern CLJS tutorials are anexcellent introduction to Boot and ClojureScript. Pretty much everything youneed to know about Boot to get a project off the ground is covered there.Check it out!
Once boot is installed (seeInstall above) do this in a terminal:
boot -h
You should see the boot manual page printed to the terminal. This informationincludes command line options recognized by boot, a list of available tasks,and other information about relevant configuration files and environmentvariables.
You can also get help for a specific task, for example therepl
task:
boot repl -h
You should see usage info and command line options for the specified task.
You can also get help in the REPL. First start a REPL session:
boot repl
Then, to get help for therepl
task, do:
boot.user=> (doc repl)
The output will be slightly different from the command line help info. We'll seewhy this is so a little later.
Let's build a simple project to get our feet wet. We'll create a new directory,saymy-project
, and a source directory in there namedsrc
with a sourcefile,hello.txt
:
mkdir -p my-project/srccd my-projectecho "hi there" > src/hello.txt
The directory should now have the following structure:
my-project└── src └── hello.txt
Suppose we want to build a jar file now, and install it to our local Mavenrepository. We'll use thepom
,jar
, andinstall
tasks to accomplish thisfrom the command line:
# The -- args below are optional. We use them here to visually separate the tasks.boot -r src -d me.raynes/conch:0.8.0 -- pom -p my-project -v 0.1.0 -- jar -M Foo=bar -- install
What we did here was we built a pipeline on the command line and ran it tobuild our project.
- We specified the resource directory (files that will end up in the jar) via boot's
-r
option. - We added the
conch
dependency via boot's-d
option.
This sets up the build environment. Then we constructed a pipeline of tasks:
- The
pom
task with options to set the project ID and version,(by default only compiled artifacts end up in the fileset), - The
jar
task with options to add aFoo
key to the jar,manifest with valuebar
, - And finally the
install
task with no options.
Boot composes the pipeline and runs it, building your project. Your localMaven repository will now containmy-project-0.1.0.jar
.
Anything done on the command line can be done in the REPL or in a build script.Fire up a REPL in the project directory:
boot repl
The default namespace isboot.user
, which is the namespace given to the buildscript. Building the project in the REPL is almost identical to what we did onthe command line.
First we'll set some global boot options–we'll set the source directory and addtheconch
dependency to the build environment:
boot.user=> (set-env! #_=>:resource-paths #{"src"} #_=>:dependencies '[[me.raynes/conch"0.8.0"]])
This was specified on the command line as the-r
or--resource-paths
and-d
or--dependencies
arguments to boot itself. These translate to calls toset-env!
in the REPL or in a script. Note that the keyword always corresponds to the longoption from the command line.
Now that boot environment is set up we can build the project:
boot.user=> (boot (pom:project 'my-project:version"0.1.0") #_=> (jar:manifest {"Foo""bar"}) #_=> (install))
Again, note that the keyword arguments correspond to long options from thecommand line.
It gets tedious to specify all of those options on the command line or in theREPL every time you build your project. Boot provides facilities for settingtask options globally, with the ability to override them by providing optionson the command line or in the REPL later.
Thetask-options!
macro does this. Continuing in the REPL:
boot.user=> (task-options! #_=> pom {:project 'my-project #_=>:version"0.1.0"} #_=> jar {:manifest {"Foo""bar"}})
Now we can build the project without specifying these options, because thetask functions have been replaced with curried versions of themselves:
boot.user=> (boot (pom) (jar) (install))
Individual options can still be set by providing arguments to the tasks suchthat they override those set withtask-options!
. Let's build our project witha different version number, for example:
boot.user=> (boot (pom:version"0.1.1") (jar) (install))
Pretty simple, right? This way of setting options requires no participation bythe tasks themselves. There is no global configuration map or anything likethat. It works because tasks accept onlykeyword arguments, so partialapplication is idempotent and last setting wins.
More sophisticated builds will require one, but even a build as simple as thisone can be made a little simpler by creating a build script containing theoptions for the tasks you're using.
Create a file namedbuild.boot
in the project directory with the followingcontents:
(set-env!:resource-paths #{"src"}:dependencies '[[me.raynes/conch"0.8.0"]])(task-options! pom {:project 'my-project:version"0.1.0"} jar {:manifest {"Foo""bar"}})
Now we can build the project without specifying the options for each task onthe command line–we only need to specify the tasks to create the pipeline.
boot pom jar install
And we can override these options on the command line as we did in the REPL:
boot -- pom -v 0.1.1 -- jar -- install
Notice how we did not need a(boot ...)
expression in thebuild.boot
script.Boot constructs that at runtime from the command line arguments.
You can start a REPL in the context of the boot script (compiled as theboot.user
namespace), and build interactively too:
boot.user=> (boot (pom) (jar) (install))
When boot is run from the command line it actually generates aboot
expressionaccording to the command line options provided.
Custom tasks can be defined in the project or inbuild.boot
. This is generallyhow boot is expected to be used, in fact. Boot ships with a selection of smalltasks that can be composed uniformly, and the user assembles them into somethingthat makes sense for the specific project.
As an example let's make a task that performs the last example above, and nameitbuild
. We'll modifybuild.boot
such that it contains the following:
(set-env!:resource-paths #{"src"}:dependencies '[[me.raynes/conch"0.8.0"]])(task-options! pom {:project 'my-project:version"0.1.0"} jar {:manifest {"Foo""bar"}})(deftaskbuild"Build my project." [] (comp (pom) (jar) (install)))
NOTE: When using comp, all arguments must be functions - nil is not supported. In this example we call each task middleware which returns the task function, these functions are composed into a new build task.
Now we should be able to see thebuild
task listed among the available tasksin the output ofboot -h
, and we can run the task from the command line as wewould run any other task:
boot build
Tasks are functions that return pipelines. Pipelines compose functionally toproduce new pipelines. If you've usedtransducers orring middlewarethis pattern should be familiar. Thepom
andinstall
functions we used inthe definition ofbuild
are, in fact, the same functions that were calledwhen we used them on the command line before. Boot's command line parsingimplicitly composes them; in our task we compose them using Clojure'scomp
function.
Now let's define a task in a namespace in our project and use it from thecommand line.
Create the namespace with the task:
(nsdemo.boot-build (:require [boot.core:as core] [boot.task.built-in:as task]))(core/deftaskbuild"Build my project." [] (comp (task/pom) (task/jar) (task/install)))
and write it tosrc/demo/boot_build.clj
in your project.
Modify thebuild.boot
file to incorporate this new task by removing thedefinition forbuild
. The newbuild.boot
file will look like this:
(set-env!:resource-paths #{"src"}:dependencies '[[me.raynes/conch"0.8.0"]])(task-options! pom {:project 'my-project:version"0.1.0"} jar {:manifest {"Foo""bar"}})(require '[demo.boot-build:refer:all])
You can now use thebuild
task defined in the project namespace from thecommand line, as before:
boot build
...
To build boot from source you will need:
You may increment Boot's version by editingversion.properties
:
# <version> is the version of your buildversion=<version>
Then, in a terminal in the project directory do:
make depsmake install
- Jars for all of the boot components will be built and installed in yourlocal Maven repository.
- The app uberjar will be built and copied to
bin/boot.jar
. - The app uberjar will be copied to
$HOME/.boot/cache/bin/<version>/boot.jar
.
Make your build the default by editing your$HOME/.boot/boot.properties
file:
# <version> is the version of your buildBOOT_VERSION=<version>
For guidelines for contributing, seeCONTRIBUTING.md.
Code from other projects was incorporated into boot wherever necessary toeliminate external dependencies of boot itself. This ensures that the projectclasspath remains pristine and free from potential dependency conflicts. We'vepulled in code from the following projects (thanks, guys!)
- technomancy/leiningen
- cemerick/pomegranate
- Raynes/conch
- tebeka/clj-digest
- cldwalker/table
- clojure/tools.cli
- bbloom/backtick
- AvisoNovate/pretty
- google/hesokuri
- barbarysoftware/watchservice
The boot source is also annotated to provide attribution wherever possible.Look for the:boot/from
key in metadata attached to vars or namespaces.
This project exists thanks to all the people who contribute. [Contribute].
Thank you to all our backers! 🙏 [Become a backer]
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]
Copyright © 2013-2018 Alan Dipert and Micha Niskin
Distributed under the Eclipse Public License, the same as Clojure.
About
Build tooling for Clojure.