Clojure

Clojure CLI Reference

Installation

The Clojure CLI can be installed in a variety of ways, see the installation guide for details and the release page for version information. You can also read the guide for a longer tutorial. For artifact building, check out the tools.build guide.

Any version of the Clojure CLI can use any version of the Clojure language. Generally it is best to use the latest version of the CLI to have the latest features and fixes. Given a CLI version A.B.C[.D], the default version of Clojure used at the REPL will be A.B.C, however you can specify an older (or newer!) version of the language in your dependencies.

Usage

The CLI is invoked via either clojure or clj. In general, you can treat these interchangeably but clj includes rlwrap for extended keyboard editing, particularly useful with the REPL. This reference uses clj for REPL examples and clojure for non-REPL examples.

Each execution of the Clojure CLI runs a Clojure program by determining all paths, dependencies, and main entry point, then invoking the program on the JVM. The primary kind of execution is determined by -X, -T, and -M (or their absence). Configuration is defined by one or more deps.edn files in combination with the command-line options.

Primary commands

Start a REPL (default):
clj [clj-opts] [-Aaliases]

Execute a function (-X):
clojure [clj-opts] -X[aliases] my/fn? [kpath v …​] kv-map?

Run a tool (-T):
clojure [clj-opts] -T[name|aliases] my/fn [kpath v …​] kv-map?

Run a main namespace or script (-M):
clojure [clj-opts] -M[aliases] [init-opts] [main-opts] [args]

Option index

exec-opts:
 -Aaliases    Apply aliases
 -X[aliases]  Invoke function, using aliases
 -Ttoolname   Invoke tool by local name
 -T[aliases]  Invoke tool using aliases
 -M[aliases]  Invoke clojure.main, using aliases
 -P           Prepare deps but don't exec

clj-opts:
 -Jopt        Pass JVM option
 -Sdeps EDN   Extra deps.edn data
 -Srepro      Ignore user deps.edn file
 -Spath       Compute and echo classpath
 -Stree       Print dependency tree
 -Scp CP      Use this classpath, ignore deps.edn
 -Sforce      Force classpath computation
 -Sverbose    Print path info
 -Sdescribe   Print env and command parsing info
 -Sthreads    Set specific number of download threads
 -Strace      Write dep expansion trace.edn
 --version    Print version to stdout and exit
 -version     Print version to stderr and exit
 --help -h -? Print this help message

Programs provided by :deps alias:
 -X:deps list          Print deps list+licenses
 -X:deps tree          Print deps tree
 -X:deps find-versions Find available lib versions
 -X:deps prep          Prepare all unprepped libs in deps
 -X:deps mvn-pom       Generate pom.xml for deps.edn
 -X:deps mvn-install   Install maven jar to local repo

Start a REPL

Use this option to start a Clojure REPL.

clj [clj-opts] [-Aaliases]

To run a REPL, invoke clj without arguments.

This works in any directory, either with a deps.edn or not. If you are not in a project directory, the version of Clojure used will depend on the Clojure language version from the org.clojure dependency in the root deps.edn (will match the version number in CLI version number) or the user deps.edn in the config directory (usually not specified).

To supply aliases that modify the classpath or JVM properties, use -A with one or more concatenated alias keywords:

clj -A:test

clj -A:test:dev

Execute a function (-X)

The Clojure CLI can execute any function on the project classpath that takes keyword args in a map as input.

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

Function execution ultimately needs the following pieces of information:

  • Classpath - based on the deps sources and aliases

  • Function to execute

  • Map of function arguments

The function and its arguments may be supplied either via data in aliases and/or at the command line.

The aliases can contain the following arg map keys:

  • :exec-fn - a function symbol to use if none is supplied on the command line

  • :exec-args - a map of key to value to include when exec-fn is executed

  • :ns-default - default namespace symbol to use when resolving the exec-fn

  • :ns-aliases - map of alias symbol to namespace symbol to use when resolving the exec-fn

