Skip to content

Commit

Permalink
project: tools.build updated approach and configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
practicalli-johnny committed Oct 19, 2023
1 parent 71e1b43 commit f52d098
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 50 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
265 changes: 215 additions & 50 deletions docs/clojure-cli/projects/package/tools-build.md
Original file line number Diff line number Diff line change
@@ -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:[email protected]: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

Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit f52d098

Please sign in to comment.