Skip to content
This repository was archived by the owner on Feb 24, 2023. It is now read-only.

Commit fe2d586

Browse files
committed
Initial version
Lifted from and tested against depstar, expectations, honeysql, and next.jdbc.
1 parent 91ff79d commit fe2d586

File tree

4 files changed

+299
-13
lines changed

4 files changed

+299
-13
lines changed

.gitignore

+23-13
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1-
pom.xml
2-
pom.xml.asc
3-
*.jar
4-
*.class
5-
/lib/
6-
/classes/
7-
/target/
8-
/checkouts/
9-
.lein-deps-sum
10-
.lein-repl-history
11-
.lein-plugins/
12-
.lein-failures
1+
.classpath
2+
.clj-kondo/.cache
3+
.cpcache
4+
.eastwood
5+
.factorypath
6+
.java-version
7+
.lsp/sqlite.db
138
.nrepl-port
14-
.cpcache/
9+
.project
10+
.rebel_readline_history
11+
.settings
12+
.socket-repl-port
13+
.sw*
14+
.vscode
15+
*.class
16+
*.jar
17+
*.swp
18+
*~
19+
/classes
20+
/classes
21+
/clojure_test_*
22+
/cljs-test-runner-out
23+
/derby.log
24+
/target

README.md

+154
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,156 @@
11
# build-clj
2+
23
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.

deps.edn

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{:deps
2+
{io.github.clojure/tools.build {:git/tag "v0.1.9" :git/sha "6736c83"}
3+
io.github.slipset/deps-deploy {:sha "b4359c5d67ca002d9ed0c4b41b710d7e5a82e3bf"}}}

src/org/corfield/build.clj

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
;; copyright (c) 2021 sean corfield, all rights reserved.
2+
3+
(ns org.corfield.build
4+
"Common build utilities.
5+
6+
The following defaults are provided:
7+
:target \"target\",
8+
:basis (create-basis {:project \"deps.edn\"},
9+
:class-dir (str target \"/classes\"),
10+
:jar-file (format \"%s/%s-%s.jar\" target lib version)
11+
12+
You are expected to provide :lib and :version as needed.
13+
14+
The following build functions are provided, with the
15+
specified required and optional hash map options:
16+
17+
clean -- opt :target,
18+
deploy -- req :lib, :version
19+
opt :target, :class-dir, :jar-file
20+
jar -- req :lib, :version
21+
opt :target, :class-dir, :basis, :src-dirs, :tag, :jar-file,
22+
run-task -- [opts aliases]
23+
opt :java-opts -- defaults to :jvm-opts from aliases
24+
:jvm-opts -- added to :java-opts
25+
:main -- defaults to clojure.main
26+
:main-args -- defaults to :main-opts from aliases
27+
:main-opts --
28+
run-tests -- opt :aliases (plus run-task options)
29+
invokes (run-task opts (into [:test] aliases))
30+
31+
All of the above return the opts hash map they were passed
32+
(unlike some of the functions in clojure.tools.build.api)."
33+
(:require [clojure.tools.build.api :as b]
34+
[clojure.tools.deps.alpha :as t]
35+
[deps-deploy.deps-deploy :as dd]))
36+
37+
(def ^:private default-target "target")
38+
(def ^:private default-basis (b/create-basis {:project "deps.edn"}))
39+
(defn- default-class-dir [target] (str target "/classes"))
40+
(defn- default-jar-file [target lib version]
41+
(format "%s/%s-%s.jar" target (name lib) version))
42+
43+
(defn clean
44+
"Remove the target folder."
45+
[{:keys [target] :as opts}]
46+
(println "\nCleaning target...")
47+
(b/delete {:path (or target default-target)})
48+
opts)
49+
50+
(defn jar
51+
"Build the library JAR file.
52+
53+
Requires: lib, version"
54+
[{:keys [target class-dir lib version basis src-dirs tag jar-file] :as opts}]
55+
(assert (and lib version) "lib and version are required for jar")
56+
(let [target (or target default-target)
57+
class-dir (or class-dir (default-class-dir target))
58+
basis (or basis default-basis)
59+
src-dirs (or src-dirs ["src"])
60+
tag (or tag (str "v" version))
61+
jar-file (or jar-file (default-jar-file target lib version))]
62+
(println "\nWriting pom.xml...")
63+
(b/write-pom {:class-dir class-dir
64+
:lib lib
65+
:version version
66+
:scm {:tag tag}
67+
:basis basis
68+
:src-dirs src-dirs})
69+
(println "Copying src...")
70+
(b/copy-dir {:src-dirs src-dirs
71+
:target-dir class-dir})
72+
(println (str "Building jar " jar-file "..."))
73+
(b/jar {:class-dir class-dir
74+
:jar-file jar-file}))
75+
opts)
76+
77+
(defn deploy
78+
"Deploy the JAR to Clojars.
79+
80+
Requires: lib, version"
81+
[{:keys [target class-dir lib version jar-file] :as opts}]
82+
(assert (and lib version) "lib and version are required for deploy")
83+
(let [target (or target default-target)
84+
class-dir (or class-dir (default-class-dir target))
85+
jar-file (or jar-file (default-jar-file target lib version))]
86+
(dd/deploy (merge {:installer :remote :artifact jar-file
87+
:pom-file (b/pom-path {:lib lib :class-dir class-dir})}
88+
opts)))
89+
opts)
90+
91+
(defn run-task
92+
"Run a task based on aliases.
93+
94+
If :main-args is not provided and not :main-opts are found
95+
in the aliases, default to the Cognitect Labs' test-runner."
96+
[{:keys [java-opts jvm-opts main main-args main-opts] :as opts} aliases]
97+
(println "\nRunning task for:" aliases)
98+
(let [basis (b/create-basis {:aliases aliases})
99+
combined (t/combine-aliases basis aliases)
100+
cmds (b/java-command
101+
{:basis basis
102+
:java-opts (into (or java-opts (:jvm-opts combined))
103+
jvm-opts)
104+
:main (or 'clojure.main main)
105+
:main-args (into (or main-args
106+
(:main-opts combined)
107+
["-m" "cognitect.test-runner"])
108+
main-opts)})
109+
{:keys [exit]} (b/process cmds)]
110+
(when-not (zero? exit)
111+
(throw (ex-info (str "Task failed for: " aliases) {}))))
112+
opts)
113+
114+
(defn run-tests
115+
"Run tests.
116+
117+
Always adds :test to the aliases."
118+
[{:keys [aliases] :as opts}]
119+
(-> opts (run-task (into [:test] aliases))))

0 commit comments

Comments
 (0)