Clojure

Deps and CLI Reference

Rationale

Clojure "endeavors to be a general-purpose language suitable in those areas where Java is suitable" (from Rationale). To effectively target the JVM platform, Clojure needs to provide ready access to Java libraries, ideally in a way suited for dynamic development. In practice, this means meeting the JVM platform in two places:

  • the classpath used when invoking JVM processes (and/or URLClassLoaders)

  • transitive dependency download and resolution from providers like Maven

Clojure build tools have traditionally taken the approach of wrapping the Maven ecosystem to gain access to Java libraries. However, they have also forced this approach on Clojure code as well, requiring a focus on artifacts that must be built and deployed (which Clojure does not require). This approach has created friction for Clojure developers, making it hard to e.g. work with libs not yet publishing artifacts, work on speculative changes w/o artifacts or across multiple libs, or give control to a 3rd party to manage shared dependencies.

To this end Clojure provides:

  • tools.deps - a library providing an API for resolving dependency graphs and building classpaths that can utilize both Maven and other providers of code or artifacts

  • Clojure CLI (Command Line Interface) - clojure and clj allow users at the terminal to declare dependencies, assemble classpaths, and launch Clojure programs with data

  • System-specific installers for downloading the Clojure CLI, improving the "Getting Started" experience

In addition to this reference, also see:

Overview

You use the Clojure CLI (clj or clojure) to run Clojure programs on the JVM, e.g. to start a REPL or invoke a specific function with data. The Clojure CLI will configure the JVM process by defining a classpath (with desired libraries), an execution environment (JVM options), the main class, and args.

Using a deps.edn file (or files), you tell Clojure where your source code resides and what libraries you need. Clojure will then calculate the full set of required libraries and a classpath, caching expensive parts of this process for better performance.

The internal steps of the Clojure CLI, as well as the Clojure functions you intend to run, are parameterized by data structures, often maps. Shell command lines are not optimized for passing nested data, so instead you will put the data structures in your deps edn file and refer to them on the command line via aliases - keywords that name data structures.

clj and clojure differ in that clj has extra support for use as a REPL in a terminal, and should be preferred unless you don’t want that support, then use clojure. The invocation structure is the same for both:

clj     [clj-opt*] [exec-opt]
clojure [clj-opt*] [exec-opt]

Start a REPL

To run a REPL, invoke clj without any exec-opt:

$ clj
Clojure 1.10.3
user=>

Execute a function

Use the following exec-opt to invoke a function that takes a map:

clojure [clj-opt*] -X[aliases] [a/fn] [kpath v]* kv-map?

-X is configured with an arg map with :exec-fn and :exec-args keys, and stored under an alias in deps.edn:

;; deps.edn
{:aliases
 {:my-fn
  {:exec-fn my.qualified/fn
   :exec-args {:my {:data 123}
               :config 456}}}}

To invoke, pass the name of the alias to -X:

clj -X:my-fn

You can supply additional keys, or override values stored in the deps.edn file by passing pairs of key-path and value. The key-path should either be a single key or a vector of keys to refer to a nested key (as with assoc-in). Each key-path will be used to assoc-in to the original :exec-args map, overriding the value there.

# Top key override
clj -X:my-fn :config 789

# Nested key override
clj -X:my-fn '[:my :data]' 789

See the later section on "Quoting keys and values" for more details on how to properly quote edn values on the command line.

If you want to execute an arbitrary function, you can pass it on the command instead, along with pairs of key-path and value:

clj -X my.qualified/fn :config 789

Alias arg maps can also include a default namespace or namespace aliases for resolving the function (see "Namespace resolution" for more details).

Running a main or script

You can use the -M exec-opt to invoke clojure.main, which supports calling a namespace with a -main function or a Clojure script:

clojure [clj-opt*] -M[aliases] [main-opts]

The -M aliases are pulled from deps.edn and combined to form an arg map. The arg map can modify the classpath or supply main options.

See the clojure.main documentation for more details on main options.

Running a tool

Use the -T exec-opt to invoke a tool that does not use the project classpath:

clojure [clj-opt*] -T[name|aliases] a/fn [kpath v]* kv-map?

When running a tool, the project’s :deps and :paths are not used. The function is invoked with a map in the same way as -X execution, built from :exec-args if found in aliases and key/vals provided on the command line.

Tools are installed and referenced by a simpler local name, instead of via an alias. See tool installation for more details.

Operation

When you execute the Clojure CLI, there are a series of steps performed to prepare and execute your command. This section is an overview of those steps, see later sections for how to modify those steps in a variety of ways.

Locate deps edn sources

Configuration is stored in one or more "deps edn" maps. These are edn maps with the following primary top-level keys:

  • :deps - map of lib (symbol) to coordinate

  • :paths - vector of project source paths

  • :aliases - map of alias name to alias data

The Clojure CLI looks for 4 potential deps edn sources:

  • Root - part of the clj installation (a resource in the tools.deps library)

  • User - cross-project configuration (typically tools), usually found at ~/.clojure/deps.edn

  • Project - the deps.edn in the current directory

  • External - a deps edn map passed on the command line

Check cache

The next several steps of this execution can be skipped if we have already computed the classpath and cached it. Classpath and the runtime basis files are cached in the current directory under .cpcache/. The key is based on the contents of the deps.edn files and some of the command arguments passed and several files will be cached, most importantly the classpath and runtime basis.

If the key is found, and the cached files are newer than all deps.edn sources, they will be used and execution begins immediately.

Replace project environment ("tool")

When you execute a tool in the context of your project, the tool can use its own paths and/or deps in place of the project’s paths and/or deps.

Merge sources

All deps edn sources are merged into a single master edn file in the order listed above - root, user, project (possibly with tool replacements), external. This merged deps will be included in the runtime basis and used after this point.

The merge is essentially merge-with merge, except for the :paths key, where only the last :paths found is used (they replace, not combine).

Resolve dependencies

Starting from the master edn’s merged :deps, the full transitive expansion of the dependency tree is computed. Dependency sources (procurers) are used to obtain metadata and other dependency information. At the completion of this step, all libraries to use in the execution have been found and downloaded to local files if needed.

