Giter Club home page Giter Club logo

boot's Introduction

Boot Logo

Boot

Build Status Stories in Ready Backers on Open Collective Sponsors on Open Collective

change log | installation | getting started | documentation | API docs

Boot is a Clojure build framework and ad-hoc Clojure script evaluator. Boot provides a runtime environment that includes all of the tools needed to build Clojure projects from scripts written in Clojure that run in the context of the project.

If you have questions or need help, please visit the Discourse site. You can find other developers and users in the #boot channel on Clojurians Slack.

Another Build Tool?

Build processes for applications always end up being complex things. A simple web application, for instance, may require many integrations–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 tool needs to be. Static build specifications become less and less useful as the project moves toward completion. Being Lispers we know what to do: Lambda is the ultimate declarative.

Instead of building the project based on a global configuration map, boot provides a runtime environment in which a build script written in Clojure can be evaluated. It is this script—a Turing-complete build specification—which builds the project.

Features

  • Write executable, self-contained scripts in Clojure and run them with or without a project context.
  • Dynamically add dependencies from Maven repositories to the running script's class path.
  • Managed filesystem tree provides a scoped, immutable, append-only interface.
  • Fine-grained control of classloader isolation–run code in separate Clojure runtimes.
  • Tasks are functions that return middleware which compose to form build pipelines.
  • Tasks are not coupled via hardcoded file paths or magical keys in a global configuration map.
  • Create new, ad-hoc tasks easily in the project, in the build script, or in the REPL.
  • Compose build pipelines in the project, in the build script, in the REPL, or on the command line.
  • Artifacts can never be stale–there is no need for a clean task.

Install

Binaries in executable format are available. Follow the instructions for your operating system (note: boot requires the Java Development Kit (JDK) version 1.8 or greater).

Unix, Linux, OSX

Package managers:

Otherwise:

  • Download boot.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"

Windows

Package managers:

  • Chocolateychoco install boot-clj
  • Scoopscoop bucket add extras && scoop install boot-clj

Otherwise, download boot.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 see these outstanding issues for specific limitations.

Update

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 and keep up to date with the following command:

boot -u

The boot.sh/boot.exe wrapper itself changes (and thus requires updating) much less frequently, and will remain compatible with future versions of the JAR files.

Getting Started

The Modern CLJS tutorials are an excellent introduction to Boot and ClojureScript. Pretty much everything you need to know about Boot to get a project off the ground is covered there. Check it out!

Once boot is installed (see Install above) do this in a terminal:

boot -h

You should see the boot manual page printed to the terminal. This information includes command line options recognized by boot, a list of available tasks, and other information about relevant configuration files and environment variables.

You can also get help for a specific task, for example the repl task:

boot repl -h

You should see usage info and command line options for the specified task.

Task Help in the REPL

You can also get help in the REPL. First start a REPL session:

boot repl

Then, to get help for the repl task, do:

boot.user=> (doc repl)

The output will be slightly different from the command line help info. We'll see why this is so a little later.

Build From the Command Line

Let's build a simple project to get our feet wet. We'll create a new directory, say my-project, and a source directory in there named src with a source file, hello.txt:

mkdir -p my-project/src
cd my-project
echo "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 Maven repository. We'll use the pom, jar, and install tasks to accomplish this from 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 to build 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 a Foo key to the jar, manifest with value bar,
  • And finally the install task with no options.

Boot composes the pipeline and runs it, building your project. Your local Maven repository will now contain my-project-0.1.0.jar.

Build From the REPL

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 is boot.user, which is the namespace given to the build script. Building the project in the REPL is almost identical to what we did on the command line.

First we'll set some global boot options–we'll set the source directory and add the conch 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 to set-env! in the REPL or in a script. Note that the keyword always corresponds to the long option 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 the command line.

Configure Task Options

It gets tedious to specify all of those options on the command line or in the REPL every time you build your project. Boot provides facilities for setting task options globally, with the ability to override them by providing options on the command line or in the REPL later.

The task-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 the task 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 such that they override those set with task-options!. Let's build our project with a 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 by the tasks themselves. There is no global configuration map or anything like that. It works because tasks accept only keyword arguments, so partial application is idempotent and last setting wins.

Write a Build Script

More sophisticated builds will require one, but even a build as simple as this one can be made a little simpler by creating a build script containing the options for the tasks you're using.

Create a file named build.boot in the project directory with the following contents:

(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 on the 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 the build.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 the boot.user namespace), and build interactively too:

boot.user=> (boot (pom) (jar) (install))

When boot is run from the command line it actually generates a boot expression according to the command line options provided.

Define a Task

Custom tasks can be defined in the project or in build.boot. This is generally how boot is expected to be used, in fact. Boot ships with a selection of small tasks that can be composed uniformly, and the user assembles them into something that makes sense for the specific project.

As an example let's make a task that performs the last example above, and name it build. We'll modify build.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"}})

