diff --git a/CHANGELOG.md b/CHANGELOG.md index 3210da890..51369abc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ## Changed - mkdocs: emoji extension name update for Material 9.4 +- project: update built.tools approach and configuration examples # 2023-08-14 diff --git a/docs/clojure-cli/projects/package/tools-build.md b/docs/clojure-cli/projects/package/tools-build.md index ba537f71c..64c588e07 100644 --- a/docs/clojure-cli/projects/package/tools-build.md +++ b/docs/clojure-cli/projects/package/tools-build.md @@ -1,91 +1,256 @@ -# Clojure tools.build +# Package projects with tools.build -[:globe_with_meridians: Clojure.org tools.build](https://clojure.org/guides/tools_build) is a library to define build related tasks using Clojure code. +!!! WARNING "Page being updated with enhanced examples" -[:globe_with_meridians: tools.build API](https://clojure.github.io/tools.build/) provides a consistent interface to access the project configuration (project basis) and common tasks that facilitate building and packaging projects. +[:globe_with_meridians: Clojure.org tools.build](https://clojure.org/guides/tools_build) is a library to define build related tasks using Clojure code. -The following files are created in each project that uses tools.build: +The [:globe_with_meridians: tools.build API](https://clojure.github.io/tools.build/) provides a consistent interface to access the project configuration (project basis) and common tasks that facilitate building and packaging projects. -* `build.clj` contains a namespace with tasks -* `:project/build` alias containing tools.build library and sets the default namespace +Include a **build alias** and **build script** in each project to make use of Clojure tools.build: -`clojure -T:build task-name` to run any of the tasks defined in the default `build` namespaces. +- `: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 ??? HINT "Practicalli Project Templates include tools.build tasks" - [:fontawesome-solid-book-open: Practicalli Project templates](/clojure/clojure-cli/projects/templates/) include a `build.clj` configuration with `jar` and `uberjar` tasks. + [:fontawesome-solid-book-open: Practicalli Project templates](/clojure/clojure-cli/projects/templates/) include a `build.clj` tasks to generate a library `jar` or a service `uberjar`. -## Define a build alias +## Define build alias Add an alias to the project `deps.edn` file which includes the `org.clojure/tools.build` project. -!!! EXAMPLE "Alias to include tools.build library" +!!! EXAMPLE ":build/task alias created by Practicalli Project Templates" ```clojure title="Project deps.edn" - :project/build + ;; tools.build `build.clj` built script + :build/task {:replace-paths ["."] :replace-deps {io.github.clojure/tools.build - {:git/tag "v0.9.4" :git/sha "76b78fe"}} + {: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. + +```shell +clojure -T:build/task task-name +``` + +??? INFO "tools.build release information" + [Clojure.org tools.build release information](:fontawesome-brands-github: https://github.com/clojure/tools.build#release-information) shows the current values for `git/tag` and `:git/sha` + + ??? INFO "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. ```shell - clojure -M:project/build:repl/rebel + clojure -M:build/task:repl/rebel ``` -??? INFO "tools.build release information" - [Clojure.org tools.build release information](:fontawesome-brands-github: https://github.com/clojure/tools.build#release-information) shows the current values for `git/tag` and `:git/sha` - ## Build Script -Create a `build.clj` file to contain the build configuration and tasks. +Create a `build.clj` file which defines a namespace requiring tools.build, a project configuration and functions as build tasks -Define the namespace and require the clojure.tools.build.api library +An **Uberjar** file is built to deploy a Clojure service, e.g. in test, staging or production environment. -```clojure title="build.clj" -(ns build - (:require [clojure.tools.build.api :as build-api])) -``` +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. -Define a configuration for the build with values used in the build tasks. - -```clojure title="build.clj" -;; --------------------------------------------------------- -;; Build configuration - -(def project-config - "Project configuration to support all tasks" - (let [library-name 'practicalli/clojure-app-template - version (format "1.0.%s" (build-api/git-count-revs nil))] - {:library-name library-name - :main-namespace library-name - :project-version version - :class-directory "target/classes" - :project-basis (build-api/create-basis) - :jar-file (format "target/%s-%s.jar" (name library-name) version) - :uberjar-file (format "target/%s-%s-standalone.jar" (name library-name) version)})) - -;; End of Build configuration -;; --------------------------------------------------------- -``` +### Namespace definition + +Define the namespace and require the clojure.tools.build.api and any additional libraries. + +=== "Service Uberjar" + + !!! EXAMPLE "Namespace definition with tools.build.api and Pretty Print" + ```clojure title="build.clj" + (ns build + (:require + [clojure.tools.build.api :as build-api] + [clojure.pprint :as pprint])) + ``` -Define functions to support common tasks `clean`, `jar`, `uberjar` +=== "Library Jar" -!!! INFO "Functions are passed command line arguments" - Function definitions should accept an argument as they are sent command line options when called via the `clojure -T:build` command + !!! EXAMPLE "Namespace definition with tools.build.api and Pretty Print" + ```clojure title="build.clj" + (ns build + (:require + [clojure.tools.build.api :as build] + [deps-deploy.deps-deploy :as deploy] + [clojure.pprint :as pprint])) + ``` - If arguments are not given on the command line, a `nil` value is passed to the called function +### Build configuration - `_` name convention is used when a function definition does not make use of the argument that is passed +Define a hash-map containing keys and values required to build the project. +=== "Service Uberjar" + 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. -!!! EXAMPLE "tools.build tasks configuration" ```clojure title="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 + ;; --------------------------------------------------------- + ``` + + +=== "Library Jar" + Define a project configuration for building a jar file for deployment on Clojars and Maven Central, or a private repository. + + ```clojure title="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 + ;; --------------------------------------------------------- + ``` + +### Clean Task + + +??? EXAMPLE "tools.build uberjar task configuration" + ```clojure title="build.clj" + ;; --------------------------------------------------------- + ;; Build Script + ;; + ;; Build project and package for deployment + ;; - `uberjar` - packaged application for deployment + ;; - `clean` remove all build assets and jar files + ;; + ;; All functions are passed command line arguments + ;; - `nil` is passed if there are no arguments + ;; + ;; + ;; tools.build API commands + ;; - `create-basis` create a project basis + ;; - `copy-dir` copy Clojure source and resources into a working dir + ;; - `compile-clj` compile Clojure source code to classes + ;; - `delete` - remove path from file space + ;; - `write-pom` - write pom.xml and pom.properties files + ;; - `jar` - to jar up the working dir into a jar file + ;; + ;; --------------------------------------------------------- + + (ns build + (:require + [clojure.tools.build.api :as build-api] + [clojure.pprint :as pprint])) + + ;; --------------------------------------------------------- + ;; Project configuration + + (def project-config + "Project configuration to support all tasks" + {:class-directory "target/classes" + :main-namespace 'practicalli/bob-the-builder + :project-basis (build-api/create-basis) + :uberjar-file "target/practicalli-service-name-standalone.jar"}) + + (defn config + "Display build configuration" + [config] + (pprint/pprint (or config project-config))) + + ;; End of Build configuration + ;; --------------------------------------------------------- + + ;; --------------------------------------------------------- + ;; 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 + ;; --------------------------------------------------------- + + + +??? EXAMPLE "tools.build jar task configuration" ;; --------------------------------------------------------- ;; Build tasks @@ -136,7 +301,7 @@ Define functions to support common tasks `clean`, `jar`, `uberjar` ;; --------------------------------------------------------- ``` -!!! EXAMPLE "Project configuration" +??? EXAMPLE "Project configuration" Pretty printed Example of a project configuration ```clojure {:library-name practicalli/clojure-app-template,