See dep expansion for more details on the dep expansion algorithm.

Prep libs

Libs in the transitive expansion will be checked for whether they are source libs that require prepping. If so, and prep is requested, prep the libs. If unprepped libs remain, throw and stop.

Make classpath

JVM execution relies on computing the classpath for the execution. The classpath will consist of the :paths in the merged deps.edn and the locations of all resolved dependencies.

Prepare JVM environment

JVM arguments may either be passed on the command line (with -J) or by using data stored in an alias under :jvm-opts and passed with -X or -A or -M

Execute command

Finally the command is executed - see the Overview above for the execution options. The already computed (or loaded from cache) classpath, JVM environment, and main arguments if relevant are used in the execution.

deps.edn

The configuration file format (in deps.edn files) is an edn map with top-level keys :deps, :paths, and :aliases, plus provider-specific keys for configuring dependency sources.

Paths

Paths are declared in a top level key :paths and are a vector of string paths or alias names. Relative paths are resolved in relation to the directory containing the deps.edn (the project root). These source paths will be added to the classpath. Source paths should be at or under the project root directory (where deps.edn is located).

Example:

{:paths ["src"]}

If used, alias names should refer to a path vector in the alias data:

{:paths [:clj-paths :resource-paths]
 :aliases
 {:clj-paths ["src/clj" "src/cljc"]
  :resource-paths ["resources"]}}

Dependencies

Dependencies are declared in deps.edn with a top level key :deps - a map from library to coordinate. Libraries are symbols of the form <groupID>/<artifactId>. To indicate a Maven classifier, use <groupId>/<artifactId>$<classifier>.

Coordinates can take several forms depending on the coordinate type:

  • Maven coordinate attributes:

    • :mvn/version (required) - string of Maven version

    • Note: :classifier is not supported - add as part of lib name as specified above in Maven docs

  • Local project attributes:

    • :local/root (required) - directory path (will auto detect whether a deps or pom project, also see :deps/manifest)

  • Local jar attributes:

    • :local/root (required) - path to jar file

    • If the jar includes a pom.xml file, it will be used to find transitive deps

  • Git coordinate:

    • :git/url (optional if inferred from lib name, otherwise required, see Git config section)

    • :git/tag (optional) indicates the semantics of the sha (:tag also supported for backwards compatibility), must refer to same :git/sha

    • :git/sha (required) if tag provided, can be prefix sha, otherwise should be full sha (:sha also supported for backwards compatibility)

  • Common coordinate attributes (all optional):

    • :exclusions - vector of lib symbols that are excluded as transitive deps from this coord

    • :deps/manifest - specifies the project manifest type and overrides auto detection, useful values are :deps, :pom. (Other values are :mvn, and :jar but these don’t need to be specified.)

    • :deps/root - relative directory path within a file based dep to search for the manifest file

{:deps
 {;; example maven dep
  org.clojure/tools.reader {:mvn/version "1.1.1"}

  ;; example git dep
  io.github.sally/awesome {:tag "v1.2.3" :sha "123abcd"}

  ;; example local project
  my.dev/project {:local/root "../project"}

  ;; example local jar
  my.driver/jar {:local/root "../libs/driver.jar"}
 }}

Aliases

Aliases give a name to a data structure that can be used either by the Clojure CLI itself or other consumers of deps.edn. They are defined in the :aliases section of the config file. These Clojure CLI subprocesses use data which can be read from aliases:

Other keys

Some other top-level keys you may encounter:

  • :mvn/repos - a map of Maven repository names to maps of repo info (like :url)

  • :mvn/local-repo - a string path to a directory to use as the local Maven cache

  • :tools/usage - exec argmap for a deps.edn-based tool

  • :deps/prep-lib - a map describing how this source-based lib should be prepped

Basis and classpath

The core of the tools.deps library is a process to take a merged deps.edn file, arg maps for the resolve-deps and make-classpath-map steps, and produce the "runtime basis", or "basis" for short. The basis is a superset of the deps.edn file also containing those args, the lib map, and the classpath map.

The JVM classpath consists of a series of roots, either directory paths or the path to a jar file. Classes (and Clojure files) map via package or namespace to a path relative to a classpath root. For example, the java.lang.String class can be found at path java/lang/String.class and the clojure.set Clojure namespace may be found at paths clojure/set.class (for AOT), clojure/set.clj, or clojure/set.cljc. When the JVM needs to load one of these files it searches each root for the relative path and loads it when found.

We divide the process of building a classpath into two primary operations: resolve-deps and make-classpath-map.

resolve-deps

(resolve-deps deps args-map)

resolve-deps takes an initial map of required dependencies and a map of args that modify the resolution process. It builds a full graph of transitive dependencies, resolves any version differences, and flattens that graph to a full list of libraries to be included on the classpath.

The deps from the merged deps.edn is a map of library to coordinate. The library is (in Maven terms) the groupId and artifactId, which are sufficient to locate the desired project. The coordinate is used to describe a particular version that is being requested from a particular provider (like Maven or git).

For example, this deps map specifies a (Maven-based) dependency:

{org.clojure/core.cache {:mvn/version "0.6.5"}}

resolve-deps expands these dependencies to include all transitive dependencies, cut cycles, resolve version differences, download required artifacts from the provider, and produce a lib map of the flattened set of all needed dependencies and where to find their artifacts:

{org.clojure/core.cache        {:mvn/version "0.6.5",
                                :deps/manifest :mvn,
                                :paths [".../core.cache-0.6.5.jar"]}
 org.clojure/data.priority-map {:mvn/version "0.0.7",
                                :deps/manifest :mvn,
                                :dependents [org.clojure/core.cache],
                                :paths [".../data.priority-map-0.0.7.jar"]}
 ... }

The lib map lists all libraries, their selected coordinates, the :paths on disk, and a list of dependents that caused it to be included. Here you can see that data.priority-map was included as a dependency of core.cache.