(deftask build
  "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 the build task listed among the available tasks in the output of boot -h, and we can run the task from the command line as we would run any other task:

boot build

Tasks are functions that return pipelines. Pipelines compose functionally to produce new pipelines. If you've used transducers or ring middleware this pattern should be familiar. The pom and install functions we used in the definition of build are, in fact, the same functions that were called when we used them on the command line before. Boot's command line parsing implicitly composes them; in our task we compose them using Clojure's comp function.

Define Tasks In Project

Now let's define a task in a namespace in our project and use it from the command line.

Create the namespace with the task:

(ns demo.boot-build
  (:require [boot.core :as core]
            [boot.task.built-in :as task]))

(core/deftask build
  "Build my project."
  []
  (comp (task/pom) (task/jar) (task/install)))

and write it to src/demo/boot_build.clj in your project.

Modify the build.boot file to incorporate this new task by removing the definition for build. The new build.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 the build task defined in the project namespace from the command line, as before:

boot build

...

Hacking Boot

To build boot from source you will need:

  • JDK 1.8
  • GNU make
  • maven 3
  • bash shell, wget
  • boot.sh (Unix) or boot.exe (Windows)

You may increment Boot's version by editing version.properties:

# <version> is the version of your build
version=<version>

Then, in a terminal in the project directory do:

make deps
make install
  • Jars for all of the boot components will be built and installed in your local 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 build
BOOT_VERSION=<version>

For guidelines for contributing, see CONTRIBUTING.md.

Attribution

Code from other projects was incorporated into boot wherever necessary to eliminate external dependencies of boot itself. This ensures that the project classpath remains pristine and free from potential dependency conflicts. We've pulled in code from the following projects (thanks, guys!)

The boot source is also annotated to provide attribution wherever possible. Look for the :boot/from key in metadata attached to vars or namespaces.

Contributors

This project exists thanks to all the people who contribute. [Contribute].

Backers

Thank you to all our backers! 🙏 [Become a backer]

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]

License

Copyright © 2013-2018 Alan Dipert and Micha Niskin

Distributed under the Eclipse Public License, the same as Clojure.

boot's People

Contributors

alandipert avatar arichiardi avatar bbatsov avatar bhagany avatar burn2delete avatar deraen avatar ekroon avatar hlship avatar jgdavey avatar jumblerg avatar kitharoidos avatar kul avatar leblowl avatar martinklepsch avatar micha avatar mudgen avatar pandeiro avatar pesterhazy avatar radicalzephyr avatar ragnard avatar schmir avatar seancorfield avatar serzh avatar severeoverfl0w avatar sundbp avatar svdm avatar tirkarthi avatar tobias avatar truppert avatar wagjo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

boot's Issues

boot -d should modify environment

Ran this in boot-cljsjs:

~/c/boot-cljsjs (master=) boot -d cljsjs/react repl
nREPL server listening: 0.0.0.0:53582
REPL-y 0.3.4, nREPL 0.2.4
Clojure 1.6.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_25-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=> (def env (get-env))
#'boot.user/env
boot.user=> (map first (->> "cljsjs/" (cljsjs.app/dep-jars-on-cp env) (mapcat #(cljsjs.app/files-in-jar % "cljsjs/" [".inc.js"]))))
("cljsjs/react/react-0.11.2.inc.js")
boot.user=> (:dependencies env)
[[org.clojure/clojure "1.6.0" :scope "provided"] [boot/core "2.0.0-pre28" :scope "provided"] [adzerk/bootlaces "0.1.5" :scope "test"]]

Boot shouldn't create .boot dir in current directory if it's not writeable

Because of the way the temp dirs stuff works we can no longer defer creating the .boot directory until a task needs a temp dir. Boot itself needs tempdirs now. This means that even things like boot -h end up creating the .boot directory in the CWD. This is unacceptable if CWD isn't writeable by the user.

Solution:

  • Create temp files in directory under ~/.boot/tmp/... instead of in ./.boot.
  • Create symlink from ./.boot~/.boot/tmp/... if CWD is writeable and there are files the user might care about in there.
  • Remove the symlink if there are not interesting files in there (?).

java.io.IOException at installation with boot.exe on Windows 7 64bit

Installation on Windows 7 64 bit didn't work.

Steps:

  • downloaded boot.exe from github
  • copied to C:\Windows\System32
  • ran boot -h

Output:
Retrieving boot-2.0.0-pre21.jar from http://clojars.org/repo/
Retrieving clojure-1.6.0.jar from http://repo1.maven.org/maven2/
Retrieving shimdandy-impl-1.0.1.jar from http://clojars.org/repo/
Retrieving pod-2.0.0-pre21.jar from http://clojars.org/repo/
Retrieving core-2.0.0-pre21.jar from http://clojars.org/repo/
Retrieving worker-2.0.0-pre21.jar from http://clojars.org/repo/
Retrieving reply-0.3.4.jar from http://clojars.org/repo/
Retrieving aether-2.0.0-pre21.jar from http://clojars.org/repo/
Retrieving cd-client-0.3.6.jar from http://clojars.org/repo/
Retrieving clj-http-lite-0.2.0.jar from http://clojars.org/repo/
Retrieving drawbridge-0.0.6.jar from http://clojars.org/repo/
Retrieving clj-http-0.3.6.jar from http://clojars.org/repo/
Retrieving ring-core-1.0.2.jar from http://clojars.org/repo/
Retrieving versioneer-0.1.1.jar from http://clojars.org/repo/
Retrieving sjacket-0.1.1.jar from http://clojars.org/repo/
Retrieving parsley-0.9.2.jar from http://clojars.org/repo/
Retrieving clj-jgit-0.8.0.jar from http://clojars.org/repo/
Retrieving clj-yaml-0.4.0.jar from http://clojars.org/repo/
Retrieving clj-pgp-0.5.4.jar from http://clojars.org/repo/
Retrieving byte-streams-0.1.13.jar from http://clojars.org/repo/
Retrieving primitive-math-0.1.3.jar from http://clojars.org/repo/
Retrieving clj-tuple-0.1.5.jar from http://clojars.org/repo/
Retrieving potemkin-0.3.9.jar from http://clojars.org/repo/
Retrieving riddley-0.1.7.jar from http://clojars.org/repo/
Retrieving desiderata-1.0.2.jar from http://clojars.org/repo/
Retrieving pomegranate-0.3.0.jar from http://repo1.maven.org/maven2/
Retrieving plexus-utils-2.0.6.jar from http://repo1.maven.org/maven2/
Retrieving httpmime-4.1.2.jar from http://repo1.maven.org/maven2/
Retrieving jline-2.12.jar from http://repo1.maven.org/maven2/
Retrieving tools.cli-0.3.1.jar from http://repo1.maven.org/maven2/
Retrieving org.eclipse.jgit.java7-3.5.0.201409260305-r.jar from http://repo1.maven.org/maven2/
Retrieving org.eclipse.jgit-3.5.0.201409260305-r.jar from http://repo1.maven.org/maven2/
Retrieving jsch-0.1.50.jar from http://repo1.maven.org/maven2/
Retrieving JavaEWAH-0.7.9.jar from http://repo1.maven.org/maven2/
Retrieving core.memoize-0.5.3.jar from http://repo1.maven.org/maven2/
Retrieving core.cache-0.6.3.jar from http://repo1.maven.org/maven2/
Retrieving data.priority-map-0.0.2.jar from http://repo1.maven.org/maven2/
Retrieving snakeyaml-1.5.jar from http://repo1.maven.org/maven2/
Retrieving jlayer-1.0.1.jar from http://repo1.maven.org/maven2/
Retrieving bcpg-jdk15on-1.51.jar from http://repo1.maven.org/maven2/
Retrieving bcprov-jdk15on-1.51.jar from http://repo1.maven.org/maven2/
Retrieving jna-4.1.0.jar from http://repo1.maven.org/maven2/
Retrieving data.xml-0.0.7.jar from http://repo1.maven.org/maven2/
Retrieving data.zip-0.1.1.jar from http://repo1.maven.org/maven2/
Retrieving tools.namespace-0.2.7.jar from http://repo1.maven.org/maven2/
Exception in thread "main" java.io.IOException: The process cannot access the file because another process has locked a portion of the file
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(Unknown Source)
at java.io.ObjectOutputStream$BlockDataOutputStream.drain(Unknown Source)
at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(Unknown Source)
at java.io.ObjectOutputStream.(Unknown Source)
at boot.App.writeCache(App.java:120)
at boot.App.readCache(App.java:131)
at boot.App.main(App.java:300)

java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) Client VM (build 24.45-b08, mixed mode, sharing)

systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
OS Name: Microsoft Windows 7 Professional
OS Version: 6.1.7601 Service Pack 1 Build 7601

Watch task broken if project directory is not same as git work directory

https://github.com/boot-clj/boot/blob/master/boot/core/src/boot/task/built_in.clj#L152

Watch task checks if the changed file is in git index. ls-files returns a set of files relative to git work directory. Watch task checks if the set contains changed file but the changed file is relative to project directory.

ls-files could be fixed to return set of files relative to project directory...
But perhaps it makes more sense to just remove gitignore-matcher: only src- and rsc-paths are watched for changes so not too many uninteresting files should trigger change.

Boot watch problem on OSX

This ticket was reported on the Discourse site.

I've been trying to have a look at boot, working through the example detailed http://adzerk.com/blog/2014/11/clojurescript-builds-rebooted/ but I'm having problems getting the watch task to work as expected on OSX - it does not detect any changes to the source files (when running on Ubuntu it works fine).

I'm wondering if this is a known issue, or a problem with my environment and if there are any guidance as to how I can try to debug.

My setup is -

OSX 10.9.5

boot -u
https://github.com/boot-clj/boot
Mon Nov 10 20:32:42 GMT 2014
BOOT_CLOJURE_VERSION=1.6.0
BOOT_VERSION=2.0.0-pre22

and tried both-

java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)

java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)

boot fails when the local git repository is somewhere other that its working directory.

boot assumes that .git will be in the same directory as .boot, but this isn't always the case. a boot task such as tomcat, for example, might have have .git in the root with two nested projects containing different boot build scripts.

(defn last-commit
  []
  (jgit/with-repo "."
    (->> (jgit/git-log repo) first .getName)))
   clojure.lang.ExceptionInfo: The Git repository at '.' could not be located.
    data: {:file
           "/var/folders/62/r3g8yy650tvf6zhl_yv6ql9r0000gn/T/boot.user4645738998745492098.clj",
           :line 15}
