I was always using -A with clojure tools, but some warnings were saying that I should use -M instead, I found this doc, but haven't got to a conclusion when to use each yet.
I was always using -A with clojure tools, but some warnings were saying that I should use -M instead, I found this doc, but haven't got to a conclusion when to use each yet.
The Clojure CLI is still evolving so, the -X option is relatively new, and the meaning of the -M option has changed in the same timeframe.
You can see the CLI versions and short release notes here: https://clojure.org/releases/tools
Up until the middle of 2020, you used -A for everything. -M simply ran the :main-opts -- it did not respect any classpath options or resolving options.
In version 1.10.1.697 (September 25, 2020), the -X option was introduced to allow for execution of a specific Clojure function, passing a hash map of data as the single argument. That release also expanded the behavior of the -M option to respect :extra-paths and :extra-deps as well as running :main-opts -- effectively making -M equivalent to -A.
There were quite a few changes to how those options worked and how the CLI overall behaved, until things settled down about a month later with 1.10.1.727 (October 21, 2020). During this time, the -A option's behavior of running :main-opts was effectively deprecated: if you use -A to run :main-opts now, you'll get a warning that you should use -M instead.
Several community tools based on the Clojure CLI and deps.edn have notes in their README that you need to use at least version 1.10.1.727 in order to leverage their features. Version 1.10.3.814 is the current version (as of March 16, 2021). It's worth staying up to date as the CLI adds new features (and it's likely to get another round of changes soon).
The TL;DR of all this is:
-M to run clojure.main and any :main-opts -- this includes -m to identify a namespace whose -main function should be executed and also -e to evaluate an expression. Note that -main is a variadic function that is invoked with zero or more String arguments.-X to run a specific function, taking a single hash map as its argument, passing in key/value pairs via the command-line, in addition to the :exec-args in deps.edn. Note that -X accepts EDN values, which means that strings need to be quoted carefully on the command-line: '"A string"' -- the double-quotes are for EDN strings and the single-quotes are to ensure the value is passed as-is via the shell. On Windows, via cmd.exe or Powershell, quoting is more complex than this (it is much easier to use the Clojure CLI on Linux and macOS, so for Windows it is worth considering WSL2).-A to start a REPL. For now, that means you need to be careful about mixing :main-opts with aliases that you use when starting a REPL (because -A still runs :main-opts but at some point it will stop doing that).Another useful option these days is -P -- prepare -- which you can add immediately after clojure (before -X, -M, or -A), which lets the CLI calculate and resolve all of the dependencies from the deps.edn files and the provided aliases (and download Maven/Clojars JARs and clone git deps), but then stops prior to actual execution of a function (the specific function for -X, clojure.main for -M, or the REPL for -A).
A final note on versions: although the CLI version number is prefixed with a Clojure version number, they are not directly tied. You can run any version of Clojure (back to 1.0.0) with any version of the CLI. The version number for the CLI is x.y.z.commits where x.y.z is the default version of Clojure you will get if you don't override it via an alias or via your project's deps.edn file.
The --help text clarifies things a bit:
> clojure --help
Version: 1.10.3.814
You use the Clojure tools ('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 tools will configure the JVM process by defining a classpath
(of desired libraries), an execution environment (JVM options) and
specifying a 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 tools, 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'.
Usage:
Start a REPL clj [clj-opt*] [-Aaliases] [init-opt*]
Exec function clojure [clj-opt*] -X[aliases] [a/fn] [kpath v]*
Run main clojure [clj-opt*] -M[aliases] [init-opt*] [main-opt] [arg*]
Prepare clojure [clj-opt*] -P [other exec opts]
exec-opts:
-Aaliases Use concatenated aliases to modify classpath
-X[aliases] Use concatenated aliases to modify classpath or supply exec fn/args
-M[aliases] Use concatenated aliases to modify classpath or supply main opts
-P Prepare deps - download libs, cache classpath, but don't exec
clj-opts:
-Jopt Pass opt through in java_opts, ex: -J-Xmx512m
-Sdeps EDN Deps data to use as the last deps file to be merged
-Spath Compute classpath and echo to stdout only
-Spom Generate (or update) pom.xml with deps and paths
-Stree Print dependency tree
-Scp CP Do NOT compute or cache classpath, use this one instead
-Srepro Ignore the ~/.clojure/deps.edn config file
-Sforce Force recomputation of the classpath (don't use the cache)
-Sverbose Print important path info to console
-Sdescribe Print environment and command parsing info as data
-Sthreads Set specific number of download threads
-Strace Write a trace.edn file that traces deps expansion
-- Stop parsing dep options and pass remaining arguments to clojure.main
--version Print the version to stdout and exit
-version Print the version to stderr and exit
init-opt:
-i, --init path Load a file or resource
-e, --eval string Eval exprs in string; print non-nil values
--report target Report uncaught exception to "file" (default), "stderr", or "none"
main-opt:
-m, --main ns-name Call the -main function from namespace w/args
-r, --repl Run a repl
path Run a script from a file or resource
- Run a script from standard input
-h, -?, --help Print this help message and exit
Programs provided by :deps alias:
-X:deps mvn-install Install a maven jar to the local repository cache
-X:deps git-resolve-tags Resolve git coord tags to shas and update deps.edn
For more info, see:
https://clojure.org/guides/deps_and_cli
https://clojure.org/reference/repl_and_main
So -A (for "alias") is a catch-all that is legal substitute for any of the more specialized flags. I usually use -A so I don't have to remember which other ones serve which purpose.
You should also see this guide on Deps/CLI.