开发者社区> 开发与运维> 正文



1 What's Leiningen

Leiningen is a tool for automating Clojure projects without setting your hair on fire.

If you come from the Java world, Leiningen is "Maven meets Ant without the pain". For Ruby and Python folks, Leiningen combines RubyGems/Bundler/Rake and pip/Fabric in a single tool.

Clojure项目自动化创建, 编译, 打包和部署的工具集. 

It manages various project-related tasks, and can:

  • create new projects
  • manage dependencies for your project
  • run tests
  • run a REPL (without you having to worry about adding dependencies to the classpath)
  • compile Java sources (if any)
  • run the project (if the project is an app)
  • generate a maven-style "pom" file for the project
  • compile and package projects for deployment
  • publish libraries to maven artifact repositories such as Clojars
  • run custom automation tasks written in Clojure (leiningen plug-ins)


2 Basic Usage

help, 帮助手册

Use lein help to see a complete list. lein help $TASK shows the usage for a specific task.

new, 创建新项目

$ lein new [TEMPLATE] NAME # generate a new project skeleton

run, 执行, 以-main函数会入口函数

$ lein run -m my.namespace # run the -main function of a namespace 

test, 执行test namespace目录里面的所有testcase

$ lein test [TESTS] # run the tests in the TESTS namespaces, or all tests

repl, launch repl

$ lein repl # launch an interactive REPL session

uberjar, 将project打包成jar

$ lein uberjar # package the project and dependencies as standalone jar


You can also chain tasks together in a single command by using the do task with comma-separated tasks:

$ lein do clean, test foo.test-core, jar


在project directory内执行命令

Most tasks need to be run from somewhere inside a project directory to work, but some (newhelpsearchversion, andrepl) may run from anywhere.



3 Create Project

使用lein new来创建新的clojure项目, 会在当前目录创建项目目录my-stuff.

$ lein new my-stuff

Generating a project called my-stuff based on the 'default' template.
$ cd my-stuff
$ find .

Namespace Mapping Convention

创建project时, 会自动创建namespace my-stuff.core 
my-stuff.core instead of just my-stuff since single-segment namespaces are discouraged in Clojure. 
Namespace在FP里面比较重要, 比在OO里面重要, 因为对于OO还有class, 其实就是做了一层的namespace封装, 有效的避免冲突 
但是对于FP, 直接就是function, 如果不加合理的namespace, 那就太容易冲突了 
所以这儿使用my-stuff.core, 在project内部需要namespace的分层, 除了core, 肯定应该有其他的子namespace……


4 Project.clj, Configuration

首先可以看出data=code, 配置文件一样可以用clojure文件来写, 只要实现macro, 配置文件直接就可以run, 很牛!


(defproject my-stuff "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.4.0"]])


(defproject myproject "0.5.0-SNAPSHOT"
  :description "A project for doing things."
  :license "Eclipse Public License 1.0"
  :url "http://github.com/technomancy/myproject"
  :dependencies [[org.clojure/clojure "1.4.0"]]
  :plugins [[lein-ring "0.4.5"]])

实际使用的会比这个复杂很多, 参考下面的sample.project.clj

See the sample.project.clj file (also available via lein help sample) for a detailed listing of configuration options.


The project.clj file can be customized further with the use of profiles.




Clojure is a hosted language and Clojure libraries are distributed the same way as in other JVM languages: as JARs.

JARs (.jar files) are basically just .zip files with a little extra JVM-specific metadata. They usually contain .class files (JVM bytecode) and .clj source files, but they can also contain other things like config files, JavaScript files or text files with static data.

必须要指明, project和其他包的依赖关系, 这样leiningen才能自动的下载配置相关包, 以保证project可以被正常的编译和运行.


Artifact IDs, Groups, and Versions

Artifact IDs, 包的名字, 如clojure, hibernate 
Groups, 一般是reversed域名, 如com.cedarsoft.utils.legacy 
Versions, 版本号, 1.3.4 
[com.cedarsoft.utils.legacy/hibernate "1.3.4"]

Clojure libraries often use the same group-id and artifact-id (as with clj-http), in which case you can omit the group-id.

[clj-http "0.5.5"]


Snapshot Versions

Sometimes versions will end in "-SNAPSHOT". This means that it is not an official release but a development build.

所以自动生成的project.clj的版本是带"-SNAPSHOT": my-stuff "0.1.0-SNAPSHOT"

当然尽量不要在dependency里面加snapshot的版本, 除了你知道你在干吗, 并确实需要这样... 
另外的原因是对于snapshot version, leiningen每天都会去更新最新版, 所以有效率问题...

Adding a snapshot dependency to your project will cause Leiningen to actively go seek out the latest version of the dependency daily (whereas normal release versions are cached in the local repository) so if you have a lot of snapshots it will slow things down.

Artifact IDs, Groups和Namespace的关系

Note that some libraries make their group-id and artifact-id correspond with the namespace they provide inside the jar, but this is just a convention. There is no guarantee they will match up at all.


Dependencies are stored in a maven repository (类似PyPi)

There are several popular open source repositories. Leiningen by default will use two of them: clojars.org and Maven Central.

leiningen需要根据配置的dependency, 来自动的找到这些包, 去哪儿找, 默认是clojars.org and Maven Central