java.io.FileNotFoundException: The Git repository at '.' could not be located.
clj-jgit.porcelain/load-repo                     porcelain.clj:  60
       boot.jgit/last-commit                          jgit.clj:  29
                         ...
          clojure.core/apply                          core.clj: 624
       boot.pod/eval-fn-call                           pod.clj: 155
            boot.pod/call-in                           pod.clj: 162
                         ...
            boot.pod/call-in                           pod.clj: 165
        boot.pod/call-worker                           pod.clj: 170
        boot.git/last-commit                           git.clj:   9
    boot.task.built-in/fn/fn                      built_in.clj: 221
                         ...
          clojure.core/apply                          core.clj: 624
      boot.user/eval67/fn/fn  boot.user4645738998745492098.clj:  11
                         ...
         boot.user/eval73/fn  boot.user4645738998745492098.clj:  13
                         ...
          clojure.core/apply                          core.clj: 624
   boot.core/construct-tasks                          core.clj: 300
                         ...
          clojure.core/apply                          core.clj: 624
            boot.user/eval87  boot.user4645738998745492098.clj:  17
                         ...
          boot.main/-main/fn                          main.clj: 126
             boot.main/-main                          main.clj: 126
                         ...
            boot.App.runBoot                          App.java: 216
               boot.App.main                          App.java: 302

some warnings I don't understand

I have written down the code from this tutorial
https://github.com/swannodette/om/wiki/Intermediate-Tutorial

With lein the clojurescript code compiles.

With boot 5 warnings pop up. Here they are

[email protected] boot-cljs-example$ boot watch speak cljs-repl cljs -usO none reload
<< started reload server on ws://localhost:8090 >>
Starting file watcher (CTRL-C to quit)...

nREPL server listening: 0.0.0.0:37240
Compiling main.js...
WARNING: Use of undeclared Var cljs.core.async/do-alts at line 62 file:/home/me/.m2/repository/org/clojure/core.async/0.1.267.0-0d7780-alpha/core.async-0.1.267.0-0d7780-alpha.jar!/cljs/core/async/impl/ioc_helpers.cljs
WARNING: Bad method signature in protocol implementation, impl/Handler does not declare method called lock-id at line 205 file:/home/me/.m2/repository/org/clojure/core.async/0.1.267.0-0d7780-alpha/core.async-0.1.267.0-0d7780-alpha.jar!/cljs/core/async.cljs
WARNING: Use of undeclared Var cljs.core.async.impl.protocols/lock-id at line 208 file:/home/me/.m2/repository/org/clojure/core.async/0.1.267.0-0d7780-alpha/core.async-0.1.267.0-0d7780-alpha.jar!/cljs/core/async.cljs
WARNING: Bad method signature in protocol implementation, impl/Handler does not declare method called lock-id at line 205 .boot/tmp/m6p/ye6vpt/out/cljs/core/async.cljs
WARNING: Use of undeclared Var cljs.core.async.impl.protocols/lock-id at line 208 .boot/tmp/m6p/ye6vpt/out/cljs/core/async.cljs
Adding <script> tags to html...
Elapsed time: 20,862 sec

Here's my build.boot file

(set-env!
 :src-paths    #{"src/clj" "src/cljs"}
 :rsc-paths    #{"html"}
 :dependencies '[[adzerk/boot-cljs      "0.0-2371-22" :scope "test"]
                 [adzerk/boot-cljs-repl "0.1.5"       :scope "test"]
                 [adzerk/boot-reload    "0.1.3"       :scope "test"]
                 ;[ring/ring "1.2.1"]
                 [org.clojure/clojurescript "0.0-2173"]
                 [org.clojure/core.async "0.1.267.0-0d7780-alpha"]
                 [om "0.5.3"]
                 [om-sync "0.1.1"]
                 ;[compojure "1.1.6"]
                 [fogus/ring-edn "0.2.0"]
                 ;[com.datomic/datomic-free "0.9.4699"]
                 ])

(require
 '[adzerk.boot-cljs      :refer :all]
... blah blah

and here's my lein project.clj file

(defproject om-async "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}

  :jvm-opts ^:replace ["-Xmx1g" "-server"]

  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojurescript "0.0-2173"]
                 [ring/ring "1.2.1"]
                 [org.clojure/core.async "0.1.267.0-0d7780-alpha"]
                 [om "0.5.3"]
                 [om-sync "0.1.1"]
                 [compojure "1.1.6"]
                 [fogus/ring-edn "0.2.0"]
                 [com.datomic/datomic-free "0.9.4699"]]
... blah blah blah

As you can see core.async and clojurescript are exactly the same in both lein and boot.

What am I missing ?

Include more :src-paths examples in documentation

If I have a build.boot file that references my src directory, I can require and use namespaces from that code.

For example, with these files

.
├── build.boot
└── src
    └── my_namespace
        └── app.clj

src/my_namespace/app.clj

(ns my-namespace.app)

(defn start []
  (println "Hello World"))

build.boot

