1. 简介
Leiningen 是一个现代化的 Clojure 项目构建工具,它完全用 Clojure 编写和配置。
它的工作方式与 Maven 类似,提供了一种声明式的配置方式来描述项目结构,而不需要手动指定每一步执行细节。
接下来我们将快速上手 Leiningen,看看如何用它来构建 Clojure 项目。
2. 安装 Leiningen
Leiningen 提供了独立可执行版本,同时也支持多种操作系统的包管理器安装方式。
你可以从以下链接下载适用于不同平台的独立脚本:
✅ 下载完成后赋予执行权限,然后就可以使用了。
首次运行脚本时,Leiningen 会自动下载其余组件并缓存起来:
$ ./lein
Downloading Leiningen to /Users/user/.lein/self-installs/leiningen-2.8.3-standalone.jar now...
.....
Leiningen is a tool for working with Clojure projects.
Several tasks are available:
.....
Run `lein help $TASK` for details.
.....
3. 创建新项目
安装完成后,可以通过 lein new
命令创建新项目。该命令基于模板生成项目结构:
- app – 用于创建应用程序
- default – 用于创建通用项目结构,通常用于库开发
- plugin – 用于创建 Leiningen 插件
- template – 用于创建新的 Leiningen 模板
例如,创建一个名为 “my-project” 的应用:
$ ./lein new app my-project
Generating a project called my-project based on the 'app' template.
生成的项目包含如下内容:
- 构建定义文件:
project.clj
- 源码目录:
src
(含初始源文件src/my_project/core.clj
) - 测试目录:
test
(含初始测试文件test/my_project/core_test.clj
) - 文档文件:
README.md
,LICENSE
,CHANGELOG.md
,doc/intro.md
构建定义文件 project.clj
内容如下:
(defproject my-project "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.9.0"]]
:main ^:skip-aot my-project.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
📌 文件中说明了项目的名称、版本、依赖、主命名空间等信息。
⚠️ 注意:命名空间使用的是 my-project.core
,对应文件是 my_project/core.clj
。Clojure 不推荐使用单段命名空间(类似 Java 中的顶层类)。
此外,文件名中的连字符 -
被替换为下划线 _
,这是由于 JVM 对文件名中连字符的兼容性问题。
初始代码非常简单:
(ns my-project.core
(:gen-class))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
📌 Clojure 本身只是项目的一个依赖项,这使得我们可以轻松切换 Clojure 版本,甚至在同一个系统中运行多个不同版本的 Clojure 项目。
4. 构建与运行
项目创建后,我们当然要能构建、运行、打包分发,下面来看具体操作。
4.1. 启动 REPL
进入项目目录后,使用 lein repl
启动 REPL 环境。此时所有项目源码和依赖都会自动加入 classpath,并默认进入主命名空间:
$ lein repl
nREPL server started on port 62856 on host 127.0.0.1 - nrepl://127.0.0.1:62856
[]REPL-y 0.4.3, nREPL 0.5.3
Clojure 1.9.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_77-b03
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
my-project.core=> (-main)
Hello, World!
nil
📌 此时执行的 (-main)
函数就是我们之前定义的那个。
4.2. 运行应用程序
对于使用 lein new app
创建的应用程序项目,可以直接使用 lein run
命令运行:
$ lein run
Hello, World!
📌 它会自动执行 project.clj
中定义的 :main
命名空间下的 -main
函数。
4.3. 构建库(Library)
对于使用 lein new default
创建的库项目,可以使用以下命令构建 JAR 文件:
lein jar
:将 JAR 文件输出到本地target
目录lein install
:构建 JAR 并生成pom.xml
,然后安装到本地 Maven 仓库
$ lein jar
Created /Users/user/source/me/my-library/target/my-library-0.1.0-SNAPSHOT.jar
$ lein install
Created /Users/user/source/me/my-library/target/my-library-0.1.0-SNAPSHOT.jar
Wrote /Users/user/source/me/my-library/pom.xml
Installed jar and pom into local repo.
4.4. 构建 Uberjar
对于应用程序项目,可以使用 lein uberjar
构建一个包含所有依赖的可执行 JAR(即 Uberjar):
$ lein uberjar
Compiling my-project.core
Created /Users/user/source/me/my-project/target/uberjar/my-project-0.1.0-SNAPSHOT.jar
Created /Users/user/source/me/my-project/target/uberjar/my-project-0.1.0-SNAPSHOT-standalone.jar
📌 其中 -standalone.jar
包含了所有依赖,可以直接运行:
$ java -jar target/uberjar/my-project-0.1.0-SNAPSHOT-standalone.jar
Hello, World!
5. 依赖管理
虽然我们可以自己实现所有功能,但复用社区已有库更高效。Leiningen 提供了强大的依赖管理能力。
5.1. 添加依赖
在 project.clj
文件中添加依赖项即可。依赖以向量形式表示,包含库名和版本:
:dependencies [[org.clojure/clojure "1.9.0"] [clj-json "0.5.3"]]
📌 添加后,Leiningen 会自动下载依赖并加入 classpath。
启动 REPL 后即可使用:
$ lein repl
Retrieving clj-json/clj-json/0.5.3/clj-json-0.5.3.pom from clojars
Retrieving clj-json/clj-json/0.5.3/clj-json-0.5.3.jar from clojars
...
my-project.core=> (require '(clj-json [core :as json]))
nil
my-project.core=> (json/generate-string {"foo" "bar"})
"{\"foo\":\"bar\"}"
也可以在项目源码中使用:
(ns my-project.core
(:gen-class))
(require '(clj-json [core :as json]))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println (json/generate-string {"foo" "bar"})))
运行效果如下:
$ lein run
{"foo":"bar"}
5.2. 查找依赖
使用 lein search
可以搜索可用的依赖库:
$ lein search json
Searching central ...
[com.jwebmp/json "0.63.0.60"]
[com.ufoscout.coreutils/json "3.7.4"]
...
Searching clojars ...
[cheshire "5.8.1"]
JSON and JSON SMILE encoding, fast.
[json-html "0.4.4"]
Provide JSON and get a DOM node with a human representation of that JSON
[ring/ring-json "0.5.0-beta1"]
Ring middleware for handling JSON
[clj-json "0.5.3"]
Fast JSON encoding and decoding for Clojure via the Jackson library.
📌 会搜索 Maven Central 和 Clojars 等仓库,返回可用于 project.clj
的依赖声明。
6. 单元测试
Clojure 内置了单元测试支持,Leiningen 也提供了测试工具。
默认项目中包含一个失败的测试用例 test/my_project/core_test.clj
:
(ns my-project.core-test
(:require [clojure.test :refer :all]
[my-project.core :refer :all]))
(deftest a-test
(testing "FIXME, I fail."
(is (= 0 1))))
📌 该测试故意失败(0 ≠ 1)。
运行测试:
$ lein test
lein test my-project.core-test
lein test :only my-project.core-test/a-test
FAIL in (a-test) (core_test.clj:7)
FIXME, I fail.
expected: (= 0 1)
actual: (not (= 0 1))
Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
Tests failed.
修改为正确断言后:
(deftest a-test
(testing "This should pass."
(is (= 1 1))))
再次运行:
$ lein test
lein test my-project.core-test
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
📌 输出简洁明了,失败时一目了然。
也可以指定命名空间运行测试:
$ lein test my-project.core-test
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
$ lein test my-project.unknown
Ran 0 tests containing 0 assertions.
0 failures, 0 errors.
7. 总结
本文介绍了 Leiningen 的基本使用方式,包括项目创建、依赖管理、构建、运行和测试。它适用于 Clojure 应用程序和库的开发。
不妨在下一个 Clojure 项目中试试 Leiningen,体验它的便捷与高效。