Reputation: 5951
I'm trying to use a function as the value for the :profiles
key in a defproject
form. Starting from a fresh project (lein new app test
) this works fine:
:profiles {}
(as you might hope!). But if I change it to:
:profiles (merge {})
then when I run lein repl
it explodes:
Caused by: java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.util.Map$Entry
I'm confused by this since if I set :profiles
back to the empty map and ask the repl these things are equal:
test.core=> (= {} (merge {}))
true
Where is my misunderstanding? Have I missed something basic? Is this an unfortunate artifact of the defproject
macro? Something else?
(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))
...
So now I can lein foo bin
and lein bar bin
to my heart's content.
Upvotes: 2
Views: 1909
Reputation: 1688
If you unquote
your form Leiningen will execute the form before the project map is evaluated.
So ~(merge {})
should work.
There is a function called unquote-project
in Leiningen
src/leiningen/core/project.clj#L176
"Inside defproject forms, unquoting (~) allows for arbitrary evaluation."
It looks like it looks items to unquote
to allow them to be execute. Based on the comment it looks like this might go away in 3.0 and suggests use read-eval syntax
Note: If you are just trying to merge values of different profiles you should look at the 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"]}]}
Upvotes: 3
Reputation: 29966
You do not merge the profiles manually. They are merged together by lein
either automatically (normal case) or when you use the with-profile
keyword (manual control).
For example, consider this 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"] }
...
)
This project.clj
says that the project always requires both org.clojure/clojure
and tupelo
. During development, the map for :dev
will be merged into the root-level, so :dependencies
will be updated to include both test.check
and criterium
. The :dev
profile values are not included when a uberjar is created, however, so these libs won't be included in the code delivered to users.
Since :sample
is not one of the default profiles, it will only be included if you use a command like:
> lein with-profile sample test
Notice that the leading colon is not included on the command line, although we use a keyword :sample
with the colon in the project.clj
file.
Full details are here: https://github.com/technomancy/leiningen/blob/master/doc/PROFILES.md#default-profiles
and here: https://github.com/technomancy/leiningen/blob/master/sample.project.clj
Having said all this, I normally don't need to use :profiles
. Unless you have something more complex than usual, you should usually just put all of your dependencies at the root level (i.e. the :dependencies
keyword at the first level under (defproject xyz ...)
in the project that lein new app xyz
gives you.
Another suggestion: the word test
is used in many places in a lein project (directory name, file name suffix, and others), so it can be very confusion to name the project itself test
! You'll save yourself (and any other readers) some grief if you choose a unique name like xyz
, joe
, or anything else.
Upvotes: 2