Clojure CLI and tools.deps
Clojure Command Line Interface (CLI) tools provide a fast way for developers to get started with Clojure and simplify an already pretty simple experience. With tools.deps it also provides a more flexible approach to including libraries, including the use of code from a specific commit in a Git repository.
Newer content can be found in Using Clojure tools section of Practicalli Clojure
Practicalli Clojure 35 - Clojure CLI tools - an introduction is a video of a live broadcast of this content (inclucing typos)
Clojure CLI tools provide:
- Running an interactive REPL (Read-Eval-Print Loop)
- Running Clojure programs
- Evaluating Clojure expressions
- Managing dependencies via tools.deps
Clojure CLI tools allow you to use other libraries to, referred to as dependencies or 'deps'. These may be libraries you are writing locally, projects in git (e.g. on GitHub) or libraries published to Maven Central or Clojars.
The Clojure CLI tools can cover the essential features of Clojure Build tools Leiningen and Boot, but are not designed as a complete replacement. Both these build tools are mature and may have features you would otherwise need to script in Clojure CLI tools.
This article is a follow on from new Clojure REPL Experience With Clojure CLI Tools and Rebel Readline
Getting started
Clojure is packaged as a complete library, a JVM JAR file, that is simply included in the project like any other library you would use. You could just use the Java command line, but then you would need to pass in quite a few arguments as your project added other libraries.
Clojure is a hosted language, so you need to have a Java runtime environment (Java JRE or SDK) and I recommend installing this from Adopt OpenJDK. Installation guides for Java are covered on the ClojureBridge London website
The Clojure.org getting started guide covers instructions for Linux and MacOXS operating systems. There is also an early access release of clj for windows
Basic usage
The installation provides the command called clojure
and a wrapper called clj
that provides a readline program called rlwrap that adds completion and history once the Clojure REPL is running.
Use clj
when you want to run a repl (unless you are using rebel readline instead) and clojure
for everything else.
Start a Clojure REPL using the clj
command in a terminal window. This does not need to be in a directory containing a Clojure project for a simple REPL.
clj
A Clojure REPL will now run. Type in a Clojure expression and press Return
to see the result
Exit the REPL by typing Ctrl+D
(pressing the Ctrl
and D
keys at the same time).
Run a Clojure program in a the given file. This would be useful if you wanted to run a script or batch jobs.
clojure script.clj
Aliases can be added that define configurations for a specific build task:
clojure -A:my-task
You can use and legal Clojure keyword name for an alias and include multiple aliases with the
clojure
command. For example in this command we are combining three aliases:clojure -A:my-task:my-build:my-prefs
What version of Clojure CLI tools are installed?
The deps.edn
file allows you to specify a particular version of the Clojure language the REPL and project use. You can also evaluate *clojue-version*
in a REPL to see which version of the Clojure language is being used.
clj -Sdescribe
will show you the version of the Clojure CLI tools that is currently installed.
clj -Sverbose
will also show the version of Clojure CLI tools used before it runs a Rimages
deps.edn
deps.edn
is a configuration file using extensible data notation (edn), the language that is used to define the structure of Clojure itself.
Configuration is defined using a map with top-level keys for :deps
, :paths
, and :aliases
and any provider-specific keys for configuring dependency sources (e.g. GitHub, GitLab, Bitbucket).
~/.clojure/deps.edn
for global configurations that you wish to apply to all the projects you work with
project-directory/deps.edn
for project specific settings
The installation directory may also contain a deps.edn
file. On my Ubuntu Linux system this location is /usr/local/lib/clojure/deps.edn
and contains the following configuration.
{
:paths ["src"]
:deps {
org.clojure/clojure {:mvn/version "1.10.1"}
}
:aliases {
:deps {:extra-deps {org.clojure/tools.deps.alpha {:mvn/version "0.6.496"}}}
:test {:extra-paths ["test"]}
}
:mvn/repos {
"central" {:url "https://repo1.maven.org/maven2/"}
"clojars" {:url "https://repo.clojars.org/"}
}
}
Note: the install
deps.edn
is now depreciated and will not be included in a future version of the Clojure CLI tools.
The deps.edn files in each of these locations (if they exist) are merged to form one combined dependency configuration. The merge is done in the order above install/config/local, 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).
You can use the -Sverbose
option to see all of the actual directory locations.
Much more detail is covered in the Clojure.org article - deps and cli
Using Libraries - deps.edn
deps.edn
file in the top level of your project can be used to include libraries in your project. These may be libraries you are writing locally, projects in git (e.g. on GitHub) or libraries published to Maven Central or Clojars.
Include a library by providing its name and other aspects like version. This information can be found on Clojars if the library is published there.
Libraries as JAR files will be cached in the $HOME/.m2/repository
directory.
Example clj-time
Declare clojure.java-time
as a dependency in the deps.edn
file, so Clojure CLI tools can downloaded the library and add it to the classpath.
{:deps
{org.clojure/clojure {:mvn/version "1.10.1"}
clojure.java-time {:mvn/version "0.3.2"}}}
Writing code
For larger projects you should definately find an editor you find productive and has great CLojure support. You can write code in the REPL and you can just run a specific file of code, if you dont want to set up a full project.
Create a directory what-time-is-it
.
Create a deps.edn
file in this directory with the following code:
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.10.1"}
clojure.java-time {:mvn/version "0.3.2"}}}
Create a src
directory and the source code file src/practicalli/what_time_is_it.clj
which contains the following code:
(ns practicalli.what-time-is-it
(:require [java-time :as time]))
(defn -main []
(println "The time according to Clojure java-time is:"
(time/local-date-time)))
The code has a static entry point named -main
that can be called from Clojure CLI tools. The -m
option defines the main namespace and by default the -main
function is called from that namespace. So the Clojure CLI tools provide program launcher for a specific namespace:
clojure -m practicalli.what-time-is-it
The time according to Clojure java-time is: #object[java.time.LocalDateTime 0x635e9727 2019-08-05T16:04:52.582]
Using libraries from other places
With deps.edn
you are not limited to using just dependencies from JAR files, its much easier to pull code from anywhere.
TODO: Expand on this section in another article with some useful examples
rebel readline
Rebel readline enhances the REPL experience by providing multi-line editing with auto-indenting, language completions, syntax highlighting and function argument hints as you code.
- New Clojure REPL experience with Clj tools and rebel readline
- Video: rebel-readline in Clojure CLI REPL
clj-new
clj-new is a tool to generate new projects from its own small set of templates. You can also create your own clj-new templates. It is also possible to generate projects from Leiningen or Boot templates, however, this does not create a deps.edn
file for Clojure CLI tools, it just creates the project as it would from either Leiningen or Boot.
Add clj-new
as an alias in your ~/.clojure/deps.edn
like this:
{
:aliases
{:new {:extra-deps {seancorfield/clj-new
{:mvn/version "0.7.6"}}
:main-opts ["-m" "clj-new.create"]}}
}
Create a Clojure CLI tools project using the clj-new
app template
clj -A:new app myname/myapp
cd myapp
clj -m myname.myapp
The app
template creates a couple of tests to go along with the sample code. We can use the cognitec test runner to run these tests using the :test
alias
clj -A:test:runner
clj-new
currently has the following built-in templates:
app
-- a deps.edn
project with sample code, tests and the congnitect test runner, clj -A:test:runner. This project includes :gensys
directive, so can be run as an application on the command line via clj -m
lib
-- the same as the app
template, but without the :gensys
directive as this is mean to be a library. template
– the basis for creating your own templates.
figwheel-main
Use the figwheel-main template to create a project for a simple Clojurescript project, optionally with one or reagent, rum or om libraries.
Defining aliases
An alias is a way to add optional configuration to your project which is included when you use the alias name when running clojure
or clj
.
We will cover examples of using aliases as we discover more about Clojure CLI tools. For now, take a look at Clojure CLI and deps.edn - video by Sean Corfield
Multiple versions of Clojure CLI tools
Installing CLI tools downloads a tar file that contains the installation files, the executables, man pages, a default deps.edn
file, an example-deps.edn
and a Jar file.
The jar file is installed in a directory called libexec
is not removed when installing newer versions of the Clojure CLI tools, so you may find multiple versions inside the libexec
directory.
Summary
Despite the seemingly stripped-down set of options available in deps.edn (just :paths, :deps, and :aliases), it turns out that the :aliases
feature really provides all you need to bootstrap a wide variety of build tasks directly into the clojure command. The Clojure community is building lots of tools on top of Clojure CLI tools to provide richer features that can simply be added as an alias.
What I really like best about this approach is that I can now introduce new programmers to Clojure using command line conventions that they are likely already familiar with coming from many other popular languages like perl, python, ruby, or node.
References
- Clojure CLI webapp template - @lambdatronic
- A sample full stack Clojure CLI project - @oakes
- JUXT Edge - a clojure application foundation - @juxt
Thank you. @jr0cket