动态 leiningen :profiles
Dynamic leiningen :profiles
我正在尝试使用函数作为 defproject
表单中 :profiles
键的值。从一个新项目 (lein new app test
) 开始工作正常:
:profiles {}
(如您所愿!)。但是如果我把它改成:
:profiles (merge {})
然后当我 运行 lein repl
它爆炸了:
Caused by: java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.util.Map$Entry
我对此感到困惑,因为如果我将 :profiles
设置回空地图并询问 repl 这些东西是相等的:
test.core=> (= {} (merge {}))
true
我的误会在哪里?我错过了一些基本的东西吗?这是 defproject
宏的不幸产物吗?还有别的吗?
(clojure 1.8.0, leiningen 2.7.1, java 1.8.0_102)
编辑 - 斯科特回答的有效解决方案:
(def project-name 'myproj)
(def mains ["foo" "bar"])
...
(defn- lein-alias [main]
{ main ["with-profile" main] })
(defn- lein-profile [main]
(let [jar (str main ".jar")
entry `~(str project-name "." main)]
{(keyword main) {:main entry
:bin {:name main}
:jar-name jar
:uberjar-name jar}}))
(defproject project-name "0.1.0"
...
:profiles ~(apply merge (concat (map lein-profile mains) {:uberjar {:aot :all}}))
:aliases ~(apply merge (map lein-alias mains))
...
所以现在我可以 lein foo bin
和 lein bar bin
尽情享受了。
您没有手动合并配置文件。它们由 lein
自动(正常情况)或在您使用 with-profile
关键字(手动控制)时合并在一起。
例如,考虑这个 project.clj
:
(defproject xyz "0.1.0-SNAPSHOT"
:dependencies [ [org.clojure/clojure "1.8.0"]
[tupelo "0.9.19"] ]
:profiles {:dev {:dependencies [ [org.clojure/test.check "0.9.0"]
[criterium "0.4.4"] ] }
:sample {:dependencies [medley "0.8.2"] }
...
)
这个 project.clj
表示项目 always 需要 org.clojure/clojure
和 tupelo
。在开发过程中,:dev
的地图将合并到 root-level 中,因此 :dependencies
将更新为包括 test.check
和 criterium
。但是,创建 uberjar 时不包含 :dev
配置文件值,因此这些库不会包含在交付给用户的代码中。
由于 :sample
不是默认配置文件之一,只有在您使用如下命令时才会包含它:
> lein with-profile sample test
请注意,尽管我们在 project.clj
文件中使用关键字 :sample
和冒号,但前导冒号并未包含在命令行中。
完整的细节在这里:https://github.com/technomancy/leiningen/blob/master/doc/PROFILES.md#default-profiles
这里:https://github.com/technomancy/leiningen/blob/master/sample.project.clj
说了这么多,我一般不需要用:profiles
。除非你有比平常更复杂的东西,否则你通常应该将所有依赖项放在根级别(即 lein new app xyz
项目中 (defproject xyz ...)
下第一级的 :dependencies
关键字给你。
另外建议:lein项目中很多地方都用到test
这个词(目录名、文件名后缀等),所以把项目本身命名会很混乱test
!如果您选择一个独特的名称,例如 xyz
、joe
或其他任何名称,您将避免自己(和任何其他读者)的痛苦。
如果您 unquote
您的表单 Leiningen 将在 评估项目地图之前 执行表单。
所以 ~(merge {})
应该可以。
Leiningen 中有一个名为 unquote-project
的函数
src/leiningen/core/project.clj#L176
"Inside defproject forms, unquoting (~) allows for arbitrary evaluation."
看起来 unquote
允许执行的项目。根据评论,这看起来可能会在 3.0 中消失并建议 use read-eval syntax
注意: 如果您只是想合并不同配置文件的值,您应该查看 Profiles documentation About Merging and Composite Profiles
Composite Profiles
Sometimes it is useful to define a profile as a combination of other profiles. To do this, just use a vector instead of a map as the profile value. This can be used to avoid duplication:
{:shared {:port 9229, :protocol "https"}
:qa [:shared {:servers ["qa.mycorp.com"]}]
:stage [:shared {:servers ["stage.mycorp.com"]}]
:production [:shared {:servers ["prod1.mycorp.com", "prod1.mycorp.com"]}]}
我正在尝试使用函数作为 defproject
表单中 :profiles
键的值。从一个新项目 (lein new app test
) 开始工作正常:
:profiles {}
(如您所愿!)。但是如果我把它改成:
:profiles (merge {})
然后当我 运行 lein repl
它爆炸了:
Caused by: java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.util.Map$Entry
我对此感到困惑,因为如果我将 :profiles
设置回空地图并询问 repl 这些东西是相等的:
test.core=> (= {} (merge {}))
true
我的误会在哪里?我错过了一些基本的东西吗?这是 defproject
宏的不幸产物吗?还有别的吗?
(clojure 1.8.0, leiningen 2.7.1, java 1.8.0_102)
编辑 - 斯科特回答的有效解决方案:
(def project-name 'myproj)
(def mains ["foo" "bar"])
...
(defn- lein-alias [main]
{ main ["with-profile" main] })
(defn- lein-profile [main]
(let [jar (str main ".jar")
entry `~(str project-name "." main)]
{(keyword main) {:main entry
:bin {:name main}
:jar-name jar
:uberjar-name jar}}))
(defproject project-name "0.1.0"
...
:profiles ~(apply merge (concat (map lein-profile mains) {:uberjar {:aot :all}}))
:aliases ~(apply merge (map lein-alias mains))
...
所以现在我可以 lein foo bin
和 lein bar bin
尽情享受了。
您没有手动合并配置文件。它们由 lein
自动(正常情况)或在您使用 with-profile
关键字(手动控制)时合并在一起。
例如,考虑这个 project.clj
:
(defproject xyz "0.1.0-SNAPSHOT"
:dependencies [ [org.clojure/clojure "1.8.0"]
[tupelo "0.9.19"] ]
:profiles {:dev {:dependencies [ [org.clojure/test.check "0.9.0"]
[criterium "0.4.4"] ] }
:sample {:dependencies [medley "0.8.2"] }
...
)
这个 project.clj
表示项目 always 需要 org.clojure/clojure
和 tupelo
。在开发过程中,:dev
的地图将合并到 root-level 中,因此 :dependencies
将更新为包括 test.check
和 criterium
。但是,创建 uberjar 时不包含 :dev
配置文件值,因此这些库不会包含在交付给用户的代码中。
由于 :sample
不是默认配置文件之一,只有在您使用如下命令时才会包含它:
> lein with-profile sample test
请注意,尽管我们在 project.clj
文件中使用关键字 :sample
和冒号,但前导冒号并未包含在命令行中。
完整的细节在这里:https://github.com/technomancy/leiningen/blob/master/doc/PROFILES.md#default-profiles
这里:https://github.com/technomancy/leiningen/blob/master/sample.project.clj
说了这么多,我一般不需要用:profiles
。除非你有比平常更复杂的东西,否则你通常应该将所有依赖项放在根级别(即 lein new app xyz
项目中 (defproject xyz ...)
下第一级的 :dependencies
关键字给你。
另外建议:lein项目中很多地方都用到test
这个词(目录名、文件名后缀等),所以把项目本身命名会很混乱test
!如果您选择一个独特的名称,例如 xyz
、joe
或其他任何名称,您将避免自己(和任何其他读者)的痛苦。
如果您 unquote
您的表单 Leiningen 将在 评估项目地图之前 执行表单。
所以 ~(merge {})
应该可以。
Leiningen 中有一个名为 unquote-project
的函数
src/leiningen/core/project.clj#L176
"Inside defproject forms, unquoting (~) allows for arbitrary evaluation."
看起来 unquote
允许执行的项目。根据评论,这看起来可能会在 3.0 中消失并建议 use read-eval syntax
注意: 如果您只是想合并不同配置文件的值,您应该查看 Profiles documentation About Merging and Composite Profiles
Composite Profiles
Sometimes it is useful to define a profile as a combination of other profiles. To do this, just use a vector instead of a map as the profile value. This can be used to avoid duplication:
{:shared {:port 9229, :protocol "https"}
:qa [:shared {:servers ["qa.mycorp.com"]}]
:stage [:shared {:servers ["stage.mycorp.com"]}]
:production [:shared {:servers ["prod1.mycorp.com", "prod1.mycorp.com"]}]}