Boot는 Leiningen을 대체할 수 있는 build tool이다.

Leiningen에서는 원하는 작업들을 project.clj 파일의 defproject '매크로' 안에 기술한다. 따라서 이 매크로가 정해준 방식 이외의 방법으로 원하는 작업을 수행하기 힘들다는 단점이 있다. 하지만 미리 정해진 방식이 있으므로, 개발자가 일일이 설정해 줄 일이 상대적으로 적다는 장점도 있다.

반면에, Boot는 원하는 작업들을 build.boot 파일 안에서 '함수’로 정의해 수행할 수 있다. 따라서 자신이 원하는 작업들을 함수형 방식으로 조합해서 사용하는 것이 가능하다. 따라서 개발자가 세세하게 설정해 줄 수 있는 장점도 있는 반면에, 그에 따라 제대로 사용하기 위해서는 개발자가 알아야 할 것도 많다는 단점도 있다. 하지만 Boot가 제공하는 더 많은 유연함과 확장성으로 인해, 현재는 Boot 프로젝트가 Leiningen에 비해 늦게 시작했지만, 시간이 지날수록 Leiningen 사용자가 Boot로 많이 넘어 올 것으로 예상된다.

1. Boot의 특징

예를 들어, Leiningen에서는 다음의 두 작업을 실행하면, JVM을 두 번 호출하게 된다.

$ lein clean
$ lein compile

반면에, Boot에서는 여러 개의 작업을 수행하더라도 한 개의 JVM에서 실행되므로, 여러 작업을 연이어 수행할 필요가 있을 때 JVM 로딩 시간으로 인한 지연을 피할 수 있다.

그리고 Boot에서는 fileset이라는 개념을 새로 도입했다. 이것은 한 개의 작업을 실행한 결과로 나오는 출력 파일들에 immutable이라는 개념을 적용한 것이다.

입력값      --> 함수       --> 출력값
입력 파일들 --> 작업(task) --> 출력 파일들

우리가 잘 알고 있는 클로저 함수들은 immutable 자료형을 다룬다. 마찬가지로 Boot는 한 개의 작업을 수행할 때, 입력 파일들에 대해 어떤 작업을 수행한 후, 그 결과를 그 입력 파일들에 다시 덮어쓰지 않는다. 그 대신에, 각 버전의 출력 파일들(fileset)을 임시 디렉토리(~/.boot/tmp) 아래에 보관해 둔다. 이러한 과정이 여러 개의 작업들을 대상으로 반복적으로 수행된다. 그리고 그 입력 파일들에 작업을 수행한 최종 결과 파일들을 target 디렉토리(디폴트는 target)에 복사해 놓는다.

2. Boot의 설치

먼저 다음의 과정을 따라 Boot를 설치해 보자. (설치 시점: 2016-01-15)

Windows
  • wget 설치는 이곳을 참조한다.

c:\> wget https://github.com/boot-clj/boot-bin/releases/download/2.5.2/boot.exe
C:\> move boot.exe %SystemRoot%
Unix, Linux, OSX
$ wget https://github.com/boot-clj/boot-bin/releases/download/2.5.2/boot.sh
$ mv boot.sh boot
$ chmod a+x boot
$ sudo mv boot /usr/local/bin
OSX with Homebrew
$ brew install boot-clj

위와 같이 설치한 후, 다음의 명령을 실행해 다음과 같은 화면이 나오면 설치에 성공한 것이다. 처음 실행하는 경우, boot 실행에 필요한 여러 라이브러리들을 함께 다운로드하므로 시간이 좀 걸린다.

$ boot
Downloading https://github.com/boot-clj/boot/releases/download/2.5.2/boot.jar...done.
Running for the first time: updating to latest version.
Retrieving boot-2.5.2.jar from https://clojars.org/repo/
Retrieving clojure-1.7.0.jar from https://repo1.maven.org/maven2/
#https://github.com/boot-clj/boot
#Thu Dec 24 12:32:40 KST 2015
BOOT_CLOJURE_NAME=org.clojure/clojure
BOOT_VERSION=2.5.2
BOOT_CLOJURE_VERSION=1.7.0

$ boot -h
Downloading https://github.com/boot-clj/boot/releases/download/2.5.2/boot.jar...done.
Retrieving dynapath-0.2.3.jar from https://clojars.org/repo/
Retrieving pod-2.5.2.jar from https://clojars.org/repo/
Retrieving shimdandy-impl-1.2.0.jar from https://repo1.maven.org/maven2/
Retrieving core-2.5.2.jar from https://clojars.org/repo/
Retrieving worker-2.5.2.jar from https://clojars.org/repo/
Retrieving aether-2.5.2.jar from https://clojars.org/repo/
......