The second args-map is a map of optional modifications to the standard expansion to account for common use cases: adding extra dependencies, overriding deps, and default deps. These can be used separately or together, or not at all:

{:extra-deps { ... }
 :override-deps { ... }
 :default-deps { ... }}

:extra-deps is the most common modification - it allows you to optionally add extra dependencies to the base dependency set. The value is a map of library to coordinate:

{:extra-deps {criterium/criterium {:mvn/version "0.4.4"}}}

:override-deps overrides the coordinate version chosen by the version resolution to force a particular version instead. This also takes a map of library to coordinate:

{:override-deps {org.clojure/clojure {:mvn/version "1.9.0"}}}

:default-deps provides a set of default coordinate versions to use if no coordinate is specified. The default deps can be used across a set of shared projects to act as a dependency management system:

{:default-deps {org.clojure/core.cache {:mvn/version "0.6.4"}}}

make-classpath-map

(make-classpath-map lib-map paths args-map)

The make-classpath-map step takes the lib map (the result of resolve-deps), the internal source paths of the project ["src"], an args-map of optional modifications, and produces a classpath string for use in the JVM.

The args-map includes support for modifications to be applied while making the classpath: adding extra paths, and overriding the location of libraries specified in the lib map. These modifications can be used separately or together or not at all in a map like this:

{:extra-paths [ ... ]
 :classpath-overrides { ... }}

:extra-paths is used to include source paths in addition to your standard source paths, for example to include directories of test source:

{:extra-paths ["test" "resources"]}

Note that external paths should be at or under the root directory of the project (location of the deps.edn file).

:classpath-overrides specify a location to pull a dependency that overrides the path found during dependency resolution, for example to replace a dependency with a local debug version. Many of these use cases are ones where you would be tempted to prepend the classpath to "override" something else.

{:classpath-overrides
 {org.clojure/clojure "/my/clojure/target"}}

Classpath ordering

Classpaths will be created in the following order, which is intended to be reproducible:

  • The :extra-paths in the order specified once classpath modifiers are merged and applied

  • The :paths in the order specified once classpath modifiers are merged and applied

  • All dependency and transitive dependency libs, sorted by:

    • Depth from root

    • Alphabetical by group/artifact

Clojure CLI usage

deps.edn sources

The Clojure CLI will use the following deps.edn map sources, in this order:

  • Root - found in the installation of clj (or as a resource in tools.deps)

  • User - cross-project configuration (typically tools)

    • Locations used in this order:

      • If $CLJ_CONFIG is set, then use $CLJ_CONFIG (explicit override)

      • If $XDG_CONFIG_HOME is set, then use $XDG_CONFIG_HOME/clojure (Freedesktop conventions)

      • Else use $HOME/.clojure (most common)

  • Project - the deps.edn in the current directory

  • External - a deps.edn map passed on the command line

The deps.edn sources can be modified or inspected by the following options:

  • -Sverbose - print all source locations

  • -Sdeps - pass the config data on the command line

  • -Srepro - omit the user deps source (other sources will be used if found)

Classpath caching

The cached classpath file is used for execution when:

  • It exists and

  • It is newer than all deps.edn sources

Classpath and basis files are cached in a directory computed as follows:

  • If there is a project deps.edn

    • Use ./.cpcache

  • Else (no project deps.edn)

    • If $CLJ_CACHE is set, then use $CLJ_CACHE (explicit override)

    • If $XDG_CACHE_HOME is set, then use $XDG_CACHE_HOME/clojure (Freedesktop conventions)

    • Else use config_dir/.cpcache (see deps.edn sources for config_dir logic, usually ~/.clojure)

A hash is computed based on the config file paths, the resolve-aliases, and the classpath aliases.

Files cached (if needed):

  • <hash>.cp - a classpath string, the output of make-classpath-map

  • <hash>.jvm - jvm options for main execution

  • <hash>.main - main options for main execution

  • <hash>.basis - the runtime basis, passed to the program

Caching can be modified by the following options:

  • -Sforce - ignore cache and force recomputation

  • -Scp CP - don’t use cache or compute, use the provided classpath instead

Replace project environment (tool)

The tool process is used when executing a tool that should not use the project classpath environment. This process takes the project deps.edn and the following modifier args and replaces them (if provided) before the project deps is merged:

  • :replace-deps - a deps map from lib to coordinate of deps to REPLACE the project :deps

  • :replace-paths a collection of string paths to REPLACE project :paths (note these should be under the project root)

This tool map should be provided as an alias in one of the deps sources and provided to the Clojure CLI using -A (for REPL invocation), -X (for function execution), or -M (for clojure.main execution). Multiple aliases can be provided by concatentating the alias names.

Merging deps.edn

The deps.edn files found from the source locations (with modifications from any options) are merged to form one master deps map. The merge is done in the order above root/user/project/config, last one wins. The operation is essentially merge-with merge, except for the :paths key, where only the last one found is used (they are not combined).

Resolve deps modifiers

The resolve-deps modifiers are provided in a map that was described in a prior section:

  • :extra-deps - a deps map from lib to coordinate of deps to add to the deps

  • :override-deps - a deps map from lib to coordinate of override versions to use

  • :default-deps - a deps map from lib to coordinate of versions to use if none is found

  • If multiple alias maps with these keys are activated, all of them are merge-with merged

The resolve-deps args should be provided as an alias in one of the deps sources and provided to the Clojure CLI using -A (for REPL invocation), -X (for function execution), or -M (for clojure.main execution). Multiple aliases can be provided by concatentating the alias names.

Make classpath modifiers

The make-classpath-map modifiers are provided in a map that was described in a prior section:

  • :extra-paths - a collection of string paths to add to :paths (should be in the project)

  • :classpath-overrides - a map of lib to string path to replace the location of the lib

  • If multiple maps with these keys are activated, :extra-paths concatenate and :classpath-overrides merge-with merge

The resolve-deps args should be provided as an alias in one of deps sources and provided to the Clojure CLI using -A (for REPL invocation), -X (for function execution), or -M (for clojure.main execution). Multiple aliases can be provided by concatentating the alias names.

