【发布时间】:2011-04-23 19:20:15
【问题描述】:
Cake 和 Leiningen 有什么区别?
【问题讨论】:
-
Cake 正在与 Leiningen 合并。链接:groups.google.com/forum/#!topic/leiningen/WnnQIZipG5E
Cake 和 Leiningen 有什么区别?
【问题讨论】:
这个答案继续引起人们的兴趣,大概是作为 StackOverflow 中 Leiningen 的参考,因此现在对其进行了重大编辑以在 2014 年对其进行更新。
Leiningen 和 Cake 早在 2011 年就合并了。Leiningen(第 2 版)现在是事实上的 Clojure 自动化工具。
Leiningen是 Clojure 的构建工具和依赖项管理器,其中包括使用适当配置的类路径以及从 maven 存储库和/或社区以自动方式获取的所有 java 和 clojure 依赖项设置交互式 REPL 的能力基于Clojars。
Cake 与 Leiningen 非常相似(当时使用相同的 project.clj 文件格式),但试图通过在后台保持持久 JVM 来避免大量启动开销。在基于 REPL 的迭代开发的典型过程中,由于持久过程中的累积状态(旧的函数定义等),这反应更快,但换取了 bug 的便利。事实证明这是一笔不划算的交易。
在 Leiningen 的经验和对更快启动时间的持续渴望导致了许多加快速度的建议和方法:https://github.com/technomancy/leiningen/wiki/Faster
【讨论】:
截至 2011 年 11 月 15 日,公告 cake and lein merge
【讨论】:
正如 Alex 所说,最显着的区别是命令行的速度。 Cake 使用持久的 JVM,因此只有在您第一次在项目中运行任务时才会遇到 jvm 启动开销。如果您不使用 emacs + slime + clojure-test-mode,这可以节省大量时间。例如,我的一个项目的一组相当大的测试在 cake 中运行时间为 0.3 秒,而在 lein 中为 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 中执行相同操作,您首先必须在项目中创建一个 leiningen 目录,其中包含 4 个文件: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...
但是 data-load 和 my-compile 都依赖于 code-gen,所以你的实际输出是......
generating code...
compiling...
generating code...
loading test data...
running tests...
你必须记住 code-gen 以防止它被多次运行:
(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 宏中使用/要求另一个。相比之下,Cake 的造型看起来更加整洁简洁。
另一个关键区别是任务的附加方式。假设我们想添加 my-test 作为 cake/lein 内置的 jar 任务的先决条件。在 cake 中,您可以使用 deftask 宏来附加到任务的表单和依赖项。
(deftask jar #{my-test})
Lein 使用 Robert Hooke 附加到任务。这是一个非常酷的库,以每个人最喜欢的自然哲学家的名字命名,但它需要一个宏来保持deftask 的简洁性。
(add-hook #'leiningen.jar/jar (fn [f & args]
(my-test)
(apply f args)))
Cake 还有一个全球项目的概念。您可以将用户特定的开发依赖项(如 swank)添加到 ~/.cake/project.clj 并在您的所有项目中使用它。全局项目还用于在项目之外启动 repl 以进行实验。 Lein 通过支持~/.lein/init.clj 中的每个用户配置和~/.lein/plugins 中的全局插件来实现类似的功能。总的来说,Lein 目前拥有比 cake 更丰富的插件生态系统,但 cake 包含更多开箱即用的任务(war、deploy、java 编译、native dependencies、clojars 和 swank)。 Cljr 也可能值得一试,它本质上只是一个带有包管理器的全球项目,但没有构建功能(但是我没有使用它的经验)。
正如技术专家指出的那样,真正不可调和的区别在于任务定义。在我(有偏见的)看来,蛋糕处理任务要好得多。当我们开始在 lein 项目中使用协议缓冲区时,对任务依赖模型的需求变得很明显。 Protobufs 是我们所有任务的先决条件,但编译它们真的很慢。我们还有很多相互依赖的任务,所以任何构建都是痛苦的。我也不喜欢我创建的每个任务都需要一个单独的命名空间,因此需要一个额外的 src 文件。开发人员应该创建很多任务,lein 的方法通过创建太多摩擦来阻止这种情况。使用 cake,您可以只使用 project.clj 中的 deftask 宏。
Cake 还很年轻,还在进行中,但它是一个非常活跃的项目。
【讨论】:
主要区别在于任务的实现方式。
Cake 的方法是“函数定义后很难扩展,所以让我们为任务发明一种新机制而不是使用函数”,这导致了 deftask 宏。
Leiningen 的方法是“在定义函数后很难扩展它们,因此我们应该找到一种方法来轻松地做到这一点;这样我们就可以将函数用于任务,也能够扩展非任务的东西”,它让您可以将函数的所有可组合性优势应用到任务中。
【讨论】: