Skip to main content

A Clojure backend for the Pants build system

Project description

pants-backend-clojure

A Clojure backend for the Pants build system.

This plugin brings first-class Clojure support to Pants, enabling REPL-driven development, automatic dependency inference, linting, formatting, testing, and uberjar packaging within a monorepo-friendly build system.

A handcrafted word from the author

Hello, yes, it's me, a real person. I'm writing this section to you, another real person. Most markdown docs these days are generated by AI. This has its pros and cons. The advantages are obvious. But among the disadvantages is an explosion of useless, overly verbose, impersonal information.

So I'm writing this section by myself to add a personal touch and help you, the human reviewing this project, to gain immediate insight into what's going on with this project instead of having to clone it down and ask Claude to review it.

AI coding tools are great when used properly. I've found them particularly useful in situations where I want to tackle a project in a domain that is largely unfamiliar to me, but I don't have the time to gain deep mastery of everything. That's basically what's happened here. I've written a lot of Clojure code in my life. I've been working with the Pants build tool for a few years now and I've written many internal plugins for it. This is my first attempt at creating something that has use and value to the outside world. I've used Claude to write all of the code.

I'm "dogfooding" this project though and using that experience as my main method of verifying this project and driving it forward. However, I'd very much welcome input from others who see value in this. Feel free to try it out and let me know what you think. I have a project which is 35k lines of Clojure. It outgrew Leiningen sometime ago. I moved it to poly as that seemed to be the best option for multi-module, multi-build Clojure project. However, I never really loved poly. The commands always felt foreign to me. The errors were unhelpful. I ran into situations where it messed with my test classpath in unexplainable ways. The distinction between "base" and "component" felt arbitrary. And the patterns around "interfaces" didn't sit well with me.

I've used the Pants build tool for a few years now with Python. I've loved the way it provides "resolves". Every large software repo I've ever worked in has two problems:

  1. Upgrading parts of the codebase either to new language runtime versions or new third party libraries results in hard or impossible to resolve conflicts with other older, unrelated parts of the codebase. Resolving these issues is usually non-trivial. You typically either avoid upgrading because the amount of resources required to upgrade isn't worth the hassle. Or you put hacks in place to test or compile your code against one version, but in production you end up using a different version. This always leads to bugs and distrust in the codebase.
  2. Unrelated projects end up depending on each other because dependencies must be specified explicitly and at the project level. A relatively minor, unrelated changes in a common project at the bottom of your stack can result in cache misses for everything else in the repo.

Pants addresses both of these issues. Pants registers each source file as a target. It understands dependencies in your codebase at the target level. So changes in one file only mean that the other files / tests / builds with a transitive dependency on that file need cache misses. It also provides "resolves" which you can think of as a combination of your target runtime (i.e. Java 21) and a lockfile of third party dependencies. Every source target belongs to one or more resolves. Suppose you have some common source that needs to work with other first party sources in both Java 8 and Java 21 and against different versions of some way-too-common library like Guava. You'd assign this hypothetical source to both resolves. Its unit tests would run in both resolves. Bugs that might exist in one runtime, but not the other would be caught and surfaced quickly. Also, Pants has the ability to detect first and third party dependencies automatically. So you no longer have to look at a long list of project dependency lines and wonder which ones you can safely remove. Just specify nothing and let Pants figure it out for you.

This is all wonderful and I've felt for awhile now that it was the missing piece for large Clojure projects. So I went out and built it. I'm using it now. I'm incredibly happy with the results.

Both first party and third party dependency inference work. After you run pants generate-lockfiles, Pants automatically analyzes the JARs in your lockfiles and infers third-party Clojure dependencies — no extra steps needed.

Anyhow, back to your regularly scheduled AI generated readme doc:

Features

  • Dependency Inference: Automatically discovers dependencies from require and import forms
  • REPL: Interactive development with nREPL support and rebel-readline
  • Testing: Run tests with clojure.test via pants test
  • Linting: Static analysis with clj-kondo
  • Formatting: Code formatting with cljfmt
  • Packaging: Build uberjars with AOT compilation and direct linking
  • JVM Integration: Works with Pants' JVM support for mixed Clojure/Java projects
  • Provided Dependencies: Maven-style provided scope for excluding runtime dependencies

Installation

Add the plugin to your pants.toml:

[GLOBAL]
pants_version = "2.30.0"
plugins = ["pants-backend-clojure==0.1.0"]
backend_packages = [
    "pants.backend.experimental.java",
    "pants_backend_clojure",
]