Prepare JVM environment

The JVM has many options and some programs also take configuration via Java system properties. JVM options can be passed on the command line for any execution specfier by prefixing with -J. If multiple options are specified, each must be prefixed.

Additionally, main execution can take a map with key :jvm-opts:

  • :jvm-opts - a collection of string JVM options

  • If multiple maps with these keys are activated, :jvm-opts concatenate

  • If -J JVM options are also specified on the command line, they are concatenated after the alias options

This map is stored in alias data and provided to the Clojure CLI using -A (for REPL invocation), -X (for function execution), or -M (for clojure.main execution). Multiple aliases can be provided by concatentating the alias names.

If JVM options are provided in the JAVA_OPTS environment variable, these will be included first on the command line (note: these options are not part of the command cache and may vary between executions).

Execution

Main execution

One additional option for main execution is a map with key :main-opts:

  • :main-opts - a collection of clojure.main options

  • If multiple maps with these keys are activated, only the last one will be used

  • If command line clojure.main arguments are supplied on the command line, they are concatenated after the last main alias map

This map is stored in alias data and provided to the Clojure CLI using -M:an-alias. Multiple aliases can be provided by concatentating the alias names.

Prepare for execution

The -P flag can be used with any other execution mode to "prepare" but not execute. Importantly, this flag will cause the Clojure CLI to resolve deps, download libraries, make and cache classpaths. This may be useful in containers, continuous integration systems, etc where an environment can be prepared to execute and do necessary network activity first, and then later execute without that activity.

Other programs

There are some options that can be provided to clj that short circuit normal execution and run an alternate program instead:

  • -Sdescribe - print environment and command parsing info as data

  • -Spath - compute classpath and echo to stdout only

The :deps alias is built into the root deps.edn and provides a default namespace with several additional programs:

  • -X:deps tree - print dependency tree (also see tree printing)

  • -X:deps list - print dependency list and license information

  • -X:deps aliases - print all aliases available in the current deps environment

  • -X:deps mvn-pom - generate (or update an existing) pom.xml with deps and paths

  • -X:deps git-resolve-tags - resolve git coordinate tags to shas and update deps.edn

Help functions

The help/doc and help/dir functions introspect how a tool can be used. Because the :deps alias does not include the project classpath, these are not currently useful when executing functions in your own project.

  • -X:deps help/doc - show the doc strings and parameter lists of the functions given with key :ns or function specified by an additional key :fn; if neither given then :ns-default is used

  • -X:deps help/dir - prints the public functions in namespace provided with key :ns or :ns-default if not given

Example for listing the set of functions in the :ns-default for the built in tools tool:

clojure -A:deps -Ttools help/dir

Example for listing the set of functions in an alias:

clojure -X:deps:my-alias help/dir

Print the docstrings for the help namespace itself (note that help is defined in the :ns-aliases map for :deps):

clojure -X:deps help/doc :ns help

Prep libs

Source libs with Clojure source can immediately be added to the classpath of a project using it. However, some source libs require some preparation before they can be added, for example due to needing Java compilation, or copying / replacing resource files, etc. The Clojure CLI will now detect projects that need preparation and prevent the program from being run from source unless the prep step has been completed.

If your library needs preparation, add the :deps/prep-lib key to your deps.edn:

{:paths ["src" "target/classes"]
 :deps/prep-lib
 {:ensure "target/classes"
  :alias :build
  :fn compile-java}}

The keys under :deps/prep-lib are:

  • :ensure - directory existence determines whether prep is needed (if it exists, prep has been done)

  • :alias - alias to invoke with -T during prep

  • :fn - function to invoke with -T during prep

Thus, the deps.edn should also have an alias that can execute the fn specified. See the tools.build Guide for how to use tools.build to define a build script with an invokable function.

If you add this git library as a dependency and try to run a program, the Clojure CLI will download it, detect that it needs preparation, and that it has not been prepared ("target/classes" path does not exist), and exit.

To find and "prepare" libs in the dependency tree you can use the prep tool provided with the built-in :deps alias:

clj -X:deps prep

The prep task will find all libs in the dependency expansion and look for libs that are source libs, need prep (based on their :deps/prep-lib key), and are not yet prepped (based on the :ensure dir in their :deps/prep-lib map). Those libs will run the command specified there by alias and function, as if: clj -T:<alias> <fn>.

Once a library has been prepped, it does not need to be prepped again by other users of this git library version.

Should you use a prep step that compiles your Clojure code? Generally, no. All users of this git library on a machine will share the prepared classpath created by the prep step. The choice of Clojure compiler and dependent libraries is better left to each application making using of this lib. For more on using dev-time compilation, see the Dev Startup Time guide.

Find versions

To search for available versions of a Maven or git lib use the find-versions tool provided with the built-in :deps alias:

clj -X:deps find-versions ...

The params that can be provided are:

  • :lib - git or Maven lib name. The git url will be automatically constructed from the git library name. For example, a :git/lib of io.github.clojure/tools.deps.graph will construct the git url https://github.com/clojure/tools.deps.graph.git. For Maven, use the Maven lib name like org.clojure/tools.deps.graph.

  • :tool - a tool name if the tool has already been installed

find-versions will print the git or Maven coordinates, one per line, to the console.

Local Maven install

The -X:deps mvn-install program is provided with the Clojure CLI for convenience and can be executed with -X to install a jar into your local Maven cache.

The install params include the following options:

Required:
:jar - path to jar file, use pom inside jar by default

To supply an explicit pom file:
:pom - path to pom file (used instead of pom inside the jar)

To generate a minimal pom file:
:lib - qualified symbol like my.org/lib
:version - string
:classifier - string

Other options:
:local-repo - path to local repo (default = ~/.m2/repository)

You can pass overrides on the command line for these as needed:

clj -X:deps mvn-install :jar '"/path/to.jar"'

As mentioned above, edn strings must be in double quotes, and then single-quoted for the shell.

A pom file must be either provided explicitly, generated from :lib/:version, or found inside the .jar file (the default).

