Leiningen is a build automation and dependency management tool for the Clojure programming language, written in Clojure itself. It simplifies project configuration, dependency resolution, task automation, and project templating, drawing inspiration from Maven and Ant while providing a more streamlined, Clojure-centric workflow. Leiningen is widely used in the Clojure ecosystem for managing everything from small scripts to large-scale applications.
1 Introduction
1.1 History
Leiningen was created by Phil Hagelberg and first released in 2009. The tool emerged from the need for a simpler build system tailored to Clojure’s REPL-driven development style. Early versions focused on dependency management and basic task execution. Over time, the project gained community contributions, including support for profiles, plugins, and project templates. Leiningen’s name is a play on the word “lein” (the command-line interface) and the German surname “Leiningen,” a nod to the short story “Leiningen Versus the Ants.” The tool has undergone several major versions, with Leiningen 2 introducing profiles and improved dependency resolution.
1.2 Design Philosophy
Leiningen emphasizes simplicity and convention over configuration. Its design draws from the Unix philosophy: doing one thing well (dependency management) while providing hooks for extensibility through plugins. The project.clj file serves as the central configuration, using Clojure data structures (maps, vectors, keywords) rather than XML. The tool aims to minimize boilerplate, making it easy to start a new project with a single command. REPL integration is a first-class concern, allowing developers to iterate quickly.
1.3 Comparison to Other Build Tools
1.3.1 Maven
Maven is a build tool for Java that uses XML for project configuration (POM files) and follows a rigid lifecycle. Leiningen borrows Maven’s dependency model (repositories, transitive dependencies, version ranges) but replaces XML with Clojure’s data literals. Unlike Maven, Leiningen does not enforce a strict build lifecycle; tasks are invoked explicitly or via simple aliases. Leiningen’s plugin system is also more lightweight, using Clojure namespaces rather than Maven’s artifact-based plugins.
1.3.2 Gradle
Gradle is a Groovy/Kotlin-based build tool that supports custom task graphs and incremental builds. While Gradle offers high flexibility and performance for large projects, its configuration can become complex. Leiningen, in contrast, is simpler to set up for Clojure projects, with less ceremony. Gradle’s strong presence in the Android ecosystem contrasts with Leiningen’s exclusive focus on Clojure. Leiningen also lacks native support for incremental compilation, relying on Clojure’s own compilation model.
1.3.3 Boot
Boot is another Clojure build tool that emphasizes composability and pipeline-style tasks. Boot’s tasks are functions that manipulate a fileset, making it more functional and flexible than Leiningen. However, Boot has a steeper learning curve and a smaller community. Leiningen remains the most widely adopted tool in the Clojure ecosystem due to its simplicity, maturity, and extensive plugin library.
2 Installation and Setup
2.1 Prerequisites (Java, Clojure)
Leiningen requires a Java Runtime Environment (JRE) version 8 or later. It works with the Clojure compiler, which is included as a dependency during project creation. No separate Clojure installation is necessary; Leiningen downloads Clojure as needed.
2.2 Installing Leiningen via Script
Installation is performed using a self-contained shell script. The user downloads the lein script from the official website, places it in a directory in the PATH, and makes it executable. On first run, the script downloads the Leiningen JAR file and supporting packages. On Windows, a batch file (lein.bat) is provided instead.
2.3 Verifying Installation
After installation, running lein version displays the installed version of Leiningen and the Java version. A successful output confirms that the tool is ready.
2.4 Upgrading Leiningen
Leiningen can be upgraded by re-running the install script or by executing lein upgrade. The command fetches the latest stable version and replaces the current installation.
3 Core Concepts
3.1 project.clj Configuration
Every Leiningen project has a project.clj file in its root directory. This file is a Clojure map that defines project metadata, dependencies, and build behavior.
3.1.1 Basic Keys (name, version, dependencies)
The minimal project.clj includes the :name (a project identifier, e.g., my-project), :version (e.g., "0.1.0-SNAPSHOT"), and :dependencies (a vector of vectors, each containing a library name and version string). Additional common keys are :description, :url, :license, and :main (to specify the namespace with a -main function for executable jars).
3.1.2 Profiles and Environments
Profiles allow different configurations for different contexts (development, testing, production). They are defined under the :profiles key as a map of profile names to configuration maps. Profiles can be activated via the lein command line (e.g., lein with-profile test test) or automatically through default profiles like :dev, :test, and :repl. They can override any key, including dependencies, repositories, and JVM options.
3.1.3 Plugins and Custom Tasks
Plugins extend Leiningen’s functionality. They are declared under the :plugins key, similar to dependencies. Plugins may define new tasks, middleware, or hooks. Custom tasks can also be defined inline in project.clj using the :aliases key, which maps a task name to a vector of subtasks or a function.
3.2 Dependency Management
Leiningen resolves dependencies from Maven repositories, downloading JARs and transitive dependencies as needed.
3.2.1 Repository Sources
By default, Leiningen uses Clojars (the community’s artifact repository) and Maven Central. Additional repositories can be specified under :repositories in project.clj, each with a name and URL. Private repositories (e.g., for internal libraries) can be configured with credentials.
3.2.2 Version Ranges and Exclusions
Dependency versions can be pinned (e.g., "1.2.3") or specified as ranges (e.g., "1.2.+"). Leiningen also supports Maven’s exclusion mechanism via the :exclusions key to avoid unwanted transitive dependencies.
3.2.3 Uberjar Assembly
An uberjar is a standalone archive containing all dependencies (including Clojure itself). It is built using the lein uberjar task. The resulting JAR can be run with java -jar. Leiningen handles the merging of conflicting files and the creation of a main-class manifest entry.
3.3 Leiningen Tasks
Tasks are the unit of work in Leiningen, invoked as lein [task-name] [args].
3.3.1 Built-in Tasks (repl, test, run, jar, uberjar)
Commonly used tasks include:
repl: starts an interactive REPL with classpath and dependencies loaded.test: runs unit tests in thetest/directory, reporting results.run: executes the project’s-mainfunction (if defined).jar: creates a non-standalone JAR file for the project.uberjar: creates a standalone JAR with all dependencies.clean: removes compiled class files.deps: prints the dependency tree.
3.3.2 Task Composition and Aliases
Users can define custom alias tasks in project.clj under :aliases. For example, {:aliases {"lint" ["do" ["eastwood"] ["kibit"]]}} creates a lint task that runs two linting tools in sequence. Aliases support arguments and can combine tasks using do, with-profile, or shell commands.
4 Advanced Usage
4.1 Creating Project Templates (lein new)
The lein new task generates a new project skeleton from a template.
4.1.1 Built-in Templates (default, app, compojure-app)
Leiningen ships with several built-in templates:
default: creates a minimal library project structure (src, test, project.clj).app: creates a project with a-mainfunction suitable for standalone applications.compojure-app: generates a web application using the Compojure routing library.
Other templates, such as lein-newnew, can be installed via plugins.
4.1.2 Custom Template Creation
Advanced users can create custom templates by writing a Leiningen plugin that implements the Leiningen.New.Templates protocol. Templates are stored on Clojars and can be used with lein new template-name project-name.
4.2 Plugin Development
Plugins are Clojure libraries that extend Leiningen.
4.2.1 Plugin Anatomy
A plugin is typically a Clojure project with a namespace that defines one or more functions. Each function can be a task, middleware, or a hook. Tasks are functions that take a project map and optional arguments. Middleware functions transform the project map before other tasks run. Plugins are included in the consuming project’s project.clj under :plugins.
4.2.2 Publishing Plugins to Clojars
To publish a plugin, the developer deploys it to Clojars using the lein deploy clojars task (with appropriate credentials). The plugin’s artifact is then available for others to use as a dependency.
4.3 Integration with REPL and Editors
Leiningen integrates with various development environments, providing REPL connectivity and project navigation.
4.3.1 CIDER (Emacs)
CIDER (Clojure Interactive Development Environment that Rocks) works with Leiningen via nREPL. Running lein repl starts an nREPL server; CIDER connects to it, offering features like interactive evaluation, code completion, and debugging.
4.3.2 Cursive (IntelliJ IDEA)
Cursive is an IntelliJ plugin for Clojure that supports Leiningen projects. It can import project.clj for dependency resolution, run tasks from the IDE, and launch a REPL.
4.3.3 Calva (VS Code)
Calva is a Visual Studio Code extension for Clojure. It uses Leiningen’s REPL integration to provide inline evaluation, test running, and project navigation. Calva can start a Leiningen REPL from the command palette.
5 Community and Ecosystem
5.1 Clojars Repository
Clojars is the primary community-maintained artifact repository for Clojure libraries. It hosts thousands of libraries and plugins, including Leiningen plugins. Publishing to Clojars is free and requires authentication.
5.2 Notable Plugins
5.2.1 lein-ring
The lein-ring plugin provides tasks for developing and deploying web applications built with Ring, the Clojure web server abstraction. It includes tasks like lein ring server, lein ring uberwar, and lein ring war.
5.2.2 lein-droid
lein-droid enables Clojure development for Android applications. It handles APK packaging, native library inclusion, and integration with the Android SDK.
5.2.3 lein-try
lein-try allows users to temporarily add a dependency and explore it in a REPL without creating a project. It is useful for quickly evaluating libraries.
5.3 Contributing to Leiningen
Leiningen’s source code is hosted on GitHub under the technomancy/leiningen repository. Contributions include bug fixes, new features, and documentation improvements. The project uses an issue tracker and pull requests. Contributors are encouraged to follow the existing code conventions and include tests.
6 Troubleshooting and Best Practices
6.1 Common Errors
Frequent issues include:
- Classpath conflicts: multiple versions of the same library on the classpath can cause
NoSuchMethodError. Usinglein deps:treehelps identify duplicates. - Missing dependencies: ensure repositories are configured correctly, especially for private or snapshot dependencies.
- Memory errors: Leiningen’s JVM heap can be increased by setting
:jvm-optsinproject.clj(e.g.,"-Xmx2g"). - Plugin conflicts: some plugins may interfere with each other; disable or upgrade plugins to resolve.
6.2 Performance Optimization
To improve build times:
- Use
:checksum :dependenciesto skip re-downloading unchanged artifacts. - Enable AOT compilation only for required namespaces.
- Use
with-profileto limit unnecessary dependency loading. - For large projects, consider using a build cache or splitting into subprojects.
6.3 Project Structure Recommendations
Standard Leiningen projects follow:
src/for source files (namespaces mapping to directory paths).test/for test files.resources/for static files (e.g., configuration, assets).project.cljin the root..lein-env(optional) for environment-specific overrides.
Keeping namespaces consistent with directory structure (e.g., src/my_project/core.clj for my-project.core) aids navigation and avoids compilation errors.