If the function to invoke is defined in a specified alias, the function is optional on the command line. If the function symbol is specified on the command line, it overrides the :exec-fn in aliases, and is resolved in terms of :ns-default and :ns-aliases in aliases.

The :exec-args form a base map that is merged with keys and values and trailing kv-map on the command line, merged in that order.

Keys on the command line may be either keywords or vector key paths (as used with assoc-in) to specify a nested key. The final arg on the command line is an optional trailing map of keys and values. See the later sections on keys and key paths and quoting for more details on how to properly specify edn keys and values.

Run a tool (-T)

Tools are functions external to the project classpath. Running tool functions with -T is the same as running project functions with -X, except the project classpath is not included.

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

When constructing the classpath for the tool, the project :deps and :paths are not included.

The tool deps are included either via the tool aliases or a tool name. Tools may be installed locally with a short name for use. Installed tools can be used on any project with the same user configuration. See tool installation for more details.

To run a tool by name, use -Ttoolname, like -Tlinter. To run a tool by alias[es], use -T:linter (aliases are always keywords).

Unlike -X, a tool function MUST be specified on the command line and cannot be specified in the tool alias or installed tool configuration. Otherwise, tool functions and arguments are specified the same as -X in both aliases and command line.

Run a main namespace or script (-M)

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 (if provided) are pulled from deps.edn and combined to form an arg map. The arg map can modify the classpath or supply :main-opts, a vector of string main options. See the clojure.main page for more information on the available main-opts.

Common uses:

# run single expression:
clojure -M -e '(+ 1 1)'

# run Clojure namespace with main entry point:
clojure -M -m my.app

# run standalone Clojure script
clojure -M script.clj

Main opts can be provided in aliases with the arg map key :main-opts - when aliases are merged, any :main-opts provided replaces those from previous aliases. Additional options on the command line after -M are appended to those found in the merged alias arg map.

Options

-Aaliases

-A takes one or more concatenated aliases, which are always simple or qualified keywords, e.g. -A:test or -A:test:perf/benchmark.

While -A can be used with all execution commands, it is the only exec opt that can be used for REPL execution, and that is the most common reason to use it.

-X[aliases]

-X optionally takes one or more concatenated aliases, which are always simple or qualified keywords, e.g. -X:test or -X:test:perf/benchmark.

The -X exec-opt indicates function execution, and all arguments after -X are arguments to the function being executed, not the CLI. All clj-opts (-S, -P, etc) must be placed prior to the -X in the command line.

-Ttoolname, -T[aliases]

-T takes either a tool name (always symbols, not keywords) or one or more concatenated aliases, which are always simple or qualified keywords, e.g. -T:test or -T:test:perf/benchmark.

The -T exec-opt indicates tool execution, and all arguments after -T are arguments to the tool function, not the CLI. All clj-opts (-S, -P, etc) must be placed prior to the -T in the command line.

-M[aliases]

-M optionally takes one or more concatenated aliases, which are always simple or qualified keywords, e.g. -M:test or -M:test:perf/benchmark.

The -M exec-opt indicates clojure.main execution, and all arguments after -M are interpreted by clojure.main. All clj-opts (-S, -P, etc) must be placed prior to -M in the command line.

-P

Use -P before any of the other exec-opts (-A, -X, -M, -T) to do a full deps expansion, download deps, and cache the classpath, but not actually execute the function, tool, main, etc.

-Jopt

Use -J to prefix any JVM option that should be provided to your program (the -J will be stripped). For example, -J-Xmx1g or -J-XX:+HeapDumpOnOutOfMemoryError. -J can be used with all execution modes that run a program (repl, -X, -T, -M).

Also see JVM opts for more information on other ways to supply JVM opts.

-Sdeps deps

Use -Sdeps to supply an additional deps.edn source on the command line. This is used as the last deps source in the merge. The deps data will need to be appropriately quoted for the command line.

-Srepro

Use -Srepro to omit the user deps.edn from the deps sources. This omits any user-specific configuration to ensure the command is repeatable for others.

Clojure deps sources

-Spath

Use -Spath to compute and print the classpath.

-Stree

Use -Stree to compute and print the dependency tree. See the dependency expansion page for more information on the tree printing output.

-Scp CP

When this option is used, the classpath is not computed and the provided classpath is used instead. This is mostly useful in testing or debugging. In general, you should let the Clojure CLI compute (and cache) your classpath based on the deps.edn settings.

-Sforce

This flag marks the existing cached classpath (if any) to be marked as stale. The classpath will be recomputed and cache.

-Sverbose

Print environment and path information found and used by the Clojure CLI, mostly useful for debugging the location of various config and cache directories.

This flag does not alter any other execution that may be specified on the command line, so it can be used to debug the location of the cache files for a particular command.

-Sdescribe

Print configuration settings as edn data and exit. This overlaps in functionality with -Sverbose but may be useful for programmatic use.

-Sthreads N

By default, dep expansion will happen using a thread pool sized based on the processor count. Use this setting to change the number of threads used during expansion. Setting to 1 will do expansion serially with a single thread.

-Strace

The dependency tree printed by -Stree or -X:deps tree often contains sufficient information to debug why a particular lib or lib version was selected. However, if you need more information, this flag prints significantly larger information to a trace.edn file that can be used when filing an issue on tools.deps at Ask Clojure.

--version, -version

Prints the Clojure CLI version to either stdout (--version) or stderr (-version). Note that while the Clojure CLI version determines the default Clojure version used in execution, any version of the CLI can use any version of Clojure, and the Clojure version can be set in deps.edn files to change that version.

See the tools release page for more version and changelog information.

--help, -h, -?

Print help to console. Also see man clojure.

Aliases

When invoking commands in the context of a project, it is common to need to specify complex data that is hard to quote on the command line. As Clojure programmers, we strongly prefer to specify that data in Clojure syntax (edn). The deps.edn file format allows us to define arbitrary Clojure data and give that data a name using aliases. Aliases are simply keywords that name edn data.