Procurers

Dependency coordinates are interpreted by procurers, which understand a particular coordinate type and know how to find dependencies and download artifacts for a library. The Clojure CLI currently suports the folllowing procurers: mvn, local, and git. The underlying tools.deps library supports procurer extensions when used as a library.

The coordinate attributes determine which procurer is used. In general, most procurer attributes are qualified per procurer type (there are some historical exceptions). Procurer-independent attributes use the deps qualifier.

Some procurers may also look for configuration attributes at the root of the deps.edn configuration map using the the same qualifier.

Maven

The Maven procurer uses the qualifier mvn and is used to retrieve library artifacts from Maven repositories, the standard repository manager in the Java ecosystem. Libraries are downloaded as .jar files and stored in the Maven local repository cache (located in ~/.m2/repository by default). Other JVM-based tools may also use this cache.

Maven coordinates have the following attributes:

  • :mvn/version (required) - Maven version string

Maven libraries are located in Maven repositories using "Maven coordinates", typically the groupId, artifactId, and version (sometimes also abbreivated as GAV). In deps.edn, the library name is parsed as groupId/artifactId and the version is taken from :mvn/version.

For example, this deps.edn file:

{:deps {org.clojure/clojure {:mvn/version "1.11.1"}}}

declares a dependency on a Maven artifact with:

  • groupId = org.clojure

  • artifactId = clojure

  • version = 1.11.1

Some Maven artifacts also have a "classifier", which is a variant of a particular artifact. In the Maven procurer, classifiers are included at the end of the lib name, separated by a $ in this format: groupId/artifactId$classifier.

Currently, only jar artifacts are supported. Support for other artifact types (particularly Bill of Materials poms) may be added in the future.

The mvn procurer will look for two configuration keys at the root of the deps.edn:

  • :mvn/repos - map of repository name to repository descriptor, a map of:

    • :url - required, url string

    • :releases - a repository policy map for releases:

      • :enabled - boolean, default=true

      • :update - a flag for how often to check the repo for updates, one of :daily (default), :always, :never, or an interval in minutes (integer)

      • :checksum - a flag for checksum validation, one of :warn (default), :fail, :ignore

    • :snapshots - repository policy map for snapshots (same structure as :releases)

  • :mvn/local-repo - a path (string) to the local repo cache. If none supplied, Maven uses ~/.m2/repository.

The root deps.edn configures these default Maven repos:

{:mvn/repos
 {"central" {:url "https://repo1.maven.org/maven2/"}
  "clojars" {:url "https://clojars.org/repo"}}}

While specifying repository name and url is typically all that is required, full repository policy maps can also be provided:

{:mvn/repos
 {"my-releases" {:url "https://example.com/releases"
                 :snapshots {:enabled false}
                 :releases {:enabled true
                            :update :daily
                            :checksum :fail}}}}

Modifying the default repositories

The :mvn/repos map is merge-with merge across the deps.edn sources, so you can modify the default repositories by specifying a new definition or remove it by specifying nil.

tools.deps guarantees that the "central" and "clojars" repositories will be checked first, in that order, for Maven libraries. The lookup order of other repositories is undefined.

Maven authenticated repos

For Maven deps in authenticated repositories, existing Maven infrastructure is used to convey credentials.

In your ~/.m2/settings.xml:

<settings>
  ...
  <servers>
    <server>
      <id>my-auth-repo</id>
      <username>zango</username>
      <password>123</password>
    </server>
    ...
  </servers>
  ...
</settings>

Then in your deps.edn include a repo with a name matching the server id (here my-auth-repo):

{:deps
 {authenticated/dep {:mvn/version "1.2.3"}}
 :mvn/repos
 {"my-auth-repo" {:url "https://my.auth.com/repo"}}}

Then just refer to your dependencies as usual in the :deps.

Maven S3 repos

The mvn procurer also supports connecting to public and private Maven repositories hosted in AWS S3. This is particularly useful when accessing private Maven repositories from within an application hosted on AWS.

Add a :mvn/repos that includes the s3 repository root:

{:deps
 {my/library {:mvn/version "0.1.2"}}
 :mvn/repos
 {"my-private-repo" {:url "s3://my-bucket/maven/releases"}}}

S3 buckets are specific to the AWS region they were created in. The s3 transporter will attempt to determine the bucket’s location. If that doesn’t work, you can specify the bucket region in the url explicitly: "s3://my-bucket/maven/releases?region=us-west-2".

For authenticated repos, AWS credentials can be set in the ~/.m2/settings.xml on a per-server basis or will be loaded ambiently from the AWS credential chain (env vars, etc). The repository name in deps.edn must match the server id in settings.xml:

<settings>
  ...
  <servers>
    <server>
      <id>my-private-repo</id>
      <username>AWS_ACCESS_KEY_HERE</username>
      <password>AWS_SECRET_ACCESS_KEY_HERE</password>
    </server>
    ...
  </servers>
  ...
</settings>

AWS S3 credentials can be set in the environment using one of these mechanisms:

  1. Set the environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.

  2. Create a default profile in the AWS credentials file ~/.aws/credentials (older ~/.aws/config also supported).

  3. Create a named profile in the AWS credentials file and set the environment variable AWS_PROFILE with its name.

  4. Amazon ECS container and instance profile credentials should also work, but have not been tested.

For more information, most of the advice in this AWS document describes how credentials are located. Note however that the Java system properties options will NOT work with the Clojure CLI (but would work if using the tools.deps library directly).

Maven proxies

In environments where the internet is accessed via a proxy, existing Maven configuration in ~/.m2/settings.xml is used to set up the proxy connection:

<settings>
  ...
  <proxies>
    <proxy>
      <id>my-proxy</id>
      <host>proxy.my.org</host>
      <port>3128</port>
      <nonProxyHosts>localhost|*.my.org</nonProxyHosts>
    </proxy>
  </proxies>
  ...
</settings>

Refer to the Maven Guide to using proxies for further details.

Maven HTTP headers

