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

seancorfield/build-clj

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

build-clj

Common tools.build tasks abstracted into a library, building on the examples in the official tools.build guide.

Having implemented build.clj (using tools.build) in several of my open source projects I found there was a lot of repetition across them, so I factored out the common functionality into this library.

Since it depends on both tools.build and Erik Assum's deps-deploy, your :build alias can just be:

  :build {:deps {io.github.seancorfield/build-clj
                 {:git/tag "v0.3.1" :git/sha "996ddfa"}}
          :ns-default build}

Your build.clj can start off as follows:

(ns build
  (:require [clojure.tools.build.api :as b]
            [org.corfield.build :as bb]))

(def lib 'myname/mylib)
;; if you want a version of MAJOR.MINOR.COMMITS:
(def version (format "1.0.%s" (b/git-count-revs nil)))

Tasks Provided

The following common build tasks are provided, all taking an options hash map as the single argument and returning that hash map unchanged so you can reliably thread the build tasks. [Several functions in clojure.tools.build.api return nil instead]

  • clean -- clean the target directory (wraps delete from tools.build),
  • deploy -- deploy to Clojars (wraps deploy from deps-deploy),
  • install -- install the JAR locally (wraps create-basis and install from tools.build),
  • jar -- build the (library) JAR and pom.xml files (wraps create-basis, write-pom, copy-dir, and jar from tools.build),
  • uber -- build the (application) uber JAR, with optional pom.xml file creation and/or AOT compilation (wraps create-basis, write-pom, copy-dir, compile-clj, and uber from tools.build),
  • run-tests -- run the project's tests (wraps create-basis, java-command, and process from tools.build, to run the :main-opts in your :test alias).

For deploy, install, and jar, you must provide at least :lib and :version. For uber, you must provide at least :lib or :uber-file for the name of the JAR file. Everything else has "sane" defaults, but can be overridden.

Typical build.clj with build-clj

You might typically have the following tasks in your build.clj:

(defn ci "Run the CI pipeline of tests (and build the JAR)." [opts]
  (-> opts
      (assoc :lib lib :version version)
      (bb/run-tests)
      (bb/clean)
      (bb/jar)))

(defn install "Install the JAR locally." [opts]
  (-> opts
      (assoc :lib lib :version version)
      (bb/install)))

(defn deploy "Deploy the JAR to Clojars." [opts]
  (-> opts
      (assoc :lib lib :version version)
      (bb/deploy)))

Or if you are working with an application, you might have:

(defn ci "Run the CI pipeline of tests (and build the uberjar)." [opts]
  (-> opts
      (assoc :lib lib :main main)
      (bb/run-tests)
      (bb/clean)
      (bb/uber)))

Note: this uber task in build-clj supplies the log4j2 conflict handler to the underlying uber task of tools.build so that you don't have to worry about the plugins cache files being merged.

Running Tests

If you want a run-tests task in your build.clj, independent of the ci task shown above, the following can be added:

(defn run-tests "Run the tests." [opts]
  (-> opts (bb/run-tests)))

By default, the run-tests task will run whatever is in your :test alias but if there is no :main-opts, it assumes Cognitect's test-runner:

  :test
  {:extra-paths ["test"]
   :extra-deps {org.clojure/test.check {:mvn/version "1.1.0"}
                io.github.cognitect-labs/test-runner
                {:git/tag "v0.5.0" :git/sha "48c3c67"}}
   :exec-fn cognitect.test-runner.api/test}

The above alias allows for tests to be run directly via:

clojure -X:test

The run-tests task above would run the tests as if the :test alias also contained:

   :main-opts ["-m" "cognitect.test-runner"]

If you want to use a different test runner with build-clj, just provide different dependencies and supply :main-opts:

  ;; a :test alias that specifies the kaocha runner:
  :test
  {:extra-paths ["test"]
   :extra-deps {lambdaisland/kaocha {:mvn/version "1.0.887"}}
   :main-opts ["-m" "kaocha.runner"]}

With this :test alias, the run-tests task above would run your tests using Kaocha.

Running Additional Programs

In addition, there is a run-task function that takes an options hash map and a vector of aliases. This runs an arbitrary Clojure main function, determined by those aliases, in a subprocess. run-tests uses this by adding a :test alias and in the absence of any :main-opts behind those aliases, assumes it should run cognitect.test-runner's -main function.

run-task picks up :jvm-opts and :main-opts from the specified aliases and uses them as the :java-args and :main-args respectively in a call to clojure.tools.build.api/java-command to build the java command to run. By default, it runs clojure.main's -main function with the specified :main-args.

For example, if your deps.edn contains the following alias:

  :eastwood {:extra-deps {jonase/eastwood {:mvn/version "0.5.1"}}
             :main-opts ["-m" "eastwood.lint" "{:source-paths,[\"src\"]}"]}

Then you can define an eastwood task in your build.clj file:

(defn eastwood "Run Eastwood." [opts]
  (-> opts (bb/run-task [:eastwood])))

Or you could just make it part of your ci pipeline without adding that function:

(defn ci "Run the CI pipeline of tests (and build the JAR)." [opts]
  (-> opts
      (assoc :lib lib :version version)
      (bb/run-task [:eastwood])
      (bb/run-tests)
      (bb/clean)
      (bb/jar)))

Defaults

The following defaults are provided:

  • :target -- "target",
  • :basis -- (b/create-basis {}) -- this is a reproducible basis, i.e., it ignores the user deps.edn file -- if you want your user deps.edn included, you will need to explicitly pass :basis (b/create-basis {:user :standard}) into tasks,
  • :class-dir -- (str target "/classes"),
  • :jar-file -- (format \"%s/%s-%s.jar\" target lib version),
  • :uber-file -- (format \"%s/%s-%s.jar\" target lib version) if :version is provided, else (format \"%s/%s-standalone.jar\" target lib).

For the functions defined in org.corfield.build, you can override the high-level defaults as follows:

  • clean
    • :target,
  • deploy
    • Requires: :lib and :version,
    • :target, :class-dir, :jar-file,
  • install
    • Requires: :lib and :version,
    • :target, :class-dir, :basis, :jar-file,
  • jar
    • Requires: :lib and :version,
    • :target, :class-dir, :basis, :resource-dirs, :scm, :src-dirs, :tag (defaults to (str "v" version)), :jar-file,
  • uber
    • Requires: :lib or :uber-file,
    • :target, :class-dir, :basis, :compile-opts, :main, :ns-compile, :resource-dirs, :scm, :src-dirs, :tag (defaults to (str "v" version) if :version provided), :version
  • run-tests
    • :aliases -- for any additional aliases beyond :test which is always added,
    • Also accepts any options that run-task accepts.

See the docstrings of those task functions for more detail on which options they can also accept and which additional defaults they offer.

As noted above, run-task takes an options hash map and a vector of aliases. The following options can be provided to run-task to override the default behavior:

  • :java-opts -- used instead of :jvm-opts from the aliases,
  • :jvm-opts -- used in addition to the :java-opts vector or in addition to :jvm-opts from the aliases,
  • :main -- used instead of 'clojure.main when building the java command to run,
  • :main-args -- used instead of :main-opts from the aliases,
  • :main-opts -- used in addition to the :main-args vector or in addition to :main-opts from the aliases.

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).

Projects Using build-clj

You can see how build-clj is used to reduce boilerplate in the build.clj file of the following projects:

License

Copyright © 2021 Sean Corfield

Distributed under the Apache Software License version 2.0.