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.
Related
Here is a part of the example taken from core.typed github repo:
(ns typedclj.rps-async
(:require [clojure.core.typed :as t]
[clojure.core.async :as a]
[clojure.core.typed.async :as ta]))
(t/defalias Move
"A legal move in rock-paper-scissors"
(t/U ':rock ':paper ':scissors))
(t/defalias PlayerName
"A player's name in rock-paper-scissors"
t/Str)
(t/defalias PlayerMove
"A move in rock-paper-scissors. A Tuple of player name and move"
'[PlayerName Move])
(t/defalias RPSResult
"The result of a rock-paper-scissors match.
A 3 place vector of the two player moves, and the winner"
'[PlayerMove PlayerMove PlayerName])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Implementation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(t/ann MOVES (t/Vec Move))
(def MOVES [:rock :paper :scissors])
(t/ann BEATS (t/Map Move Move))
(def BEATS {:rock :scissors, :paper :rock, :scissors :paper})
(def BEATS {:a :b})
Note that in the last line I redefined BEATS to {:a :b}, which conflicts its type annotation, but when I eval this in the repl, no error is thrown. I was expecting an error because the latest version of core.typed is said to be able to report type errors at the runtime.
Here is the entire project.clj file:
(defproject typedclj "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.6.0"]
[org.clojure/core.async "0.1.346.0-17112a-alpha" :exclusions [org.clojure/tools.analyzer.jvm]]
[org.clojure/core.typed "0.2.92"]
[clj-http "1.1.2"]
[http-kit "2.1.18"]
]
:repl-options {:nrepl-middleware [clojure.core.typed.repl/wrap-clj-repl]}
:main ^:skip-aot typedclj.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
With core.typed 0.3.0-alpha2 this type error is nicely catched:
Type Error (/private/var/folders/5d/44ctbbln4dsflgzxph1dm8wr0000gn/T/form-init3488589171262628870.clj:36:12) Type mismatch:
Expected: typedclj.rps-async/Move
Actual: (t/Val :b)
in: :b
Type Error (/Users/kaiyin/personal_config_bin_files/workspace/typedclj/src/typedclj/rps_async.clj:36:12) Type mismatch:
Expected: (t/Map typedclj.rps-async/Move typedclj.rps-async/Move)
Actual: (t/HMap :mandatory {:a typedclj.rps-async/Move} :complete? true)
in: {:a :b}
You need to explicitly opt-in to implicit type checking. Change your ns form like so:
(ns ^:core.typed typedclj.rps-async
...)
Yes, it does report errors at runtime when you explicitly ask it to do so. This is slightly different from statically typed languages, where a type error prevents the program from building successfully – it's just an optional "sanity check" here.
Type checking is separate to compilation and must be explicitly run
Use clojure.core.typed/check-ns to type check the current namespace.
This can be done at the REPL.
Note: Global annotations like ann are only valid when found in a
namespace currently being checked with check-ns, or wrapped in a cf.
raw ann in a REPL has no effect. Global annotations should be
top-level forms or inside a (possibly nested) top-level do.
— clojure.typed Quick Guide
In REPL, you should wrap your expressions in cf, so that types are inferred from the given code and printed out. (see this blog post) If you want to type-check code from a namespace defined in source files, use check-ns to type-check the entire namespace.
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/"]}}
Is it possible to grab the project information within the clojure repl?
For example, if there was a project defined:
(defproject blahproject "0.1.2" ....)
When running a repl in the project directory, is there a function like this?
> (project-version)
;=> 0.1.2
While you can parse project.clj yourself, this may be annoying. It's also a lot of work. Instead, you can just do:
(System/getProperty "projectname.version")
Leiningen project files are just Clojure data :)
(-> "/path/to/project.clj" slurp read-string (nth 2))
I use environ (https://github.com/weavejester/environ) which sucks in settings from a number of sources, including system properties. The project version appears as :<project-name>-version:
foobar.repl=> (require '[environ.core :refer [env]])
nil
foobar.repl=> (:foobar-version env)
"0.1.0-SNAPSHOT"
Add the below code to the end of project.clj:
(def project (assoc-in project [:repl-options :init]
`(~'def ~'project-version ~(project :version))))
Now you will have a var called project-version in the initial namespace for the repl.
As described in this discussion.
(ns myproject.example
(:require [clojure.java.io :as io])
(:import java.util.Properties))
(defn get-version [dep]
(let [path (str "META-INF/maven/" (or (namespace dep) (name dep))
"/" (name dep) "/pom.properties")
props (io/resource path)]
(when props
(with-open [stream (io/input-stream props)]
(let [props (doto (Properties.) (.load stream))]
(.getProperty props "version"))))))
(get-version 'myproject) ; => 0.1.0
(get-version 'org.clojure/clojure) ; => 1.3.0
As vemv said, Leiningen project files are just Clojure data. So, it's easy to access your project as an ordinary hash-map:
(->> "project.clj"
slurp
read-string
(drop 2)
(cons :version)
(apply hash-map)
(def project))
If you need this variable only in your repl, you can add it to repl-options to your project.clj:
(defproject yourproject "0.1.0"
:description ""
:url ""
:dependencies [ [org.clojure/clojure "1.4.0"]]
:repl-options { :init (->> "project.clj"
slurp
read-string
(drop 2)
(cons :version)
(apply hash-map)
(def project))})
Now, you have project variable in your repl. So, to access the version of your project you can simply type (:version project).
Of course, you can simply use native Leiningen code to parse you project file:
(defproject yourproject "0.1.0"
:description ""
:url ""
:dependencies [ [org.clojure/clojure "1.4.0"]
[leiningen-core "2.1.3"]]
:repl-options { :init (do (require 'leiningen.core.project)
(def project
(leiningen.core.project/read)))})
But, if you need only the version of your project and nothing more, then it's best to use Ankur's solution.
For a more fully-featured approach, you might want to take a look at the configleaf plugin for Leiningen (https://github.com/davidsantiago/configleaf). It will make the project map, with active profiles merged in, available to project code in a namespace of your choosing.
In case you need to do this from clojurescript you could create a macro (from another clj file) and call it from the cljs code :
;;ex: macro.clj
(defmacro get-project-version []
(System/getProperty "penelope.version"))
;;my_logic_code.cljs
(ns my-logic-code
(:require-macros [macros :as m]))
(def project-version (m/get-project-version))
I have a clojure application called mr1; its project.clj is located in ./mr1/, and mr1.clj is located in ./mr1/src.
I cannot figure out how to structure the directories, namespace, and project.clj file correctly, so I can build a binary using cake bin.
Enclosed are my current project.clj and the head and tail of mr1.clj.
src/mr1.clj loads fine into cake repl and executes. cake bin produces an mr1 file that contains no -main.
(defproject mr1 "0.0.1-SNAPSHOT"
:description "TODO: add summary of your project"
:dependencies [[org.clojure/clojure "1.3.0"]
[org.clojure/tools.cli "0.1.0"]]
:main mr1)
(ns mr1
(:use [clojure.string :only [split]]
[clojure.string :only [join]]))
.
.
.
(defn -main
[& args]
(do
(reset! grid-dim (prompt-for-grid-dim))
(reset! mr1-pos (prompt-for-rover-pos 1))
(let [moves (prompt-for-rover-moves)]
(execute-each-move moves #mr1-pos))
(reset! mr2-pos (prompt-for-rover-pos 2))
(let [moves (prompt-for-rover-moves)]
(execute-each-move moves #mr2-pos))
)
)
As I mentioned in answer to your first question, you have to enable AOT-compilation of your namespace. I've copied that answer here in case if someone would have same problem.
As sample.project.clj file says, :main key should have as an assigned value a namespace which contains -main function. So you should have such function
(defn -main [& args]
(do-things-you-want-to-do-on-program-start))
in your mr1.clj. Also AFAIR if you want to use your program as a standalone jar you have to have this namespace gen-classed. By this I mean that you have to:
Include :gen-class option in your namespace definition like this:
(ns mr1
(:gen-class)
...other options...)
Make the namespace AOT-compiled (AOT stands for Ahead Of Time). To do this you need to specify your namespace in the list of AOT-compiled namespaces in project.clj:
(defproject mr1 "0.0.1-SNAPSHOT"
...other definitions...
:aot [mr1]
:main mr1)
After you've done this, you can use cake to generate executable jar for you.