For adding custom headers to outgoing HTTP requests, existing Maven configuration in ~/.m2/settings.xml is used.

<settings>
  ...
  <servers>
    <server>
      <id>my-token-repo</id>
      <configuration>
        <httpHeaders>
          <property>
            <name>Private-Token</name>
            <value>abc123</value>
          </property>
        </httpHeaders>
      </configuration>
    </server>
    ...
  </servers>
  ...
</settings>

The server id in settings.xml must match the repository name in deps.edn:

{:mvn/repos
 {"my-token-repo" {:url "https://my.auth.com/repo"}}}

This mechanism is used by repositories that authenticate using a token, rather than by username and password.

Git

The git procurer supports direct use of source-based libs hosted in Git repositories. Git libs are downloaded by default to the ~/.gitlibs directory. The working tree is checked out and cached for each sha included as a dependency.

Coord attributes

To specify a git lib as a dependency, two pieces of information must be known - the Git repo url and a specific sha. Using movable references like branch names is not supported.

Git coordinates have the following attributes:

  • :git/url - optional, Git url (may be inferred from lib name, see below)

  • :git/sha - required, either a full 40-char sha or a sha prefix can be provided in combination with a tag (:sha is also accepted for backwards compatibility)

  • :git/tag - optional, git tag name (may be used only in combination with a :git/sha)

The git url must either be provided or inferred from the lib name. If provided, the :git/url takes precedence. Lib to url convention is as follows:

Lib format Inferred :git/url

io.github.ORG/PROJECT

"https://github.com/ORG/PROJECT.git"

com.github.ORG/PROJECT

"https://github.com/ORG/PROJECT.git"

io.gitlab.ORG/PROJECT

"https://gitlab.com/ORG/PROJECT.git"

com.gitlab.ORG/PROJECT

"https://gitlab.com/ORG/PROJECT.git"

io.bitbucket.ORG/PROJECT

"https://bitbucket.org/ORG/PROJECT.git"

org.bitbucket.ORG/PROJECT

"https://bitbucket.org/ORG/PROJECT.git"

io.beanstalkapp.ORG/PROJECT

"https://ORG.git.beanstalkapp.com/PROJECT.git"

com.beanstalkapp.ORG/PROJECT

"https://ORG.git.beanstalkapp.com/PROJECT.git"

ht.sr.ORG/PROJECT

"https://git.sr.ht/~ORG/PROJECT"

The git sha is either provided as a full sha in :git/sha or as a full or prefix :git/sha in combination with a :git/tag (to provide semantic version context). If a :git/tag is provided, the full sha it refers to must match the full sha included or implied by :git/sha.

This is an example deps.edn including two valid git deps, the first with inferred git url, git tag, and sha prefix, and the second with explicit git url and full sha:

{:deps
 {;; implied git url, tag + sha prefix
  io.github.clojure/tools.deps.graph {:git/tag "v1.1.68" :git/sha "6971bb4"}

  ;; explicit git url, full sha
  org.clojure/tools.build {:git/url "https://github.com/clojure/tools.build.git"
                           :git/sha "ba1a2bf421838802e7bdefc541b41f57582e53b6"}}}

The supported git url protocols are https and ssh. https repos will be accessed anonymously and require no additional authentication information. This approach is recommended for public repos. ssh repos may be either public or private.

For more information on creating keys and using the ssh-agent to manage your ssh identities, GitHub provides excellent info:

Either a full :git/sha must be provided or a :git/tag with at least a prefix :git/sha. The latter is preferred as it provides both a meaningful version (via the tag) and the means to verify the tag has not been moved (via the sha). The CLI will verify that both the tag and prefix sha point to the same full sha commit, and error if not.

Version selection

When selecting a version from between sha A and sha B where B has A as an ancestor, then B will be preferred (the "most descendant" one). If A and B do not have an ancestor/descendant relationship (commits in parallel branches for example), this is an error and classpath construction will fail.

Configuration and debugging

The git procurer shells out to command-line git (and ssh). git >= 2.5 is required. In general, if access works at the command line, it should work via the Clojure CLI. Git is expected to be installed and by default, git is expected to be on the path. For ssh access, refer to documentation for your system (typically you will register your ssh keys in ~/.ssh/id_rsa).

The following environment variables can be set to control git usage:

  • GITLIBS - the path to the gitlibs storage directory (default = ~/.gitlibs)

  • GITLIBS_COMMAND - the command to run for git access (default = git)

  • GITLIBS_DEBUG - set true to see a log of the actual git commands being run and their output (default = false)

Clojure CLI installers

For CLI installation, see the instructions in the Install Clojure guide.

Tool installation and invocation

A tool is a collection of functions delivered in a lib. Tool functions are run in a separate process with their own classpath, independent of the project classpath. Tool functions take a single map argument and are invoked with -T (same key value arg syntax as -X style execution).

Tools are described either via an alias (which can be shared by others using the project), or by a local tool name installed on your machine (which can be shared across projects).

Using tool aliases

Tools declared with an alias are executed with -T, in the same manner as -X. However, -T will remove the project deps and paths and add "." as a path.

Declare an alias in a deps.edn (could be project or user):

{;; ...
 :aliases {
   :linter {:deps {cool/lint-tool {:git/tag "..." :git/sha "..."}}
            :ns-default cool.lint.tool}
 }}

This example invocation invokes the linter in a classpath that includes only the linter dependency (and its transitive dependencies). The function invoked will be cool.lint.tool/linter formed by combining the :ns-default in the alias and the function name lint on the command line:

clj -T:linter lint

Additional key / value argument may be passed on the command line after that, with same syntax as -X args.

Using named tools

The Clojure CLI comes with programs to search for available versions of a tool (either maven or git), and to install a tool under a local name for subsequent execution. Tools additionally may provide their usage configuration (like :ns-default) as part of the tool, rather than every user needing to define that in an alias.

Find versions

See find-versions for more on finding versions of a lib or tool (Maven or git).

Tool installation