可以自行增加third-party repositories 
You can add third-party repositories by setting the :repositories key in project.clj. See the sample.project.clj.


5 Running Code


The REPL is an interactive prompt where you can enter arbitrary code to run in the context of your project.

lein repl和一般的repl相比, 最大的优势是, project context awareness 
对于一般的repl, 能够被require的包都必须要在classpath底下能够找到, 如果没有, 你需要自己去download并配置到classpath里面去 
而lein会自动的管理dependency, 只需要在project.clj里面把需要的denpendency写清楚, lein会自动完成download和classpath配置 
所以只需要在project目录下执行lein repl, 即可以require当前project namespace的所有代码, 以及所有denpendency的库 
这个相当方便, 尤其在测试的时候

需要注意的是, lein repl其实是可以在任何目录下执行的, 但是如果不是project目录, 那就和普通repl没有任何区别.


Lein REPL还有命令提示, 主要有如下命令

user=> (require 'my-stuff.core)
user=> (my-stuff.core/foo “me”)
me Hello, World!

如果在denpendency中加了[clj-http "0.5.5"]

user=> (require '[clj-http.client :as http])
user=> (def response (http/get "http://leiningen.org"))
帮助文档, doc, javadoc, clojuredocs

doc, find-doc显示帮助文档 
javadoc, 显示java的帮助文档

clojuredocs offers more thorough examples from the ClojureDocs site.

ClojureDocs is a community-powered documentation and examples repository for the Clojure programming language.

clojuredocs这个挺管用, 比如你不知道reduce怎么用

user=> (user/clojuredocs reduce)

就会给出很多例子, 也可以直接去网站查, 更好看些, http://clojuredocs.org/clojure_core/clojure.core/reduce

查看源代码, source
user=> (source my-stuff.core/foo)
(defn foo
  "I don't do a whole lot."
  (println x "Hello, World!"))

Lein Run

如果要使用lein run去运行namespace, 必须先实现-main函数, 因为lein run -m会在namespace里面找-main, 如果没有, 会报错

(defn -main
  "I don't do a whole lot."
  [& args]
  (println "Hello, World!"))
$ lein run -m my-stuff.core
Hello, World!

Providing an alternate -m argument will tell Leiningen to look for the -main function in another namespace. 
Setting a default :main in project.clj lets you omit -m

:main my-stuff.core
可以直接运行, 不用-m 

$ lein run 
Hello, World!


6 Tests

We haven't written any tests yet, but we can run the failing tests included from the project template:

$ lein test

lein test my.test.stuff

FAIL in (a-test) (stuff.clj:7)
FIXME, I fail.
expected: (= 0 1)
  actual: (not (= 0 1))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.

Once we fill it in the test suite will become more useful. Sometimes if you've got a large test suite you'll want to run just one or two namespaces at a time; lein test my.test.stuff will do that. You also might want to break up your tests using test selectors; see lein help test for more details.


7 Make Jar

Generally speaking, there are three different goals that are typical of Leiningen projects:

  • An application you can distribute to end-users
  • A library for other Clojure projects to consume
  • A server-side application


The simplest thing to do is to distribute an uberjar. This is a single standalone executable jar file most suitable for giving to nontechnical users. 
最简单的方式, 直接打包成可执行的jar

1. 在project.clj加上:main, 配置-main存在的namespace

2. 在namespace声明中, 加上:gen-class. 不加会报错(Error: Could not find or load main class my_stuff.core)

(ns my-stuff.core
$ lein uberjar
Compiling my.stuff
Compilation succeeded.
Created /home/phil/src/leiningen/my-stuff/target/my-stuff-0.1.0-SNAPSHOT.jar
Including my-stuff-0.1.0-SNAPSHOT.jar
Including clj-http-0.4.1.jar
Including clojure-1.3.0.jar
Including lucene-core-3.0.2.jar
Created /home/phil/src/leiningen/my-stuff/target/my-stuff-0.1.0-SNAPSHOT-standalone.jar

4. 象普通jar一样的执行

$ java -jar my-stuff-0.1.0-standalone.jar Hello world.
Welcome to my project! These are your args: (Hello world.)

Framework (Uber)jars

看下面的例子, 对于hadoop这样framework的包, 无法也无需打包到jar里面 
因为必须实际的运行环境里面有hadoop集群否则, 根本运行不了 
所以使用:provided, 表示需要, 但是不包含在jar里面, 需要使用者保证运行环境符合.

(project example.hadoop "0.1.0"
  :profiles {:provided
              [[org.apache.hadoop/hadoop-core "0.20.2-dev"]]}}
  :main example.hadoop)

Server-side Projects

There are many ways to get your project deployed as a server-side application. Aside from the obvious uberjar approach, simple programs can be packaged up as tarballs with accompanied shell scripts using the lein-tar plugin and then deployed using palletchef, or other mechanisms.


Publishing Libraries

If your project is a library and you would like others to be able to use it as a dependency in their projects, you will need to get it into a public repository.


$ lein deploy clojars


8 Getting Started with Eclipse and Counterclockwise

Eclipse的leiningen插件, 使用更方便


ctrl+alt+s, 执行当前cljr文件, 并启动repl



+ 订阅