Cake和Leiningen有什么区别?
答案 0 :(得分:48)
这个答案继续引起人们的兴趣,可能是作为Stackinverflow中Leiningen的参考,因此现在进行了大量编辑,以便在2014年更新它。
Leiningen和Cake于2011年合并.Leiningen(第2版)现在是事实上的Clojure自动化工具。
Leiningen是Clojure的构建工具和依赖项管理器,它包括使用适当配置的类路径设置交互式REPL的能力,以及从maven存储库以自动方式获取的所有java和clojure依赖项和/或基于社区Clojars。
Cake与Leiningen非常相似(当时使用相同的project.clj文件格式)但是试图通过在后台保持持久的JVM来避免大量的启动开销。由于在基于REPL的迭代开发的典型过程中持久化过程中的累积状态(旧函数定义等),这更容易响应,但交易方便了。事实证明这是一个糟糕的交易。
Leiningen的经验以及对更快启动时间的持续渴望导致了许多建议和方法来加快速度:https://github.com/technomancy/leiningen/wiki/Faster
答案 1 :(得分:24)
主要区别在于任务的实施方式。
Cake的方法是“在定义函数之后很难扩展函数,所以让我们为任务发明一种新的机制而不是使用函数”,这导致了deftask宏。
Leiningen的方法是“在定义函数之后很难扩展函数,所以我们应该轻松地做到这一点;这样我们就可以使用函数来完成任务,也可以扩展非任务的东西,“它允许您将任务的所有可组合性优势应用于任务。
答案 2 :(得分:20)
正如Alex所说,最显着的区别是来自命令行的速度。 Cake使用持久性JVM,因此当您第一次在项目中运行任务时,您只会遇到jvm启动开销。如果你没有使用emacs + slime + clojure-test-mode,这可能会节省很多时间。例如,我的一个项目的相当大的一组测试在蛋糕上运行0.3秒,而在莱恩运行时则为11.2秒。
除了性能之外,蛋糕背后的核心思想是依赖任务模型。每个任务仅在给定构建中运行一次,同时考虑依赖关系图中的所有传递先决条件。以下是Cake语法中Martin Fowler's article on rake的示例,它直接在您的project.clj。
中(deftask code-gen
"This task generates code. It has no dependencies."
(println "generating code...")
...)
(deftask compile #{code-gen}
"This task does the compilation. It depends on code-gen."
(println "compiling...")
...)
(deftask data-load #{code-gen}
"This task loads the test data. It depends on code-gen."
(println "loading test data...")
...)
(deftask test #{compile data-load}
"This task runs the tests. It depends on compile and data-load."
(println "running tests...")
...)
要在Leiningen中执行相同操作,首先必须在项目中创建一个包含4个文件的leiningen目录:code_gen.clj,compile.clj,data_load.clj和my_test.clj。
的src / leiningen / code_gen.clj
(ns leiningen.code-gen
"This task generates code. It has no dependencies.")
(defn code-gen []
(println "generating code..."))
的src / leiningen / my_compile.clj
(ns leiningen.my-compile
"This task does the compilation. It depends on code-gen."
(:use [leiningen.code-gen]))
(defn my-compile []
(code-gen)
(println "compiling..."))
的src / leiningen / data_load.clj
(ns leiningen.data-load
"This task loads the test data. It depends on code-gen."
(:use [leiningen.code-gen]))
(defn data-load []
(code-gen)
(println "loading test data..."))
的src / leiningen / my_test.clj
(ns leiningen.my-test
"This task runs the tests. It depends on compile and data-load."
(:use [leiningen.my-compile]
[leiningen.data-load]))
(defn my-test []
(my-compile)
(data-load)
(println "running tests..."))
有人会期待......
generating code...
compiling...
loading test data...
running tests...
但是数据加载和我的编译都依赖于代码生成,所以你的实际输出是......
generating code...
compiling...
generating code...
loading test data...
running tests...
您必须记住代码生成以防止它多次运行:
(ns leiningen.code-gen
"This task generates code. It has no dependencies.")
(def code-gen (memoize (fn []
(println "generating code..."))))
输出:
generating code...
compiling...
loading test data...
running tests...
这就是我们想要的。
如果每个构建只运行一次任务,构建会更简单,更高效,因此我们将其作为蛋糕构建中的默认行为。这种理念已有数十年历史,并由一系列构建工具共享。您仍然可以使用功能,您仍然可以反复调用它们,并且您可以随时使用clojure的全部功能。
Lein只是给你一个简单的函数作为一个任务,但添加了一个约束,它必须在src中拥有它自己的命名空间。如果任务依赖于它,它将位于单独的命名空间中,并且必须在其ns
宏中使用/要求另一个。相比之下,蛋糕构建看起来更整洁,更简洁。
另一个关键区别是如何附加任务。假设我们想添加my-test
作为cake / lein内置jar
任务的先决条件。在蛋糕中,您可以使用deftask
宏附加到任务的表单和依赖项。
(deftask jar #{my-test})
莱恩使用罗伯特胡克追加任务。这是一个非常酷的图书馆,以每个人最喜欢的自然哲学家命名,但它需要一个宏来表达deftask
的简洁性。
(add-hook #'leiningen.jar/jar (fn [f & args]
(my-test)
(apply f args)))
Cake也有全球项目的概念。您可以将用户特定的dev依赖项(如swank)添加到~/.cake/project.clj
,并将其包含在所有项目中。全局项目还用于在项目之外启动repl以进行实验。 Lein通过支持~/.lein/init.clj
中的每用户配置和~/.lein/plugins
中的全局插件来实现类似功能。一般来说,Lein目前拥有比蛋糕更丰富的插件生态系统,但蛋糕包含更多开箱即用的任务(war,deploy,java编译,本机依赖,clojars和swank)。 Cljr也值得一试,它本质上只是一个包管理器的全局项目,但没有构建功能(但我没有经验)。
正如技术指出的那样,真正不可比拟的差异是任务定义。在我的(有偏见的)意见中,蛋糕处理任务要好得多。当我们在项目中使用lein开始使用协议缓冲区时,对任务依赖模型的需求变得明显。 Protobufs是我们所有任务的先决条件,但编译它们的速度非常慢。我们还有很多相互依赖的任务,所以任何构建都很痛苦。对于我创建的每个任务,我也不喜欢单独命名空间的要求,因此也不喜欢另外的src文件。开发人员应该创建很多任务,lein的方法通过创建太多的摩擦来阻止这一点。使用cake,您可以在project.clj中使用deftask宏。
蛋糕还很年轻,正在进行中,但这是一个非常活跃的项目。
答案 3 :(得分:7)