Usage:   boot OPTS <task> TASK_OPTS <task> TASK_OPTS ...

OPTS:    -a --asset-paths PATH       Add PATH to set of asset directories.
         -b --boot-script            Print generated boot script for debugging.
         -B --no-boot-script         Ignore boot script in current directory.
         -C --no-colors              Remove ANSI escape codes from printed output.
         -d --dependencies SYM:VER   Add dependency to project (eg. -d foo/bar:1.2.3).
         -e --set-env KEY=VAL        Add KEY => VAL to project env map.
         -h --help                   Print basic usage and help info.
         -P --no-profile             Skip loading of profile.boot script.
         -r --resource-paths PATH    Add PATH to set of resource directories.
         -q --quiet                  Suppress output from boot itself.
         -s --source-paths PATH      Add PATH to set of source directories.
         -t --target-path PATH       Set the target directory to PATH.
         -T --no-target              Don't automatically write files to the target directory.
         -u --update                 Update boot to latest release version.
         -v --verbose                More error info (-vv more verbose, etc.)
         -V --version                Print boot version info.

Tasks:   add-repo                    Add all files in project git repo to fileset.
         aot                         Perform AOT compilation of Clojure namespaces.
         checkout                    Checkout dependencies task.
         help                        Print usage info and list available tasks.
         install                     Install project jar to local Maven repository.
         jar                         Build a jar file for the project.
         javac                       Compile java sources.
         pom                         Create project pom.xml file.
         push                        Deploy jar file to a Maven repository.
         repl                        Start a REPL session for the current project.
         show                        Print project/build info (e.g. dependency graph, etc).
         sift                        Transform the fileset, matching paths against regexes.
         speak                       Audible notifications during build.
         target                      Writes output files to the given directory on the filesystem.
         uber                        Add jar entries from dependencies to fileset.
         wait                        Wait before calling the next handler.
         war                         Create war file for web deployment.
         watch                       Call the next handler when source files change.
         web                         Create project web.xml file.
         zip                         Build a zip file for the project.

Env:     BOOT_AS_ROOT                Set to 'yes' to allow boot to run as root.
         BOOT_CLOJURE_VERSION        The version of Clojure boot will provide (1.7.0).
         BOOT_CLOJURE_NAME           The artifact name of Clojure boot will provide (org.clojure/clojure).
         BOOT_COLOR                  Set to 'no' to turn colorized output off.
         BOOT_EMIT_TARGET            Set to 'no' to disable automatic writing to target directory.
         BOOT_FILE                   Build script name (build.boot).
         BOOT_GPG_COMMAND            System gpg command (gpg).
         BOOT_HOME                   Directory where boot stores global state (~/.boot).
         BOOT_JAVA_COMMAND           Specify the Java executable (java).
         BOOT_JVM_OPTIONS            Specify JVM options (Unix/Linux/OSX only).
         BOOT_LOCAL_REPO             The local Maven repo path (~/.m2/repository).
         BOOT_VERSION                Specify the version of boot core to use.
         BOOT_WARN_DEPRECATED        Set to 'no' to suppress deprecation warnings.

Files:   ./boot.properties           Specify boot options for this project.
         BOOT_HOME/boot.properties   Specify global boot options.
         BOOT_HOME/profile.boot      A script to run before running the build script.

Do `boot <task> -h` to see usage info and TASK_OPTS for <task>.

3. Boot update하기

boot -u 명령을 실행하면 최신 버전의 boot로 update할 수 있다. 아래의 예에서는 이미 최신버전이어서 update가 이루어지지 않고 있댜.

$ boot -u
#http://boot-clj.com
#Thu Dec 24 12:48:41 KST 2015
BOOT_CLOJURE_NAME=org.clojure/clojure
BOOT_CLOJURE_VERSION=1.7.0
BOOT_VERSION=2.5.2

4. build.properties 파일 갱신하기

$ boot -V > build.properties

5. task 설명 보기

해당 작업에 대한 옵션이나 설명을 보려면 boot <task-name> -h를 실행한다.

$ boot watch -h
Call the next handler when source files change.

Debouncing time is 10ms by default.

Options:
  -h, --help     Print this help info.
  -q, --quiet    Suppress all output from running jobs.
  -v, --verbose  Print which files have changed.
  -M, --manual   Use a manual trigger instead of a file watcher.

6. REPL 실행하기

$ boot repl
nREPL server started on port 46131 on host 127.0.0.1 - nrepl://127.0.0.1:46131
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.7.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_66-b17
        Exit: Control+D or (exit) or (quit)
    Commands: (user/help)
        Docs: (doc function-name-here)
              (find-doc "part-of-name-here")
