Skip to content

Package projects with tools.buildλ︎

Improved build script examples have been added

Please report any issues using the new examples

🌐 Clojure.org tools.build is a library to define build related tasks using Clojure code.

The 🌐 tools.build API provides a consistent interface to access the project configuration (project basis) and common tasks that facilitate building and packaging projects.

Include a build alias and build script in each project to make use of Clojure tools.build:

  • :build/task alias adding tools.build library to the class path in the project deps.edn file
  • build.clj defines a namespace requiring tools.build, a project configuration and functions as build tasks
Practicalli Project Templates include tools.build tasks

Practicalli Project templates include a build.clj tasks to generate a library jar or a service uberjar.

Define build aliasλ︎

Add an alias to the project deps.edn file which includes the org.clojure/tools.build project.

:build/task alias created by Practicalli Project Templates

Project deps.edn
  ;; tools.build `build.clj` built script
  :build/task
  {:replace-paths ["."]
   :replace-deps {io.github.clojure/tools.build
                  {:git/tag "v0.9.6" :git/sha "8e78bcc"}}
   :ns-default build}

Use Clojure CLI to run any of the tasks defined in the build namespaces.

clojure -T:build/task task-name
tools.build release information

Clojure.org tools.build release information shows the current values for git/tag and :git/sha

Developing code in the build script

:replace-paths ["."] includes the build.clj file on the class path to allow for REPL development of the build tasks

Include :build alias in the Clojure command when starting the REPL.

clojure -M:build/task:repl/rebel

Build Scriptλ︎

Create a build.clj file which defines a namespace requiring tools.build, a project configuration and functions as build tasks

An Uberjar file is built to deploy a Clojure service, e.g. in test, staging or production environment.

A Jar file is built to published a Clojure library to a Maven repository, e.g. Clojars.org, Maven Central or a private Maven repository.

Namespace definitionλ︎

Define the namespace and require the clojure.tools.build.api and any additional libraries.

Namespace definition with tools.build.api and Pretty Print

build.clj
(ns build
  (:require
   [clojure.tools.build.api :as build-api]
   [clojure.pprint :as pprint]))

Namespace definition with tools.build.api and Pretty Print

build.clj
(ns build
  (:require
   [clojure.tools.build.api :as build-api]
   [deps-deploy.deps-deploy :as deploy-api]
   [clojure.pprint :as pprint]))

Build configurationλ︎

Define a hash-map containing keys and values required to build the project.

Define a project configuration for building an Uberjar file to run a service using the java -jar command.

The Uberjar can be deployed to run the service in test, staging and production environments.

Clojure Service build tasks

build.clj
;; ---------------------------------------------------------
;; Project configuration

(def project-config
  "Project configuration to support build tasks"
  {:class-directory "target/classes"
   :main-namespace  'practicalli/project-name/service
   :project-basis   (build-api/create-basis)
   :uberjar-file    "target/practicalli-servicename-standalone.jar"})

(defn config
  "Display build configuration"
  [config]
  (pprint/pprint (or config project-config)))

;; End of Build configuration
;; ---------------------------------------------------------

Define a project configuration for building a jar file for deployment on Clojars and Maven Central, or a private repository.

  • pom-template is the standard structure for generating a pom.xml file, required by Maven repositories, i.e. Clojars.org and Maven Central
  • project-config specific values for building the project, e.g. name, version, etc.
  • config function to pretty print the build configuration

Clojure Library build tasks

build.clj
;; ---------------------------------------------------------
;; Build configuration

(defn- pom-template
  "Standard structure for a `pom.xml` file, a Maven project configuration 
  required to deploy libraries to Clojars.org, Maven Central or private Maven repositories
  https://maven.apache.org/guides/introduction/introduction-to-the-pom.html"
  [project-version]
  [[:description "FIXME: add purpose of library."]
   [:url "https://github.com/organisation/project-name"]
   [:licenses
    [:license
     [:name "Creative Commons Attribution-ShareAlike 4.0 International"]
     [:url "https://creativecommons.org/licenses/by-sa/4.0/"]]]
   [:developers
    [:developer
     [:name "Organisation name"]]]
   [:scm
    [:url "https://github.com/organisation/project-name"]
    [:connection "scm:git:https://github.com/organisation/project-name.git"]
    [:developerConnection "scm:git:ssh:git@github.com:organisation/project-name.git"]
    [:tag (str "v" project-version)]]])


