|
1 | 1 | # build-clj
|
| 2 | + |
2 | 3 | Common build tasks abstracted into a library.
|
| 4 | + |
| 5 | +Having implemented `build.clj` in several of my open source projects |
| 6 | +I found there was a lot of repetition across them, so I factored out |
| 7 | +the common functionality into this library. |
| 8 | + |
| 9 | +Since it depends on `tools.build` and |
| 10 | +[Erik Assum's `deps-deploy`](https://github.com/slipset/deps-deploy), |
| 11 | +your `:build` alias can just be: |
| 12 | + |
| 13 | +```clojure |
| 14 | + :build {:deps {io.github.seancorfield/build-clj |
| 15 | + {:git/tag "v0.1.0" :git/sha "..."}} |
| 16 | + :ns-default build} |
| 17 | +``` |
| 18 | + |
| 19 | +Your `build.clj` can start off as follows: |
| 20 | + |
| 21 | +```clojure |
| 22 | +(ns build |
| 23 | + (:require [clojure.tools.build.api :as b] |
| 24 | + [org.corfield.build :as bb])) |
| 25 | + |
| 26 | +(def lib 'myname/mylib) |
| 27 | +;; if you want a version of MAJOR.MINOR.COMMITS: |
| 28 | +(def version (format "1.0.%s" (b/git-count-revs nil))) |
| 29 | +``` |
| 30 | + |
| 31 | +The following common build tasks are provided, all taking an options |
| 32 | +hash map as the single argument _and returning that hash map unchanged_ |
| 33 | +so you can reliably thread the build tasks. |
| 34 | +_[Several functions in `clojure.tools.build.api` return `nil` instead]_ |
| 35 | + |
| 36 | +* `clean` -- clean the target directory, |
| 37 | +* `deploy` -- deploy to Clojars, |
| 38 | +* `jar` -- build the (library) JAR and `pom.xml` files, |
| 39 | +* `run-tests` -- run the project's tests. |
| 40 | + |
| 41 | +For `deploy` and `jar`, you must provide at least `:lib` and `:version`. |
| 42 | +Everything else has "sane" defaults, but can be overridden. |
| 43 | + |
| 44 | +You might typically have the following tasks in your `build.clj`: |
| 45 | + |
| 46 | +```clojure |
| 47 | +(defn ci "Run the CI pipeline of tests (and build the JAR)." [opts] |
| 48 | + (-> opts |
| 49 | + (assoc :lib lib :version version) |
| 50 | + (bb/run-tests) |
| 51 | + (bb/clean) |
| 52 | + (bb/jar))) |
| 53 | + |
| 54 | +(defn deploy "Deploy the JAR to Clojars." [opts] |
| 55 | + (-> opts |
| 56 | + (assoc :lib lib :version version) |
| 57 | + (bb/deploy))) |
| 58 | +``` |
| 59 | + |
| 60 | +In addition, there is a `run-task` function that takes an options hash |
| 61 | +map and a vector of aliases. This runs an arbitrary Clojure main function, |
| 62 | +determined by those aliases, in a subprocess. `run-tests` uses this by |
| 63 | +adding a `:test` alias and in the absence of any `:main-opts` behind those |
| 64 | +aliases, assumes it should run `cognitect.test-runner`'s `-main` function. |
| 65 | + |
| 66 | +If you want a `run-tests` task in your `build.clj`, independent of the `ci` |
| 67 | +task shown above, the following can be added: |
| 68 | + |
| 69 | +```clojure |
| 70 | +(defn run-tests "Run the tests." [opts] |
| 71 | + (-> opts (bb/run-tests))) |
| 72 | +``` |
| 73 | + |
| 74 | +`run-task` picks up `:jvm-opts` and `:main-opts` from the specified aliases |
| 75 | +and uses them as the `:java-args` and `:main-args` respectively in a call to |
| 76 | +`clojure.tools.build.api/java-command` to build the `java` command to run. |
| 77 | +By default, it runs `clojure.main`'s `-main` function with the specified |
| 78 | +`:main-args`. |
| 79 | + |
| 80 | +For example, if your `deps.edn` contains the following alias: |
| 81 | + |
| 82 | +```clojure |
| 83 | + :eastwood {:extra-deps {jonase/eastwood {:mvn/version "0.5.1"}} |
| 84 | + :main-opts ["-m" "eastwood.lint" "{:source-paths,[\"src\"]}"]} |
| 85 | +``` |
| 86 | + |
| 87 | +Then you can define an `eastwood` task in your `build.clj` file: |
| 88 | + |
| 89 | +```clojure |
| 90 | +(defn eastwood "Run Eastwood." [opts] |
| 91 | + (-> opts (bb/run-task [:eastwood]))) |
| 92 | +``` |
| 93 | + |
| 94 | +Or you could just make it part of your `ci` pipeline without adding that function: |
| 95 | + |
| 96 | +```clojure |
| 97 | +(defn ci "Run the CI pipeline of tests (and build the JAR)." [opts] |
| 98 | + (-> opts |
| 99 | + (assoc :lib lib :version version) |
| 100 | + (bb/run-task [:eastwood]) |
| 101 | + (bb/run-tests) |
| 102 | + (bb/clean) |
| 103 | + (bb/jar))) |
| 104 | +``` |
| 105 | + |
| 106 | +## Defaults |
| 107 | + |
| 108 | +The following defaults are provided: |
| 109 | + |
| 110 | +* `:target` -- `"target"`, |
| 111 | +* `:basis` -- `(create-basis {:project "deps.edn"}`, |
| 112 | +* `:class-dir` -- `(str target "/classes")`, |
| 113 | +* `:jar-file` -- `(format \"%s/%s-%s.jar\" target lib version)`. |
| 114 | + |
| 115 | +For the functions defined in `org.corfield.build`, you can override |
| 116 | +the defaults as follows: |
| 117 | + |
| 118 | +* `clean` |
| 119 | + * `:target`, |
| 120 | +* `deploy` |
| 121 | + * Requires: `:lib` and `:version`, |
| 122 | + * `:target`, `:class-dir`, `:jar-file`, |
| 123 | +* `jar` |
| 124 | + * Requires: `:lib` and `:version`, |
| 125 | + * `:target`, `:class-dir`, `:basis`, `:src-dirs`, `:tag` (defaults to `(str "v" version)`), `:jar-file`, |
| 126 | +* `run-tests` |
| 127 | + * `:aliases` -- for any additional aliases beyond `:test` which is always added, |
| 128 | + * Also accepts any options that `run-task` accepts. |
| 129 | + |
| 130 | +As noted above, `run-task` takes an options hash map and a vector of aliases. |
| 131 | +The following options can be provided to `run-task` to override the default |
| 132 | +behavior: |
| 133 | + |
| 134 | +* `:java-opts` -- used _instead of_ `:jvm-opts` from the aliases, |
| 135 | +* `:jvm-opts` -- used _in addition to_ the `:java-opts` vector or _in addition to_ `:jvm-opts` from the aliases, |
| 136 | +* `:main` -- used _instead of_ `'clojure.main` when building the `java` command to run, |
| 137 | +* `:main-args` -- used _instead of_ `:main-opts` from the aliases, |
| 138 | +* `:main-opts` -- used _in addition to_ the `:main-args` vector or _in addition to_ `:main-opts` from the aliases. |
| 139 | + |
| 140 | +> Note: if `:main-args` is not provided and there are no `:main-opts` in the aliases provided, the default will be `["-m" "cognitect.test-runner"]` to ensure that `run-tests` works by default without needing `:main-opts` in the `:test` alias (since it is common to want to start a REPL with `clj -A:test`). |
| 141 | +
|
| 142 | +## Projects Using `build-clj` |
| 143 | + |
| 144 | +You can see how `build-clj` is used to reduce boilerplate in the |
| 145 | +`build.clj` file of the following projects: |
| 146 | + |
| 147 | +* [`depstar`](https://github.com/seancorfield/depstar/blob/develop/build.clj) |
| 148 | +* [`expectations`](https://github.com/clojure-expectations/clojure-test/blob/develop/build.clj) |
| 149 | +* [`honeysql`](https://github.com/seancorfield/honeysql/blob/develop/build.clj) |
| 150 | +* [`next.jdbc`](https://github.com/seancorfield/next-jdbc/blob/develop/build.clj) |
| 151 | + |
| 152 | +# License |
| 153 | + |
| 154 | +Copyright © 2021 Sean Corfield |
| 155 | + |
| 156 | +Distributed under the Apache Software License version 2.0. |
0 commit comments