Find by Name: (find-name "part-of-name-here")
      Source: (source function-name-here)
     Javadoc: (javadoc java-object-or-class-here)
    Examples from clojuredocs.org: [clojuredocs or cdoc]
              (user/clojuredocs name-here)
              (user/clojuredocs "ns-here" "name-here")
boot.user=> (+ 1 2)
3
boot.user=> (exit)
Bye for now!

$

7. boot show

7.1. -c, --classpath

현재 프로젝트의 classpath를 보여 준다. 다음의 출력 결과는 일부는 생략하고, 보기 좋게 정리한 것이다.

$ boot show -c
/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:
/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:
/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:
/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:
......

7.2. -e, --env

현재 프로젝트의 env 정보를 출력해 준다.

$ boot show -e
{:watcher-debounce 10,
 :dependencies
 [[org.clojure/clojure "1.7.0"]
  [org.clojure/clojurescript "1.7.228"]
  [cljsjs/d3 "3.5.7-1"]
  [reagent "0.5.1"]
  [domina "1.0.3"]
  [datascript "0.13.3"]
  [posh "0.3.3.1"]
  [philoskim/debux "0.1.0"]
  [adzerk/boot-cljs "1.7.170-3" :scope "test"]
  [pandeiro/boot-http "0.7.0" :scope "test"]
  [adzerk/boot-reload "0.4.2" :scope "test"]
  [adzerk/boot-cljs-repl "0.3.0" :scope "test"]
  [com.cemerick/piggieback "0.2.1" :scope "test"]
  [weasel "0.7.0" :scope "test"]
  [org.clojure/tools.nrepl "0.2.12" :scope "test"]
  [adzerk/boot-test "1.0.7" :scope "test"]
  [crisptrutski/boot-cljs-test "0.2.1" :scope "test"]],
 :directories
 #{"/home/philos/.boot/cache/tmp/home/philos/work/chart/emh/f0sqpx"
   "/home/philos/.boot/cache/tmp/home/philos/work/chart/emh/-rcsl8f"
   "/home/philos/.boot/cache/tmp/home/philos/work/chart/emh/-grrwi1"
   "/home/philos/.boot/cache/tmp/home/philos/work/chart/emh/62mkia"},
 :source-paths #{"src/cljs"},
 :resource-paths #{"html"},
 :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_JVM_OPTIONS" "-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xverify:none", "BOOT_FILE" "build.boot", "BOOT_CLOJURE_VERSION" "1.7.0", "BOOT_VERSION" "2.5.5"}}

7.3. -u, --updates

build.boot 파일의 set-env! 함수의 :dependencies에 지정한 라이브러리들보다 더 최신 버전이 있으면 일단 ~/.m2/repository 디렉토리에 내려 받아 놓은 후, 최신 버전의 목록을 보여 준다. 이를 확인한 후에, 위의 :dependencies의 해당 라이브러리 버전을 수작업으로 직접 바꾸어 주어야 한다.

$ boot show -u
Retrieving clojure-1.8.0-RC5.jar from https://repo1.maven.org/maven2/
Retrieving clojure-1.4.0.jar from https://clojars.org/repo/
Retrieving datascript-0.14.0.jar from https://clojars.org/repo/
Retrieving boot-cljs-1.7.228-1.jar from https://clojars.org/repo/
Retrieving boot-reload-0.4.4.jar from https://clojars.org/repo/
[org.clojure/clojure "1.8.0-RC5"]
[reagent "0.6.0-alpha"]
[datascript "0.14.0"]
[adzerk/boot-cljs "1.7.228-1" :scope "test"]
[adzerk/boot-reload "0.4.4" :scope "test"]
[adzerk/boot-test "1.1.0" :scope "test"]

7.4. -p, --pedantic

각 라이브러리가 의존하고 있는 라이브러리의 버전이 상이할 때, 각 라이브러리들이 어느 버전의 라이브러리들에 의존하고 있는지를 일목요연하게 보여준다. 아래의 출력 결과에서 일부 내용은 생략했다.

$ boot show -p
[✔] org.clojure/clojure
    ✘ 1.7.0-RC1
      weasel
    ✔ 1.7.0
      posh
      reagent
      org.clojure/clojure
      org.clojure/clojurescript
      philoskim/debux
    ✘ 1.6.0
      com.cemerick/piggieback
    ✘ 1.4.0
      domina
    ✘ 1.2.0
      org.clojure/tools.nrepl
[✔] org.clojure/clojurescript
    ✔ 1.7.228
      org.clojure/clojurescript
    ✘ 1.7.170
      posh
    ✘ 1.7.48
      reagent
    ✘ 0.0-3308
      weasel
      philoskim/debux
    ✘ 0.0-3165
      com.cemerick/piggieback
......

8. boot watch

watch는 source-paths에 지정된 파일들이 변경되는 것을 감시한다.