Clojure 工具链演进:Leiningen、Clojure CLI 与 deps.edn

全景梳理 Clojure 开发与构建工具的发展历程:从 Leiningen 的常规项目结构到 Clojure CLI / deps.edn 的现代工具链,覆盖项目初始化、依赖解析、REPL 启动、测试运行、AOT 编译、REPL 驱动编辑(Emacs/CIDER、VS Code/Calva、IntelliJ/Cursive)及生产环境打包部署方案。

Clojure 自 2007 年诞生以来,其工具链经历了从 Leiningen 一统天下到 Clojure CLI(tools.deps)双轨并行的长期演进。理解这两种主流工具的差异与选型原则,是每位 Clojure 开发者必须掌握的基础技能。本文将深入对比两者的项目结构、依赖管理、构建生命周期及 IDE 集成,帮助你选择最适合团队场景的工具链。


1. 工具链演进时间线

年份里程碑说明
2009Leiningen 1.0首个广泛使用的 Clojure 构建工具
2011Leiningen 2.0profiles、plugins、模板等现代功能
2014Boot 工具链与 Leiningen 竞争的可编程构建工具
2018Clojure CLI (tools.deps.alpha)官方推出的轻量级依赖管理
2020deps.edn 成为主流生态迁移,Clojure CLI 内置
2023tools.build + Clojure CLI官方推荐的完整构建工具链

当前推荐:新项目首选 Clojure CLI + deps.edn,旧项目可逐步迁移。Boot 基本不再维护。


2. Leiningen:传统而成熟的构建工具

2.1 项目结构

my-project/
├── project.clj          # 项目配置(类似 Maven pom.xml)
├── src/
│   └── my_project/
│       └── core.clj
├── test/
│   └── my_project/
│       └── core_test.clj
├── resources/           # 静态资源
├── dev-resources/       # 开发环境资源
└── target/              # 编译输出

2.2 project.clj 核心配置

(defproject myapp "1.0.0-SNAPSHOT"
  :description "我的 Clojure 应用"
  :url "https://example.com"
  :license {:name "EPL-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}

  ;; 依赖管理(类比 Maven)
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [ring/ring-core "1.10.0"]
                 [compojure "1.7.0"]
                 [org.clojure/java.jdbc "0.7.12"]]

  ;; 开发环境额外依赖
  :profiles {:dev {:dependencies [[org.clojure/tools.namespace "1.4.4"]]
                   :source-paths ["dev"]}
             :uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}}

  ;; 插件扩展
  :plugins [[lein-ring "0.12.6"]]

  ;; 主入口
  :main ^:skip-aot myapp.core

  ;; 测试配置
  :test-selectors {:default (complement :slow)
                   :slow :slow})

2.3 Leiningen 常用命令

命令功能
lein new app myapp创建新应用项目
lein new template mylib创建库项目
lein deps下载依赖
lein repl启动 REPL
lein test运行测试
lein uberjar构建独立可执行 JAR
lein run运行项目 main 函数

2.4 Leiningen 的优缺点

优点

  • 生态极其成熟,社区支持完善
  • 丰富的插件体系(lein-ring、lein-midje 等)
  • 配置文件结构清晰,约定优于配置

缺点

  • 配置格式是 Clojure 代码(DSL),学习成本略高
  • 构建定制需编写插件,不够灵活
  • 启动速度受插件加载影响

3. Clojure CLI + deps.edn:现代轻量级方案

3.1 deps.edn 项目结构

my-project/
├── deps.edn             # 依赖与别名配置
├── build.clj            # 构建脚本(可选)
├── src/
│   └── my_project/
│       └── core.clj
├── test/
│   └── my_project/
│       └── core_test.clj
└── resources/

3.2 deps.edn 核心配置