(set-env! :src-paths #{"src"})

(require '[my-namespace.app :as app])

(deftask run
  "Run my app"
  []
  (app/start))

I can execute boot run and it will print Hello World

$ boot run
Hello World

It wasn't obvious to me from the docs that this would work.

From the docs I can see three ways to run Clojure code in boot:

  • Pull in a dependency from Maven
  • Include the source code directly in a .boot script.
  • Build a jar

I initially assumed source paths were just referenced by build tasks, which consume Clojure or ClojureScript source code. But it looks like the source is actually compiled and available within .boot scripts.

I was looking for something like "lein run" to load code from a source path and execute it, but it took me a while to figure out how to do it.

I think some more explanation and examples about :src-paths would be helpful in understanding boot.

refactor verbosity levels

We should support maybe 4 levels:

level option logging stack traces stack trace formatting
0 -q none none none
1 none info + warn + fail elided formatted & colorized
2 -v info + warn + fail full formatted & colorized
3 -vv dbug + info + warn + fail full none

On OSX changes take a couple seconds to be noticed

I can see barbarywatchservice is used as a WatchService implementation on OSX. On my machine it still takes a couple seconds before a change is noticed.

There is another way to reduce the time to notice a file change that works pretty well for me (JVM 1.8.0-b132):

(ns ...
   (:import com.sun.nio.file.SensitivityWatchEventModifier))

(.register p ws
             (into-array (type StandardWatchEventKinds/ENTRY_MODIFY) (vector StandardWatchEventKinds/ENTRY_MODIFY))
             (into-array (type SensitivityWatchEventModifier/HIGH) (vector SensitivityWatchEventModifier/HIGH)))

From my experience using this trick changes are noticed almost instantaneously. It would also reduce the codebase complexity. The downside is that it introduces a dependency on com.sun.nio that might need some special care for VM not shipping with this package.

boot show -u should show new releases (shows old)

My dependencies in build.boot look like:

[org.clojure/clojurescript "0.0-2411" :scope "provided"]
[om "0.8.0-beta3" :scope "provided"]

According to the help on the boot show command:

  -u, --updates    Print newer releases of outdated dependencies.

However, when I run boot show -u, I get the following:

[org.clojure/clojurescript "0.0-2411" :scope "provided"]
[om "0.8.0-beta3" :scope "provided"]

In other words, I don't get the newer versions promised by the help text. (This used to work.)

javac should inherit environment classpath

I tried to make a Java shim to invoke a Clojure function, pending AOT support (which we should also have btw):

import clojure.java.api.Clojure;
import clojure.lang.IFn;

public class Main {
  public static void main(String [] args) {
    IFn seq = Clojure.var("clojure.core", "seq");
    Clojure.var("main", "-main").applyTo(seq.invoke(args));
  }
}

It didn't work, because clojure.java.api.Clojure and clojure.lang.IFn aren't on the classpath as far as the Java compiler is concerned. I'm not 100% sure it's possible, but maybe we can get the javac task to inherent the parent environment classpath:
http://stackoverflow.com/questions/1563909/how-to-set-classpath-when-i-use-javax-tools-javacompiler-compile-the-source

Confusing set-env! semantics for different keys

This is probably related to #16 in a more general sense.

In the following script I tried to establish additional src/resource paths and dependencies for the dev environment (calling set-env! in a dev task).:

(set-env!
 :src-paths    #{"src/clj", "src/cljs"}
 :rsc-paths    #{"resources"}
 :dependencies '[[compojure "1.2.1"]])

(deftask dev
  "Start development environment"
  []
  (set-env! :rsc-paths #{"dev/resources"})
  (set-env! :src-paths #{"dev/clj"})
  (set-env! :dependencies '[[org.clojure/tools.namespace "0.2.7"]])
  (println "Deps" (get-env :dependencies))
  (println "Src" (get-env :src-paths))
  (println "Res" (get-env :rsc-paths)))

which produces

=> boot dev
Deps [[org.clojure/tools.namespace 0.2.7]]
Src #{dev/clj src/cljs src/clj}
Res #{dev/resources}

Am I using the set-env! function correctly for the result I want to get (dev environment/profile)?

In any case the semantics of set-env! are confusing.
Currently set-env! works by delegating to merge-env!, which merges sources, overwrites resources (seems like a bug as there is a defmethod for :src, but not for :rsc) and overwrites dependencies (although it seems to be calling add-dependency! with an old dependency set).
I'd think that set-env! should always overwrite, not merge, while update-env! should accept a function analogous to clojure.core/update. There could be a simplified version of update-env! - append-env! (or conj-env!) which would always try to merge the values if it made sense and fail otherwise. Could you please explain the reasoning behind the current semantics of set-env! and merge-env!?

I think Boot is a great project and a step forward for the Clojure ecosystem. I'd love to contribute, so understanding the design decisions a bit better would help a great deal!

How is updating handled?

I assume the boot.sh script would change over time.
How are you making sure people update from time to time?
Is that somehow built into boot.sh or should boot be added to
something like homebrew for that kind of stuff?

PS.: I was very sceptic in the first place but I begin to see where and why the approach boot has chosen might be useful. Will play around with it a bit throughout the day :-)

Establish stable public API

Need to refactor boot such that namespaces providing a stable public API are clearly separated from implementation and namespaces used internally by boot. Ideally there would be no more than say 4 public API namespaces, organized mainly around the 3 types of pods provided by boot (core, worker, and task pods).

File watching issues on Linux (tempdirs)

Hi,

When trying tempdirs branch, I have problems with file watching. It seems that syncing files from source dirs to tempdirs is working but watch-task is broken. For some reason the watch-key is invalid.

Watch-service doesn't have any real changes on tempdirs branch so it could be that problem exists in master but is only triggered by something on tempdirs branch.

A watch key is created when a watchable object is registered with a watch service. The key remains valid until:

- It is cancelled, explicitly, by invoking its cancel method, or
- Cancelled implicitly, because the object is no longer accessible, or
- By closing the watch service.

I've been trying to debug this and I'm quite confident I have checked that none of those things are happening. I have added some debug prints to boot code and I have checked that

  • isValid is called right after retrieving it from watchService so there is no chance of cancel being called?
  • Dir being watched exists
  • Watch service has not been closed
~/Source/boot-cljx tempdirs  2m 38s
❯ boot dev
Starting new-watch-service src
Sync (src)  ->  #<File .boot/tmp/k3b/rcesf6>
([:cp #<File src/deraen/boot_cljx/impl.clj> #<File .boot/tmp/k3b/rcesf6/deraen/boot_cljx/impl.clj>] [:cp #<File src/deraen/boot_cljx.clj> #<File .boot/tmp/k3b/rcesf6/deraen/boot_cljx.clj>])
Sync (src)  ->  #<File .boot/tmp/k3b/rcesf6>
()
Sync (src)  ->  #<File .boot/tmp/k3b/rcesf6>
()
Starting new-watch-service .boot/tmp/k3b/6qp7pj, .boot/tmp/k3b/rcesf6, .boot/tmp/k3b/-p9hf6h
Starting file watcher (CTRL-C to quit)...

Sync (src)  ->  #<File .boot/tmp/k3b/rcesf6>
()
#<LinuxWatchKey sun.nio.fs.LinuxWatchService$LinuxWatchKey@4eb69366> watchable #<UnixPath .boot/tmp/k3b/rcesf6/deraen/boot_cljx> is-valid false exists true
watch service: invalid watch key
nREPL server listening: 0.0.0.0:49714
Writing pom.xml and pom.properties...
Writing boot-cljx-0.2.0-SNAPSHOT.jar...
Installing boot-cljx-0.2.0-SNAPSHOT.jar...
([:cp #<File .boot/tmp/k3b/11lgcg/boot-cljx-0.2.0-SNAPSHOT.jar> #<File target/boot-cljx-0.2.0-SNAPSHOT.jar>])
Elapsed time: 4.538 sec

What should be the next steps in resolving this?
Can anyone else reproduce this? https://github.com/Deraen/boot-cljx/tree/tempdirs and running boot dev should print "watch service: invalid watch key"
If needed I think I could create virtualbox image to test this (or Vagrant script which can be used to create the image).

clojure-aot task

We should have a task that takes a list of namespaces to AOT compile. Until we do, or #40 is resolved, we don't have a way to create an executable Clojure without leaving boot.

:exclusions break pom task

build.boot:

(set-env! :dependencies '[[lein-beanstalk "0.2.7" :exclusions [lein-ring ]]])

(task-options!
 pom '{:project extest
       :version "1.0.0"})

What I ran:

boot pom

The result:

alandipert@alanputer:~/Desktop/extest boot pom
Writing pom.xml and pom.properties...
             clojure.lang.ExceptionInfo: java.lang.UnsupportedOperationException: nth not supported on this type: Symbol
    data: {:file
           "/var/folders/1l/yypdg1hj2_1chvrfw7j5wc0r0000gn/T/boot.user86422816965862564.clj",
           :line 19}
java.util.concurrent.ExecutionException: java.lang.UnsupportedOperationException: nth not supported on this type: Symbol
java.lang.UnsupportedOperationException: nth not supported on this type: Symbol
                                   ...
boot.pom/pom-xml/iter/fn/fn/iter/fn/fn                         pom.clj:   70
   boot.pom/pom-xml/iter/fn/fn/iter/fn                         pom.clj:   70
                                   ...

This was first reported by @pleasetrythisathome in IRC

Not compatible with Cygwin?

Can't seem to get it running under Cygwin. I shall try and illustrate by copy pasting commands from mintty:

$ ls -l boot.sh
-rwxr-xr-x+ 1 Domas None 7012598 Dec  7 21:23 boot.sh

$ boot.sh
Error: Unable to access jarfile /home/Domas/bin/boot.sh

$ java -jar boot.sh -V
#https://github.com/boot-clj/boot
#Mon Dec 08 10:27:04 EET 2014
BOOT_CLOJURE_VERSION=1.6.0
BOOT_VERSION=2.0.0-pre28

Now here comes the crash (?). I will run the repl, but it will not respond to any input, I will ultimately Ctrl-c:

Domas@DomoHP-Win81 ~/bin
$ java -jar boot.sh repl
nREPL server listening: 0.0.0.0:49542
REPL-y 0.3.4, nREPL 0.2.4
Clojure 1.6.0
Java HotSpot(TM) 64-Bit Server VM 1.7.0_71-b14
        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=> (+ 2 2)

$ (+ 2 2)
-bash: +: command not found

Some additional information below. Notably I am using a Windows Java installation, which has not caused problems running sbt or leiningen by the way.

$ java -version
java version "1.7.0_71"
Java(TM) SE Runtime Environment (build 1.7.0_71-b14)
Java HotSpot(TM) 64-Bit Server VM (build 24.71-b01, mixed mode)

$ javac -version
javac 1.7.0_71

$ which java
/cygdrive/c/Program Files/Java/jdk1.7.0_71/bin/java

$ which javac
/cygdrive/c/Program Files/Java/jdk1.7.0_71/bin/javac

$ echo $TERM
xterm-256color

Creating directories doesn't trigger watcher

I had a directory in my resource paths, and started the watcher. Then I added a directory an existing directory, put a file on it, and observed the watcher didn't do anything.

add-sync! doesn't sync files if they've been deleted in destination

(core/task-options! garden [:styles-var 'creationist.styles/screen
                            :output-to  "public/css/screen.css"])

(add-sync! "public" ["target/public"])

I have something like this in my build.boot, now I do the following

  1. boot watch garden
  2. rm public/css/screen.css (i.e. remove target file from add-sync!'s destination)
  3. rm target/public/css/screen.css (that's add-sync!'s source)
  4. Trigger build cycle by modifying styles.clj

The result is that I have a newly compiled target/public/css/screen.css file but don't have a file in public/css/screen.css. This happened with pre21.

When restarting the boot watch garden task it properly copies the file into the public dir again.
I assume you could verify this with other tasks that put files into a mktgtdir! as well.

Add -c/--chroot option to sift task

The sift task should accept a -c or --chroot option that performs a transformation on all output files in the fileset, moving them to a different "root" in the fileset. For example, suppose a fileset has the output file foo/bar.x:

$ boot sift -c asdf/qwer

would produce the file asdf/qwer/foo/bar.x in the target dir.

boot show -u fails when SNAPSHOT dependencies present

Thanks @whodidthis for reporting the issue. This repo whodidthis/boot-show-u demonstrates the problem.

Description of Problem

Lein project [example.checkout "0.1.0-SNAPSHOT"] installed locally. Boot project has dependency on the Lein project.

build.boot

#!/usr/bin/env boot

(set-env!
  :src-paths #{"src"}
  :dependencies '[[adzerk/boot-cljs "0.0-2371-25"]
                  [example.checkout "0.1.0-SNAPSHOT"]])

(require '[adzerk.boot-cljs :refer :all])

(task-options!
  pom [:project 'boot-show-u
       :version "0.1.0-SNAPSHOT"])

To reproduce the error:

$ boot -V
#https://github.com/boot-clj/boot
#Thu Nov 20 10:21:50 EST 2014
BOOT_CLOJURE_VERSION=1.6.0
BOOT_VERSION=2.0.0-pre24
$ boot show -u
                                  clojure.lang.ExceptionInfo: org.sonatype.aether.resolution.DependencyResolutionException: Failed to collect dependencies for
 [#<Dependency adzerk:boot-cljs:jar:RELEASE (compile)> #<Dependency example.checkout:example.checkout:jar:RELEASE (compile)>]
    data: {:file
           "/var/folders/5m/8zs_v2nx3sz_r3bt_nj9344h0000gn/T/boot.user3825422000268151430.clj",
           :line 17}
                     java.util.concurrent.ExecutionException: org.sonatype.aether.resolution.DependencyResolutionException: Failed to collect dependencies for
 [#<Dependency adzerk:boot-cljs:jar:RELEASE (compile)> #<Dependency example.checkout:example.checkout:jar:RELEASE (compile)>]
org.sonatype.aether.resolution.DependencyResolutionException: Failed to collect dependencies for [#<Dependency adzerk:boot-cljs:jar:RELEASE (compile)> #<Depen
dency example.checkout:example.checkout:jar:RELEASE (compile)>]
    result: #<DependencyResult [adzerk:boot-cljs:jar:0.0-2371-25 < /Users/micha/.m2/repository (enhanced)]>
org.sonatype.aether.collection.DependencyCollectionException: Failed to collect dependencies for [#<Dependency adzerk:boot-cljs:jar:RELEASE (compile)> #<Depen
dency example.checkout:example.checkout:jar:RELEASE (compile)>]
    result: #<CollectResult [adzerk:boot-cljs:jar:0.0-2371-25 (compile)]>
  org.sonatype.aether.resolution.ArtifactDescriptorException: Failed to read artifact descriptor for example.checkout:example.checkout:jar:RELEASE
    result: #<ArtifactDescriptorResult example.checkout:example.checkout:jar:RELEASE -> []>
   org.sonatype.aether.resolution.VersionResolutionException: Failed to resolve version for example.checkout:example.checkout:jar:RELEASE
    result: #<VersionResult null @ null>
                 org.apache.maven.repository.internal.DefaultVersionResolver.resolveVersion           DefaultVersionResolver.java:  287
               org.apache.maven.repository.internal.DefaultArtifactDescriptorReader.loadPom  DefaultArtifactDescriptorReader.java:  250
org.apache.maven.repository.internal.DefaultArtifactDescriptorReader.readArtifactDescriptor  DefaultArtifactDescriptorReader.java:  186
                       org.sonatype.aether.impl.internal.DefaultDependencyCollector.process       DefaultDependencyCollector.java:  412
           org.sonatype.aether.impl.internal.DefaultDependencyCollector.collectDependencies       DefaultDependencyCollector.java:  240
              org.sonatype.aether.impl.internal.DefaultRepositorySystem.resolveDependencies          DefaultRepositorySystem.java:  333
                                                                                        ...
                                          cemerick.pomegranate.aether/resolve-dependencies*                            aether.clj:  720
                                                                                        ...
                                                                         clojure.core/apply                              core.clj:  624
                                           cemerick.pomegranate.aether/resolve-dependencies                            aether.clj:  729
                                                                                        ...
                                                          boot.aether/resolve-dependencies*                            aether.clj:   73
                                                                                        ...
                                                                         clojure.core/apply                              core.clj:  624
                                                                    clojure.core/memoize/fn                              core.clj: 5846
                                                                                        ...
                                                           boot.aether/resolve-dependencies                            aether.clj:   92
                                                                                        ...
                                                                         clojure.core/apply                              core.clj:  624
                                                                      boot.pod/eval-fn-call                               pod.clj:  174
                                                                           boot.pod/call-in                               pod.clj:  181
                                                                                        ...
                                                                           boot.pod/call-in                               pod.clj:  184
                                                                       boot.pod/call-worker                               pod.clj:  189
                                                              boot.pod/resolve-dependencies                               pod.clj:  220
                                                                          boot.pod/outdated                               pod.clj:  240
                                                                                        ...
                                                          boot.task.built-in/fn/fn/fn/fn/fn                          built_in.clj:  113
                                                             boot.task.built-in/fn/fn/fn/fn                          built_in.clj:  109
                                                                        boot.core/run-tasks                              core.clj:  327
                                                                       boot.user/eval134/fn      boot.user3825422000268151430.clj:   19
                                                        clojure.core/binding-conveyor-fn/fn                              core.clj: 1910
                                                                                        ...

Workaround

This issue appears to be related to boot -u and snapshot dependencies that are installed locally. The issue can be worked around by adding the -s (--snapshots) option:

$ boot show -us

add without-exiting macro

Various Java libraries presume to own the JVM and call System/exit willy-nilly. Libraries like this can be hard to wrap with tasks. So, we should have a macro task authors can use to run code without worrying about it killing the JVM.

Ctrl-C doesn't kill watch task

$ boot watch
Starting file watcher (CTRL-C to quit)...

{:time 1415181737849, :id G__58}
Elapsed time: 2.253 sec

^C^C^C

OS X 10.10
Java 1.7.0_71 and 1.8.0_25

task-options! shouldn't merge options when updating via function

When task-options! is passed an update function instead of an option map, the correct behavior should be to replace the current options with the result of applying the update fn to them. The current (erroneous) behavior is to merge the current options with the result.

:tgt-path has to be set before :rsc-paths in set-env!

If you set :tgt-path after setting :rsc-paths, it just dumps resources into target/ (default).

(set-env!
  :tgt-path "blah"
  :rsc-paths #{"resources/public"})

works, dumping resources into blah/

(set-env!
  :rsc-paths #{"resources/public"})
  :tgt-path "blah")

doesn't work as expected, dumping resources into target/

*data-readers* not properly populated

When using boot with Datomic, it seems as if *data-readers* is not being properly populated.

In boot, *data-readers* is empty, even with Datomic included as a dependency:

code/tmp  ▸ boot -d com.datomic/datomic-free repl
nREPL server listening: 0.0.0.0:59655
REPL-y 0.3.5, nREPL 0.2.6
Clojure 1.6.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_20-b26
        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=> *data-readers*
{}

Whereas in Leiningen, *data-readers* contains Datomic's db/id et al readers.

code/tmp  ▸ lein try com.datomic/datomic-free
WARNING!!! version ranges found for:
[com.datomic/datomic-free "RELEASE"] -> [com.amazonaws/aws-java-sdk "1.8.11" :exclusions [javax.mail/mail org.apache.httpcomponents/httpclient commons-logging]] -> [com.amazonaws/aws-java-sdk-core "1.8.11"] -> [joda-time "[2.2,)"]
Consider using [com.datomic/datomic-free "0.9.5078" :exclusions [joda-time]].


nREPL server started on port 59665 on host 127.0.0.1 - nrepl://127.0.0.1:59665
REPL-y 0.3.5, nREPL 0.2.6
Clojure 1.6.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_20-b26
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> *data-readers*
{db/id #'datomic.db/id-literal, db/fn #'datomic.function/construct, base64 #'datomic.codec/base-64-literal}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.