A tool for managing tools is automatically installed by the Clojure CLI under the name "tools". There are several useful functions provided:

  • install - Installs or reinstalls a tool

  • install-latest - Installs or reinstalls the latest version of a tool

  • list - Lists all installed tools

  • remove - Removes an installed tool

  • show - Prints info and usage for a tool

Install

When you’ve determined which version of a tool to install, use the install function to install the tool by name.

clj -Ttools install ...

The args to install-tool are:

  • lib - val is coord map, as per deps.edn

  • :as - tool name, will be used for later invocations

For example:

clj -Ttools install io.github.clojure/tools.deps.graph '{:git/tag "v1.0.63"}' :as deps-graph

On Windows, additional escape quoting is required in Powershell:

clj -Ttools install io.github.clojure/tools.deps.graph '{:git/tag """v1.0.63"""}' :as deps-graph

Note that git deps can be fully described at tool installation time with only a git lib name (used to form a git url by convention), and a git tag. Alternately, the coord may contain an explicit :git/url or :git/sha instead.

Install latest

To find and install the latest version of a tool in one step:

clj -Ttools install-latest :lib io.github.clojure/tools.deps.graph :as deps-graph

To update an existing tool to the latest version you can also just specify the tool by name:

clj -Ttools install-latest :tool deps-graph

List

To list all installed tools:

clj -Ttools list

To remove an installed tool:

clj -Ttools remove :tool name

Other functions will likely be added in the future.

Tool usage

Tools may include a top-level key :tools/usage in their deps.edn file to publish how the tool can be used with respect to namespace context. The value is a map which may have the following keys that are the same as their usage in deps.edn aliases:

  • :ns-default - a lib symbol indicating the default namespace when resolving functions

  • :ns-aliases - a map of alias to lib symbol

For example, the tools.deps.graph tool can publish the following usage in deps.edn:

{:tools/usage
 {:ns-default clojure.tools.deps.graph}}

Currently, only tools that are deps.edn-based and available as either a git or local repository can provide a :tools/usage configuration.

Invoke a tool by name

Once you’ve installed a named tool, you can invoke it with -T using just the tool name.

clj -Ttool f args...

The function will be resolved using the :ns-default and :ns-aliases found in the tool’s deps.edn.

For example, for the tools.deps.graph tool declared above, it can be invoked as follows:

clj -Tdeps-graph graph :output '"graph.png"'

Best practices for tool authors

Best practices:

  • Provide your tool as a public git library

  • Define your tool api in one or more Clojure namespaces as functions that take a map

  • Create a :tools/usage key in the root of your deps.edn with either an :ns-default and/or an :ns-aliases key for your api namespaces

  • Tag your git repo to create a release, using a scheme that makes sequencing obvious to users. A common convention is to use versions strings like "v1.2.3".

Tools can provide these instructions for users:

  • Find tool versions: clj -X:deps find-versions :lib io.github.USER/TOOL

  • Install tool with clj -Ttools install io.github.USER/TOOL '{:git/tag "VERSION"}' :as NAME

  • Invoke tool with clj -TNAME f args…​

Function execution protocol

Some tools provide functions designed to be used for programmatic execution from another process, with the following constraints and expectations:

  • The function should take a single map argument

  • Results are returned from the function (as usual)

  • Exceptions thrown by the function will cause a failure and the exception data (ala Throwable→map will be conveyed to the external process, which will re-throw it in an ex-info)

  • Printing output will (by default) not be captured

  • Any result or exception data should be safe to print and read back to data in another process

There are several argument map keys that have special significance to the Clojure CLI during -X or -T. All of these keys will be removed from the argument map before the function is invoked:

  • :clojure.exec/invoke - true to use this protocol

  • :clojure.exec/out - true to capture and return stdout during function execution

  • :clojure.exec/err - true to capture and return stderr during function execution

The result is wrapped into a printed envelope map with the following keys and returned via the CLI stdout:

  • :tag - either :ret or :err based on whether the function returned a result or threw an exception

  • :val - either return value or exception data, prn to a string

  • :out - if requested, the captured stdout return

  • :err - if requested, the captured stderr return

  • :ms - function execution time in ms

A consumer API for this protocol will be provided as of Clojure 1.12.0-alpha2.

Command line reference

Arg map keys

This section summarizes all of the available arg map keys that clj uses and that can be conveyed via alias data in deps.edn. For clarity, these are separated by category, but in general, all of them can be used with -X (exec), -A (repl), and -M (main).

  • tool

    • :replace-deps - map of lib to coords that should replace the project deps

    • :replace-paths - vector of path strings that should replace the project paths

  • resolve-deps

    • :extra-deps - map of lib to coords that should be added to the root deps

    • :override-deps - map of lib to coords that should replace deps in the root deps

    • :default-deps - map of lib to coords that should be used if the coord is nil in root or transitive deps

  • make-classpath-map

    • :extra-paths - vector of string paths (or keyword aliases to same) to add to base paths

    • :classpath-overrides - map of lib to string path to replace lib on classpath

  • jvm opts

    • :jvm-opts - vector of strings to pass as jvm args

  • namespace aliasing

    • :ns-aliases - map of alias symbol to namespace symbol, used to resolve symbols (such as :exec-fn)

    • :ns-default - default namespace for unqualified symbols (such as :exec-fn)

  • execution (-X only)

    • :exec-fn - function to execute with -X

    • :exec-args - function args to pass to -X (can be overridden at command line)

  • main (-M only)

    • :main-opts - vector of string args to pass to clojure.main

Namespace resolution

Symbols in the exec-opts or argmaps (like :exec-fn) are resolved with the following rules:

  • If function is unqualified, use the namespace in the :ns-default key in the arg map (if none, this is an error)

  • If function is qualified, and the qualifier is an alias in the arg map under :ns-aliases, use that namespace

  • Else use the fully qualified function symbol

Keys and key paths

With the -X exec-opt you can supply key-path/value pairs on the command line. The key-path is either a single key or a vector of keys to refer to a nested key (as with assoc-in). Each key-path will be used to assoc-in to the original :exec-args map, overriding the value there.

# Top key override
clj -X:my-fn :val 789