Each execution mode of the Clojure CLI has many configuration options (the "arg map"). These can be defined in edn maps via aliases and selected on the command line using the aliases in -A, -X, -T, or -M. Below is a list of all arg map keys - unless otherwise noted, these are valid in all execution modes.

  • Project dependency modifiers

    • :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

  • Classpath modifiers

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

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

  • Tool deps and paths (primarily used by -T)

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

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

  • JVM options

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

  • Namespace resolution (primarily used by -X and -T)

    • :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)

  • Function execution (-X and -T 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

When multiple aliases are supplied, the keys in the aliases arg maps are merged with the following semantics (in the order specified in the concatenated aliases):

  • :extra-deps - merge

  • :override-deps - merge

  • :default-deps - merge

  • :extra-paths - concatenate and distinct

  • :classpath-overrides - merge

  • :replace-deps / :deps - merge

  • :replace-paths / :paths - concatenate and distinct

  • :jvm-opts - concatenate

  • :ns-aliases - merge

  • :ns-default - replace (last wins)

  • :exec-fn - replace (last wins)

  • :exec-args - merge if map, or replace

  • :main-opts - replace (last wins)

Using aliases for custom purposes

The alias keys above are meaningful for Clojure CLI execution, but you may define aliases for any purpose. If you are creating a custom tool that needs configuration, it is a good practice to define well-known, namespaced aliases or alias-keys for use by your tool. Please refrain from adding top-level keys to the deps.edn files - they may not always be available via programmatic tools.

Programs run by the Clojure CLI are given the "runtime basis" for the execution, including all alias data. The clojure.java.basis API being added in Clojure 1.12 can be used to retrieve alias data at runtime in the program.

Namespace resolution

Symbols in the exec-opts or arg maps (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

Dependencies

Each dependency is defined in the deps.edn format with a lib and coordinate, multiple deps combined into a map (the alias arg map keys use the same format).

deps.edn sources

The Clojure CLI will construct the map of paths, dependencies, and aliases to use by combining up to four deps edn sources:

  • Root deps - defined as a resource embedded in the tools.deps library, defines Clojure itself as the only dependency (version will match the Clojure CLI version), and two built-in aliases: :deps and :test. Two built-in Maven repositories are included - Maven central and Clojars.

  • User deps (optional) - a deps.edn file in the user configuration directory. The file starts empty but may have additional configuration and tools defined that cross projects.

  • Project deps (optional) - the deps.edn in the current directory (aka the project directory)

  • Extra deps (optional) - provided on the command-line with -Sdeps

The deps sources are merged into a single master deps edn in the order listed above except:

  • -T tool execution - project :deps is removed, and project :paths are replaced with ["."]

  • -Srepro - user :deps is ignored

The merge is essentially merge-with merge, except for :paths where only the last deps source :paths is used.

Classpath

The JVM classpath consists of a series of roots, either directory paths or paths to jar files. 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.

The Clojure CLI will compute a classpath based on:

Ultimately all of those combine into either:

  • Project paths

  • Deps (external libraries and their transitive deps)

Project paths

Once merging of deps and alias args has occurred, there are :paths from deps.edn and :extra-paths from aliases. These both consist of vectors and the order in those vectors is retained. The extra paths are always before the paths, so that aliases may override the project settings.

Note that both project paths and extra-paths are resolved relative to the current directory and should refer only to paths inside the project, not parent or sibling directories. Currently, referring outside the project will emit a warning, but support for this is deprecated and will be removed in the future (use local deps instead).

Dependency expansion

Once deps have been merged, there is a top-level set of dependencies and these form the roots of the dependency graph. The graph is explored top-down and each dependency is expanded in turn using the appropriate procurers (Maven. Git, local). Cycles are detected and will not be examined again.

When multiple versions of the same library are found, a version selection process occurs. The dependency expansion page has more details on this process but generally the newest version of a library is used. The top-level dependency versions however, are always used without modification - if you need to specify a specific version or resolve a conflict, set the version in a top-level dependency.

Dependencies are ordered in graph depth from the top, and alphabetically sorted by lib name at each level.

For more information on the arg map modifiers like :extra-deps, :override-deps, and :default-deps, see the deps.edn reference page.

Classpath construction

The classpath will contain:

  • Extra paths (relative to project), in order declared in the last alias to specify them

  • Source paths (relative to project), in order declared in the last deps to specify them

  • Dependency paths (usually absolute) referring to jars or directories in the appropriate procurer download locations

    • Dependencies are ordered from the top of the dependency graph based on depth, then alphabetically sorted by lib name

Classpaths may further be modified by the arg map key :classpath-overrides (see the deps.edn reference).

You can print the computed classpath with -Spath.

JVM properties

JVM properties have several sources:

  • Hard-coded options: -XX:-OmitStackTraceInFastThrow

  • $JAVA_OPTS environment variable

  • Alias arg map key :jvm-opts (merged across aliases in the master deps)

  • Command line -J options

All JVM command-line options are concatenated in the order above. In most cases, JVM options later on the command-line will override any previous setting, but in all cases the semantics of the concatenated options are those of the JVM. There is no de-duplication or replacement of options.

CLI command

The clj and clojure commands are host-specific scripts that:

  1. Parse CLI arguments

  2. (If not cached), launch a JVM to compute and cache classpath and other setup

  3. Launch a JVM to run the user program as specified by the exec opts -X, -T, -M

Step 2 is done with an uberjar that is part of the Clojure CLI installation - generally you do not control the classpath or configuration of that JVM (but see environment variables for some exceptions).

Java

You are expected to install Java and have it available by one of several means when using the Clojure CLI. Java 8 or higher is required. Any Java distribution will work.

Java is found by checking the following in this order:

If not found, the CLI will stop with an error message. If clj -h completes successfully, then a Java executable was found.

Directories and caching

There are several important directories used by the Clojure CLI, this section details how they are computed.

Project directory

The project directory is the current directory. If the project directory contains a deps.edn file, that will be used as the project deps source.

There is no option for executing in the context of a remote project directory.

Config directory

The config directory contains:

The config directory is computed as follows:

  • If $CLJ_CONFIG is set, use that path

  • If $XDG_CONFIG_HOME is set, use $XDG_CONFIG_HOME/clojure

  • Else use $HOME/.clojure

If the config directory does not exist, it will be created and the following files will be copied to it:

  • deps.edn - the default user deps.edn (essentially empty)

  • tools/tools.edn - the built-in tool "tools" for managing tools

Cache directory

Every execution of the CLI uses a cache directory to store computed classpath and other files.

The cache directory is computed as follows:

  • If the current directory has a project deps.edn and the current directory is writeable, use ./.cpcache

  • Else use the .cpcache directory in the config directory

The files in the cache directory are a cache to improve startup time when using a classpath that has already been computed. In general, this cache should never be stale, however you can use -Sforce to force a recomputation on a specific command, or simply rm the cache directory if unsure.

When you install a new version of the CLI, this sometimes invalidates the cache (if the cache key format has changed), causing commands you have run before to recompute the classpath.

The Clojure CLI never deletes files in the cache directories, so it is up to you if you wish to clean these directories periodically. It is a good practice to include .cpcache/ in your project .gitignore file - nothing there is useful to share with other project users.

Environment vars

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

  • CLOJURE_CLI_ALLOW_HTTP_REPO - set to true to allow http:// repository urls (this may make your dependency downloads subject to man-in-the-middle attacks)

Keys and key paths

With the -X or -T exec-opts 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

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 using clojure.exe, additional escape quoting is required for string values. Unfortunately the combination of quoting rules for converting command line Windows program arguments, 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"}}

Programs

The Clojure CLI is designed to provide access to programs (functions) in other libraries with their own independent classpath. This capability is leveraged to provide expanded functionality via the built-in :deps alias in the root deps.edn, which provides the tools.deps.cli (see API).

Dependency list

---
clj -X:deps list
---

Prints sorted list of all transitive dependencies selected for the classpath, and license information (if found). Additional options can be used to select aliases or other classpath modifications, or to modify printing information.

See API docs.

Dependency tree

---
clj -X:deps tree
---

Prints dependency tree with inclusion/exclusion information as determined during dependency expansion). Additional options can be used to select aliases or other classpath modifications, or to modify printing information.

For example, the following can be used to print the deps tree for a :test alias:

---
clj -X:deps tree :aliases '[:test]`
---

See API docs.

Alias list

---
clj -X:deps aliases
---

This program prints all aliases available at the command line based on the current deps environment. Additional options can be used to select aliases or other classpath modifications.

See API docs.

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

Generate Maven pom

Use the following program to generate or update an existing pom.xml with the deps and paths from your project:

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

See API docs for more.

Tools

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

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:

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

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 - :fn to use this protocol

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

  • :clojure.exec/err - :capture 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 is provided since Clojure 1.12.0-alpha2.

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: Maven, Git, and local (which includes both directories and jars). 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 a few exceptions). Procurer-independent coordinate 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.

See the deps.edn reference for details on the Maven coordinate type and Maven procurer config.

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.

See the deps.edn reference for details on the Git coordinate type.

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)

Local

The local procurer includes local directories or jar files as a dependency. See the deps.edn reference for details on the local coordinate type.

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.

Version

A human numbering system whose interpretation is determined by convention, often x.y.z. One popular scheme is "semantic versioning" aka "semver", which ascribes meaning to changes at each level, e.g. a change in the first major number indicates breaking change.

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