[coursier]
repos = [
    "https://repo.clojars.org/",
    "https://repo1.maven.org/maven2",
]

[jvm.resolves]
jvm-default = "locks/jvm-default.lock"

Quick Start

1. Define your dependencies

Create a BUILD file with your Clojure dependencies:

jvm_artifact(
    name="clojure",
    group="org.clojure",
    artifact="clojure",
    version="1.12.0",
)

jvm_artifact(
    name="core-async",
    group="org.clojure",
    artifact="core.async",
    version="1.7.701",
)

2. Add source targets

clojure_sources(
    name="lib",
    dependencies=["//:clojure"],
)

clojure_tests(
    name="tests",
    dependencies=["//:clojure"],
)

3. Generate the lockfile

pants generate-lockfiles

4. Run common commands

# Start a REPL
pants repl src/myapp:lib

# Run tests
pants test ::

# Lint code
pants lint ::

# Format code
pants fmt ::

# Check for errors
pants check ::

# Build an uberjar
pants package src/myapp:deploy

Target Types

clojure_source / clojure_sources

Source files containing application or library code.

clojure_sources(
    name="lib",
    sources=["*.clj", "*.cljc"],
    dependencies=["//:clojure"],
    resolve="jvm-default",
)

clojure_test / clojure_tests

Test files using clojure.test.

clojure_tests(
    name="tests",
    sources=["*_test.clj"],
    dependencies=[":lib", "//:clojure"],
    timeout=120,
)

clojure_deploy_jar

An executable uberjar with AOT compilation.

clojure_deploy_jar(
    name="deploy",
    main="myapp.core",  # Namespace with -main and (:gen-class)
    dependencies=[":lib", "//:clojure"],
    provided=[":servlet-api"],  # Excluded from JAR
)

The main namespace must include (:gen-class) and define a -main function:

(ns myapp.core
  (:gen-class))

(defn -main [& args]
  (println "Hello, World!"))

Goals

Goal Command Description
REPL pants repl path/to:target Start an interactive nREPL session
Test pants test :: Run clojure.test tests
Lint pants lint :: Static analysis with clj-kondo
Format pants fmt :: Format code with cljfmt
Check pants check :: Verify code compiles
Package pants package path/to:jar Build an uberjar

Configuration

Tool versions

Override default tool versions in pants.toml:

[clj-kondo]
version = "2025.10.23"

[cljfmt]
version = "0.14.0"

[nrepl]
version = "1.4.0"

Skip linting/formatting per target

clojure_source(
    name="generated",
    source="generated.clj",
    skip_cljfmt=True,
    skip_clj_kondo=True,
)

System Requirements

  • Python: 3.9+
  • Pants: 2.30.0+
  • JDK: 11+ (17 or 21 recommended)
  • zip/unzip: Required for clj-kondo
    • macOS: Pre-installed
    • Debian/Ubuntu: apt-get install zip unzip
    • RHEL/Fedora: dnf install zip unzip
    • Alpine: apk add zip unzip

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

License

Apache License 2.0 - see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pants_backend_clojure-0.1.1.tar.gz (55.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pants_backend_clojure-0.1.1-py3-none-any.whl (65.8 kB view details)

Uploaded Python 3

File details

Details for the file pants_backend_clojure-0.1.1.tar.gz.

File metadata

  • Download URL: pants_backend_clojure-0.1.1.tar.gz
  • Upload date:
  • Size: 55.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pants_backend_clojure-0.1.1.tar.gz
Algorithm Hash digest
SHA256 9f4372286367f9f828b133a95b33d259cfe05cbb63161bc489f4b4c66e7c1fbd
MD5 7f23bf3cb8d39042840ed6861d8699b4
BLAKE2b-256 b64b5ee6103a3b501081eb412fb6bbda9dd2da0afc15e89a4f514f6c7dd1f798

See more details on using hashes here.

Provenance

The following attestation bundles were made for pants_backend_clojure-0.1.1.tar.gz:

Publisher: publish.yaml on enragedginger/pants_backend_clojure

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pants_backend_clojure-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for pants_backend_clojure-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 affd537500d337b81514902b09c2d764a685e96ea4e76742ee3d61bc697cd129
MD5 f0387ffa2da12b78bd93e51da469a8f7
BLAKE2b-256 1f214f789d7b2d0c2e1f62b2cb045eaeca9f1e6420dd303756523131a3179287

See more details on using hashes here.

Provenance

The following attestation bundles were made for pants_backend_clojure-0.1.1-py3-none-any.whl:

Publisher: publish.yaml on enragedginger/pants_backend_clojure

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page