;; deps.edn — 纯数据(EDN),非代码
{:paths ["src" "resources"]

 ;; Maven 依赖
 :deps {org.clojure/clojure {:mvn/version "1.11.1"}
        ring/ring-core {:mvn/version "1.10.0"}
        metosin/reitit {:mvn/version "0.7.0"}}

 ;; 别名(功能模块化)
 :aliases
 {:dev {:extra-paths ["dev"]
        :extra-deps {org.clojure/tools.namespace {:mvn/version "1.4.4"}}}

  :test {:extra-paths ["test"]
         :extra-deps {io.github.cognitect-labs/test-runner
                      {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
         :main-opts ["-m" "cognitect.test-runner"]
         :exec-fn cognitect.test-runner.api/test}

  :build {:deps {io.github.clojure/tools.build {:git/tag "v0.9.6" :git/sha "8e78bcc"}}
          :ns-default build}

  :uber {:replace-deps {com.github.seancorfield/depstar {:mvn/version "2.1.303"}}
         :exec-fn hf.depstar/uberjar
         :exec-args {:aot true
                     :main-class myapp.core
                     :jar "target/myapp.jar"}}}}

3.3 Clojure CLI 常用命令

命令功能
clojure -Tnew create :name myapp创建新项目
clojure -M:dev以 dev 别名启动 REPL
clojure -M:test运行测试
clojure -T:build jar构建 JAR
clojure -T:uber uber构建 uberjar
clojure -X:dep-tree查看依赖树

3.4 工具安装与版本管理

# macOS(Homebrew)
brew install clojure/tools/clojure

# Linux(官方安装脚本)
curl -L -O https://github.com/clojure/brew-install/releases/latest/download/linux-install.sh
chmod +x linux-install.sh
sudo ./linux-install.sh

# 验证安装
clojure --version
# => Clojure CLI version 1.11.1.1435

4. Leiningen vs deps.edn 深度对比

维度LeiningenClojure CLI + deps.edn
配置格式Clojure DSL(project.clj)纯数据 EDN(deps.edn)
依赖解析本地仓库 + CentralMaven + git deps 混合
插件机制闭包宏定义Git 仓库直接引用(tools)
组合方式profiles 继承aliases 叠加
启动速度较慢(插件加载)更快
IDE 支持IntelliJ/Cursive 原生VS Code/Calva 原生
生态成熟度极高(10+ 年积累)快速增长
AOT 编译lein compileclojure -T:build compile
uberjarlein uberjarclojure -T:uber uber
适用场景传统企业项目新项目、函数式偏好团队

5. tools.build:官方构建工具

5.1 build.cljs 构建脚本

;; build.clj
(ns build
  (:require [clojure.tools.build.api :as b]))

(def lib 'com.example/myapp)
(def version "1.0.0")
(def class-dir "target/classes")
(def basis (b/create-basis {:project "deps.edn"}))
(def uber-file (format "target/%s-%s.jar" (name lib) version))

(defn clean [_]
  (b/delete {:path "target"}))

(defn jar [_]
  (b/write-pom {:class-dir class-dir
                :lib lib
                :version version
                :basis basis
                :src-dirs ["src"]})
  (b/copy-dir {:src-dirs ["src" "resources"]
               :target-dir class-dir})
  (b/jar {:class-dir class-dir
          :jar-file (format "target/%s-%s-lib.jar" (name lib) version)}))

(defn uber [_]
  (clean nil)
  (b/copy-dir {:src-dirs ["src" "resources"]
               :target-dir class-dir})
  (b/compile-clj {:basis basis
                  :src-dirs ["src"]
                  :class-dir class-dir})
  (b/uber {:class-dir class-dir
           :uber-file uber-file
           :basis basis
           :main 'myapp.core}))

5.2 构建命令

# 清理
clojure -T:build clean

# 构建库 JAR
clojure -T:build jar

# 构建可执行 uberjar
clojure -T:build uber

# 运行
java -jar target/myapp-1.0.0.jar

6. REPL 驱动开发:IDE 集成

6.1 Emacs + CIDER(传统最强)

;; .emacs 或 init.el 配置
(require 'cider)

;; 连接 Clojure CLI REPL
(setq cider-clojure-cli-aliases ":dev:test")

;; jack-in 快捷键
;; C-c M-j — 启动 REPL
;; C-c C-e — 求值当前表达式
;; C-c C-k — 加载整个文件
;; C-c C-d C-d — 文档查看

CIDER 特性

  • 完整的调试器(断点、单步跟踪)
  • 代码补全与文档实时显示
  • 测试运行与覆盖率查看
  • Refactoring 工具(重命名、提取函数等)
  • nREPL 协议支持

6.2 VS Code + Calva(现代推荐)

// settings.json
{
  "calva.replConnectSequences": [
    {
      "name": "My Project REPL",
      "projectType": "deps.edn",
      "cljsType": "none",
      "menuSelections": {
        "cljAliases": ["dev", "test"]
      }
    }
  ]
}

Calva 快捷键

  • Ctrl+Alt+C Ctrl+Alt+J — jack-in 启动 REPL
  • Ctrl+Enter — 求值当前 top-level 表达式
  • Alt+Enter — 求值当前光标表达式

6.3 IntelliJ IDEA + Cursive(Java 团队过渡首选)

Settings → Plugins → Marketplace → 搜索 "Cursive" → Install

新建项目:File → New → Project → Clojure → deps.edn

Cursive 优势

  • 与 IntelliJ 完全一致的操作习惯
  • 强大的代码导航与重构
  • 与 Java 代码无缝跳转
  • structural editing(paredit)

6.4 REPL 工作流最佳实践

;; 1. 启动 REPL 和命名空间
(require '[myapp.core :as core])
(in-ns 'myapp.core)

;; 2. 开发函数原型
(defn calculate [x y]
  (* x y))

;; 3. 测试
(calculate 3 4)  ; => 12

;; 4. 修改后重新加载文件
;; C-c C-k(Emacs)或 Reload 按钮(Calva)

;; 5. 使用 tools.namespace 自动重载
(require '[clojure.tools.namespace.repl :refer [refresh]])
(refresh)

7. 生产环境打包与部署

7.1 Uberjar 部署

# 构建
clojure -T:build uber

# 部署
scp target/myapp-1.0.0.jar server:/opt/app/
ssh server << 'EOF'
cd /opt/app
java -Xmx2g -jar myapp-1.0.0.jar
EOF

7.2 Docker 容器化

FROM clojure:temurin-17-tools-deps-alpine AS builder
WORKDIR /app
COPY deps.edn build.clj ./
COPY src ./src
COPY resources ./resources
RUN clojure -T:build uber

FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-Xmx2g", "-Dclojure.compiler.direct-linking=true", "-jar", "app.jar"]

7.3 GraalVM Native Image(极致启动速度)

# 1. 安装 GraalVM
sdk install java 21.0.2-graalce
sdk use java 21.0.2-graalce

# 2. 使用 org.graalvm.buildtools 构建 native image
clojure -T:build native-image

# 3. 结果:秒级启动、低内存占用
./target/myapp    # 启动时间 < 50ms

8. 总结

Leiningen 和 Clojure CLI 是 Clojure 生态的两根支柱。新项目推荐 deps.edn + tools.build,享受纯数据配置和更快的启动速度;维护老项目可以继续使用 Leiningen,其插件生态依然丰富。无论选择哪种工具链,REPL 驱动开发都是 Clojure 最核心的生产力优势。

团队背景推荐工具链
纯 Clojure 新团队Clojure CLI + deps.edn + Calva/Emacs
Java 背景过渡团队Clojure CLI + Cursive(IntelliJ)
传统 Clojure 老项目继续使用 Leiningen,逐步迁移
极端性能要求deps.edn + GraalVM native-image

延伸阅读可参考 Clojure 宏编程深度解析 了解 Leiningen 插件的内部宏机制,以及 Clojure 现代 Web 全栈开发 中 Ring/Leiningen 的整合方案。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「clojure」更多文章

  1. Clojure 并发设计模式:STM、core.async 与 Agent 实战
  2. Clojure 现代 Web 全栈开发:Ring、reitit 与数据库集成
  3. Clojure spec 与测试:数据验证、生成测试与属性驱动