# Nested key override
clj -X:my-fn '[:my :data]' 789

Trailing map argument

In addition to passing key-path/value pairs and key paths in the command line, an optional map providing value mappings may be passed as the final argument. This map will merge with and perhaps override the previously provided key-path/value mappings:

# Augment the arguments to my-fn
clj -X:a-tool my-fn :val 789 '{:val2 123}'

# Override the arguments to my-fn
clj -X:a-tool my-fn :val 789 '{:val 123}'

# Single map (no kvs) provides arguments to my-fn
clj -X:a-tool my-fn '{:val 789}'

Quoting keys and values

Keys and values on the command line are read as edn. The following data can be used without quoting:

  • Numbers - 123, 12.4

  • Booleans - true, false

  • Nil - nil

  • Symbols - name, foo/bar

  • Keywords - :id, :company/name

These data types need to be surrounded by single quotes:

  • Strings - '"hi there"' - note use of both single quotes for the shell and double quotes to be read as an edn string

  • Vectors - '[1 2 3]'

  • Maps - '{:a 1, :b 2}'

  • Sets - '#{:a :b}'

  • Lists - '(1 2 3)'

On Windows, WSL2 shells can follow the advice above, but on cmd.exe or Powershell, additional escape quoting is required for string values. Unfortunately the combination of quoting rules for converting command line Windows program arguments and Powershell quoting and word splitting are very complicated.

To pass a string value at the top level, if the string value does not have spaces, you can use '\"str\"'. If the string value does have spaces (or not) you should use '"""str value"""'.

PS D:> clj -X clojure.core/prn :string1 '\"no-spaces\"' :string2 '"""has spaces"""'
{:string1 "no-spaces", :string2 "has spaces"}

For string values nested inside other collections, use double quotes if there are spaces and triple quotes if there are not:

PS D:> clj -X clojure.core/prn :val '{:s1 """nospaces""" :s2 ""has spaces""}'
{:val {:s1 "nospaces", :s2 "has spaces"}}

Environment variables

The following environment variables can influence CLI execution (many are described in more detail elsewhere on this page):

  • CLJ_CONFIG - user config directory, used to store user deps.edn, tool config, and cpcache for commands without a project deps.edn (default = ~/.clojure)

  • XDG_CONFIG_HOME - if this standard var is set, will use $XDG_CONFIG_HOME/clojure as the user config directory

  • CLJ_CACHE - user cache directory, (defaults = <config-dir>/.cpcache)

  • XDG_CACHE_HOME - if this standard var is set, will use $XDG_CACHE_HOME/clojure as the user cache directory

  • CLJ_JVM_OPTS - JVM options to be included in internal calls to the classpath building process, which can be useful to provide things like a truststore with a self-signed certifate using to download internal artifacts

  • JAVA_CMD - path to Java executable to use

  • JAVA_HOME - if no $JAVA_CMD and no java on path, will try to use $JAVA_HOME/bin/java

  • JAVA_OPTS - JVM options to be included in the user command being executed - not cached. Included before any -J command line parameters and any :jvm-opts

  • AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and other AWS env vars are used when accessing S3-based Maven repos

  • GITLIBS - the path to the gitlibs storage directory (default = ~/.gitlibs)

  • GITLIBS_COMMAND - the git command to run (default = git)

  • GITLIBS_DEBUG - set to true to print all git commands being run

Glossary

Library

A collection of Clojure or other code that solves some problem, managed under a single directory root. In typical (but not exclusive) usage, most GitHub repos hold a single library. Each library has a distinct namespaced name - in Maven this is determined by the group-id/artifact-id.

Artifact

A library released in a container file, capturing the state of the library at a point in time, possibly subjected to some build process, labeled with a version, containing some manifest documenting its dependencies, and packaged in e.g. a jar.

Coordinate

A particular version of a library chosen for use, with information sufficient to obtain and use the library.

Dependency

An expression, at the project/library level, that the declaring library needs the declared library in order to provide some of its functions. Must at least specify library name, might also specify version and other attrs. Actual (functional) dependencies are more fine-grained.

Dependency types:

  • maven artifacts (artifact-based)

  • git libraries (source-based)

  • local library (source-based) - a local directory

  • local artifact (artifact-based) - a local jar file

Classpath (and roots/paths)

An ordered list of local 'places' (filesystem directories and/or jars) that will form the root paths for searches of requires/imports at runtime, supplied as an argument to Java which controls the semantics. We discourage order-dependence in the classpath, which implies a namespace or resource file is duplicated (and thus likely broken).

Expansion

Given a set of root dependencies, a full closure of the transitive dependency set.

Resolution

Given a collection of root dependencies and additional modifications, creates a fully-expanded dependency tree, then produces a mapping from each library mentioned to a single version to be used that would satisfy all dependents, as well as the local path to that source or artifact. We will also include those dependents for each entry. Conflicts arise only if libraries depend on different major versions of a library.

Classpath creation

Creates a classpath from a resolved lib-map and optional extra local lib paths. Current plan for lib-map does not provide for control over resulting order.

Version

A human numbering system whose interpretation is determined by convention. Often x.y.z. Must protect against 'semver' interpretation, which allows libraries to break users while keeping the name the same. Ascending by convention - higher numbers are 'later', vague compatibility with lower/earlier.

Version difference

This occurs when the dependency expansion contains the same library with more than one "version" specified but where there is a relative ordering (either by number or by sha etc). Version differences can be resolved by choosing the "later" or "newest" version when that relationship can be established.

Version conflict

A version conflict occurs when the dependency expansion contains the same library with more than one "version" such that the best choice cannot be automatically chosen:

  • semver version breakage (major version changed)

  • github shas that do not contain any common root or ancestry (two shas on different branches or unrelated repos, for example)

  • versions that cross different repos or repo types such that no relative relationship can be established

Maven Repo

A repository of library artifacts - e.g. Maven central or Clojars

Tools

See the project’s wiki for a list of tools that use or work with tools.deps (or the clojure tools) - tools for project creation, packaging, and much more.