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)
Edit - working solution with Scott's answer:
(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.
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"]}]}
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.
Related
I want to produce a command line version of a clojure library using clojure/tools.cli and lein bin. This works fine except I am getting output to stderr when I run the script. This particular library has functions that override some basic functions, so naturally there are warnings when the clojure code is compiled. I know this is generally a bad idea but after careful consideration in this case I believe it is the best way to go. How do I stop these messages from appearing every time I run the script?
I added slf4j-nop to the dependencies as recommended in the monger documentation to suppress unwanted messages from monger, and this works, but has no effect on the warnings from the clojure compiler.
I have also tried using slf4j-log as detailed here: suppress output from `clojure.tools.logging` but without success.
Here is some of the code:
project.clj
(defproject image-search "0.1.0-SNAPSHOT"
:description "Search for images containing specific metadata"
:url "http://github.com/soulflyer/image-search"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.7.0"]
[org.clojure/tools.cli "0.3.3"]
[org.slf4j/slf4j-nop "1.7.21"]
[image-lib "0.1.1-SNAPSHOT"]]
:main image-search.command-line
:bin {:name "image-search"
:bin-path "~/bin"})
and command_line.clj:
(ns image-search.command-line
(:require [image-search.core :refer [open all-images ifeq ifin ]]
[clojure.tools.cli :refer :all])
(:gen-class))
(def cli-options
[["-c" "--count" "Counts the results"]
["-D" "--database DATABASE" "specifies database to use"
:default "photos"]
["-I" "--image-collection IMAGE-COLLECTION" "specifies the image collection"
:default "images"]
["-K" "--keyword-collection KEYWORD-COLLECTION" "specifies the keyword collection"
:default "keywords"]
["-h" "--help"]
["-i" "--iso ISO" "Search on ISO value"]
["-s" "--shutter SHUTTER-SPEED" "search on SHUTTER-SPEED"]
["-f" "--aperture APERTURE" "search on APERTURE"]
["-y" "--year YEAR" "search on YEAR"]
["-m" "--month MONTH" "search on MONTH"]
["-M" "--model MODEL" "search by camera model"]
["-p" "--project PROJECT" "search photos in PROJECT"]
["-k" "--keyword KEYWORD" "search for KEYWORD"]])
(defn print-count [pics]
(println (count pics)))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(let [{:keys [options arguments errors summary]} (parse-opts args cli-options)
output-function (if (:count options) print-count open)]
(-> all-images
(ifeq :ISO-Speed-Ratings (:iso options))
(ifeq :Year (:year options))
(ifeq :Month (:month options))
(ifin :Project (:project options))
(ifeq :F-Number (:aperture options))
(ifin :Keywords (:keyword options))
(ifin :Model (:model options))
(output-function))))
I run lein bin, then run the executable it produces and get something like this:
(master) image-search: image-search -i 640 -c
WARNING: or already refers to: #'clojure.core/or in namespace: image-search.core, being replaced by: #'image-search.core/or
WARNING: and already refers to: #'clojure.core/and in namespace: image-search.core, being replaced by: #'image-search.core/and
Note that the 2 warning are referring to functions I don't use in the command line version. I am not even including them. They are not in the list given to refer when I :require the library. They are, however, important to the library when I'm using it from the repl or cider. So I really don't want to rename them.
You can remove the warning by adding following to your (ns image-search.core) declaration:
(ns image-search.core
(:refer-clojure :exclude [or and])
With that change you can still use the original Clojure's or and and by fully qualifying them:
`clojure.core/or`
`clojure.core/and`
Without that change the compiler warns you that you are overriding your namespace binding of or and and as they are by default bound to functions from clojure.core/or and clojure/and vars.
I've set up my project with lein-ring to allow hot code reload. It does work when I change any .clj file while the app is running...
How can I make it the same for change in any html, css and js files. (located in resources/public...)
Here is my project.clj set-up:
(defproject ...
:plugins [[lein-cljsbuild "1.0.4"]
[lein-ring "0.9.2"]]
:ring {:handler votepourca.server/handler
:auto-reload? true
:auto-refresh? true}
:resource-paths ["resources" "markup"]
:source-paths ["src/clj"]
...)
EDIT:
I am using Enlive, and apparently, it needs an extra ring wrapper to allow static file reloading: [com.akolov.enlive-reload "0.1.0"]
So in my server.clj/core.clj/handler.clj, I now have this and it works perfectly!
(:require
[ring.middleware.reload :refer [wrap-reload]]
[com.akolov.enlive-reload :refer [wrap-enlive-reload]])
...
(defn app [routes]
(-> routes
(wrap-params)
(wrap-reload)
(wrap-enlive-reload))))
Thank you to "Kolov" the author of this lib https://github.com/kolov/enlive-reload
Add :reload-paths in addition to :auto-reload?/:auto-refresh?. https://github.com/weavejester/lein-ring/blob/master/src/leiningen/ring/server.clj#L25
This is really strange. I had this in my project.clj:
:cljsbuild
{:builds
[{:id "async-tues-id"
:source-paths ["src/async_tues/cljs"]
:compiler {:optimizations :none
:pretty-print false
:output-dir "resources/compiler/"
:output-to "resources/main.js"}}]}
Note that source-map is not turned on here.
Then I had this in my cljs file:
(ns tues.page)
(enable-console-print!)
(println "hello from cljs!")
(println "hi from page.js")
After lein cljsbuild the browser prints to empty lines to the console. I'd add more println statement and it executes them... but there is no text.
I then added this to the project.clj:
:source-map true
After new compilation, the printlns show up with the actual text as desired! Now why would source-map have anything to do with the actual functionality of println??
If you use something like "lein new reagent my-project" to create your project, you'll see something like this inside of prod/cljs/my-project/prod.cljs.
(ns reagent.prod
(:require [reagent.core :as core]))
;;ignore println statements in prod
(set! *print-fn* (fn [& _]))
(core/init!)
To specify no :optimizations, I believe the correct value is :whitespace, not :none. :simple and :advanced are also valid (always use :advanced in production).
It's a bit far fetched, but perhaps turning on :source-maps caused :optimizations to default to a more logical non-production value (since :none is not valid), which somehow caused the overriding of *print-fn* to not occur.
I've got a two profiles defined in project.clj, one locally, one for testing on travis:
:profiles {:dev {:dependencies [[midje "1.6.0"]
[mysql/mysql-connector-java "5.1.25"]]
:plugins [[lein-midje "3.1.3"]]
:user "root" :pass "root"}
:travis {:user "travis" :pass ""}}
I'm hoping to be able to get access to the :user and :pass values in my projects. How can this be done?
Update:
I also want to be able to use the lein with-profile command... so my tests would have:
lein with-profile dev test
-> would use "root", "root" credentials
lein with-profile dev,travis test
-> would use "travis", "" credentials
If you don't need the values defined in project.clj for anything else (IE, you're free to choose the representation) consider Environ.
You can then define the following in your project.clj
:profiles {:dev {:env {:user "root" :pass "root"}}}
and read the values:
(use 'environ.core)
(def creds
{:user (env :user)
:pass (env :pass)})
This has the advantage that you can also specify the values using environment variables and system properties.
Leiningen's build file is Clojure code so you can just read it in:
(->> "project.clj" slurp read-string (drop 3) (partition 2) (map vec) (into {})
:profiles :dev)
; => {:dependencies [[midje "1.5.1"] [ring-server "0.2.8"]], :plugins [[lein-midje "3.1.0"]]}
If you need heavier functionalities (such as access to the final project map) then something like configleaf might be better suited.
Another way to manage this (which I've utilized quite often) is to have a separate config file for profile specific data:
example/profiles/travis/example/config.clj:
(ns example.config)
(def user "travis")
(def pass "")
example/dev-resources/example/config.clj:
(ns example.config)
(def user "root")
(def pass "root")
example/src/example/core.clj:
(ns example.core
(:require [example.config :as config]))
(println config/user)
And you need to add the profile specific resource path to your project.clj:
:profiles {:travis {:resource-paths ["profiles/travis/"]}}
I'd like to have two main classes (or more) with leiningen, and then be able to choose which one at the java command line. For example I have:
(ns abc (:gen-class))
(defn -main [] (println "abc"))
(ns def (:gen-class))
(defn -main [] (println "def"))
With a project.clj having:
(defproject my-jar "0.0.1"
:description "test"
:dependencies [
]
:main abc)
Then I build with lein uberjar, and run:
java -cp my-jar-0.0.1-standalone.jar abc
java -cp my-jar-0.0.1-standalone.jar def
I get it that when I specified :main abc in the project.clj it was calling that out as the main-class in the manifest, but I couldn't get it to run without putting something. But either way when I try to run the 'def' main, I get a class not found:
Exception in thread "main" java.lang.NoClassDefFoundError: def
This works at least with leiningen 2.0+
(defproject my-jar "0.0.1"
:description "test"
:dependencies [
]
:profiles {:main-a {:main abc}
{:main-b {:main def}}
:aliases {"main-a" ["with-profile" "main-a" "run"]
"main-b" ["with-profile" "main-b" "run"]})
Then you can run each main like so:
lein main-a
lein main-b
Which expands to this:
lein with-profile main-a run
lein with-profile main-b run
I'm using this in one of my projects and it works perfectly.
I added :aot [abc def] to the project.clj to generate compiled code and it worked.
What worked for me in both lein 2.7.0's run task as well as from the resulting uberjar is as follows...
project.clj:
(defproject many-mains "0.1.0-SNAPSHOT"
:description "Project containing multiple main methods"
:dependencies [[org.clojure/clojure "1.8.0"]]
:main nil
:target-path "target/%s"
:profiles {:main-abc {:main many-mains.abc}
:main-def {:main many-mains.def}
:main-ghi {:main org.rekdev.mm.ghi}
:core {:main many-mains.core}
:uberjar {:aot :all}})
For source like...
$ cat src/many_mains/abc.clj
(ns many-mains.abc
(:gen-class))
(defn -main
""
[& args]
(println "Hello, from many-mains.abc!"))
This lets lein run work like...
$ lein with-profile main-abc run
Hello, from many-mains.abc!
From the command line the '-' in many-mains needs to become a '_' which makes it a legal Java classname.
$ java -cp target/uberjar/many-mains-0.1.0-SNAPSHOT-standalone.jar many_mains.abc
Hello, from many-mains.abc!
There seems to have been some behavior changes between Lein 2.7.0 and prior around the effect of :main nil on the MANIFEST.MF. What I've got here works like a champ in Lein 2.7.0. The full source is at https://github.com/robertkuhar/many-mains