(def project-config
  "Project configuration to support build tasks"
  (let [library-name 'net.clojars.organisation/project-name
        version "0.1.0-SNAPSHOT"]
    {:library-name     library-name
     :project-version  version
     :jar-file         (format "target/%s-%s.jar" (name library-name) version)
     :project-basis    (build-api/create-basis)
     :class-directory  "target/classes"
     :src-directory    ["src"]
     :target-directory "target"
     :pom-config       (pom-template version)}))


(defn config
  "Display build configuration"
  [config]
  (pprint/pprint (or config project-config)))
;; End of Build configuration
;; ---------------------------------------------------------

Build Taskλ︎

Define Clojure functions to run the required build tasks

  • clean to remove build artefacts, e.g. target directory
  • Uberjar creates a Jar file for a Clojure library, ready for publishing

Clojure Service build tasks

build.clj
;; ---------------------------------------------------------
;; Build tasks

(defn clean
  "Remove a directory
  - `:path '\"directory-name\"'` for a specific directory
  - `nil` (or no command line arguments) to delete `target` directory
  `target` is the default directory for build artefacts
  Checks that `.` and `/` directories are not deleted"
  [directory]
  (when
   (not (contains? #{"." "/"} directory))
   (build-api/delete {:path (or (:path directory) "target")})))


(defn uberjar
  "Create an archive containing Clojure and the build of the project
  Merge command line configuration to the default project config"
  [options]
  (let [config (merge project-config options)
        {:keys [class-directory main-namespace project-basis uberjar-file]} config]
    (clean "target")
    (build-api/copy-dir {:src-dirs   ["src" "resources"]
                         :target-dir class-directory})

    (build-api/compile-clj {:basis     project-basis
                            :class-dir class-directory
                            :src-dirs  ["src"]})

    (build-api/uber {:basis     project-basis
                     :class-dir class-directory
                     :main      main-namespace
                     :uber-file uberjar-file})))

;; End of Build tasks
;; ---------------------------------------------------------

Define Clojure functions to run the required build tasks

  • clean to remove build artefacts, e.g. target directory
  • jar creates a Jar file for a Clojure library, ready for publishing
  • install a built jar into the local Maven repository, e.g. `~/.m2/repository/
  • publish a built jar to Clojars.org

Clojure Library build tasks

build.clj
;; ---------------------------------------------------------
;; Build tasks

(defn clean
  "Remove a directory
  - `:path '\"directory-name\"'` for a specific directory
  - `nil` (or no command line arguments) to delete `target` directory
  `target` is the default directory for build artefacts
  Checks that `.` and `/` directories are not deleted"
  [directory]
  (when (not (contains? #{"." "/"} directory))
    (build-api/delete {:path (or (:path directory) "target")})))

(defn jar "Run the CI pipeline of tests (and build the JAR)."
  [config]
  (clean "target")
  (let [config (project-config config)
        class-directory (config :class-directory)]
    (println "\nWriting pom.xml...")
    (build-api/write-pom (merge (pom-template config)))
    (println "\nCopying source...")
    (build-api/copy-dir {:src-directory ["resources" "src"] :target-directory class-directory})
    (println "\nBuilding JAR..." (:jar-file config))
    (build-api/jar config))
  config)

(defn install
  "Install a built JAR in the local Maven repository, e.g. `.m2/repository`"
  [config]
  (let [config (project-config config)]
    (build-api/install config))
  config)

(defn publish 
  "Publish the built JAR to Clojars." 
  [config]
  (let [{:keys [jar-file] :as config} (project-config config)]
    (deploy-api/deploy
     {:installer :remote :artifact (build-api/resolve-path jar-file)
      :pom-file (build-api/pom-path (select-keys config [:library-name :class-directory]))}))
  config)

;; End of Build tasks
;; ---------------------------------------------------------

Resourcesλ︎

🌐 Clojure.org tools.build Guide

🌐 Clojure.org tools.build API Docs

🌐 Clojure.org tools.build release information