Structuring Clojure cake build environment - clojure

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.

Related

boot-clj: how to build/launch unit test for class generated with gen-class

In my clojure project I built several java classes using gen-class command. They are [extractor.yaml YAMLExtractor YAMLExtractorFactory]. I wanted now build unit test against those classes but I have error: java.lang.ClassNotFoundException: extractor.yaml.YAMLExtractor when I run test.
File which cause the error is:
yaml_extrator_factory.clj
(ns extractor.yaml-extractor-factory
(:gen-class :name extractor.yaml.YAMLExtractorFactory
:extends org.apache.any23.extractor.SimpleExtractorFactory
:implements [org.apache.any23.extractor.ExtractorFactory]
:init init
:constructors {[] [String org.apache.any23.rdf.Prefixes java.util.Collection String]}
:main false)
(:require [extractor.yaml-extractor])
(:import [extractor.yaml YAMLExtractor]
[org.apache.any23.rdf Prefixes]
[org.apache.any23.extractor SimpleExtractorFactory ExtractorFactory Extractor ExtractionContext ]))
The error occurs only during testing. Whole project is AOT-compilable with no error and it is fine when I build a jar file as well.
The test simple.clj contains head:
(ns extractor.simple
(:use [clojure.tools.logging :as log]
[clojure.java.io :as jio]
[clojure.test :as test])
(:require [extractor.yaml-extractor-factory])
(:import [java.util Arrays]
[extractor.yaml.YAMLExtractorFactory]))
And test which prints CLASSPATH. yaml-extractor-factory was not used.
Test is run with command:
boot aot -a update-classpath run-test -t extractor.simple
where task update-classpath adds (get-env :directories) into classpath
and run-test runs a test. run-test works fine with normal clojure code.
run-test is my task with following content:
(deftask run-test "Run unit tests"
[t test-name NAME str "Test to execute. Run all tests if not given."]
(require '[extractor.simple])
(with-pass-thru [_]
(if (nil? test-name)
(do
(util/info "Run all tests")
(test/run-all-tests))
(do
(util/info (format "Run test: %s" (:test-name *opts*)))
(test/run-tests (symbol (:test-name *opts*)))
))))

How to suppress output to stderr in a clojure program

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.

What is wrong with (use 'korma.db) directive?

When I try to create an uberjar using lein with the following very simple Clojure test file, I get an error
Compiling korma-test.core
Exception in thread "main" java.lang.Exception:
lib names inside prefix lists must not contain periods, compiling:(core.clj:1:1)
and cannot figure out why. I got the (use 'korma.db) from sqlkorma.com's docs section, and tried a require statement as well (not listed in my examples here).
project.clj
(defproject korma-test "0.1.0-SNAPSHOT"
:description "korma db test"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]
[korma "0.3.0-RC5"]]
:main korma-test.core)
core.clj (simplified)
(ns korma-test.core
(:gen-class)
(use 'korma.db)
(require '[clojure.string :as str])
(:import java.util.Date)
)
(defn -main
[& args]
(let [opts (parse-opts args)
start-time (str (Date.))]))
The ns macro uses keywords in place of functions and does take quoted arguments.
(ns korma-test.core
...
(:use korma.db)
(:require [clojure.string :as str])
...)
There is a nice write-up here: http://blog.8thlight.com/colin-jones/2010/12/05/clojure-libs-and-namespaces-require-use-import-and-ns.html

Getting the version of the current clojure project in the repl

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))

ClassNotFound Exception in Lein (can't compile)

I am trying to :gen-class a Servlet
This is my code:
(ns test.test
(:import (java.io PrintWriter) (javax.servlet.http HttpServlet))
(:gen-class :name test.TestServlet :extends javax.servlet.http.HttpServlet))
(defn -doGet[request response]
(let [wrtr (.getWriter response)]
(.println wrtr "hello from clojure")))
it can't be compiled by Lein
it said Exception in thread "main" java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet (Test.clj:1)
I already modified the :library-path in Lein as ":library-path "/home/long/workspaces/spring/LongHDi/war/WEB-INF/lib" but it didn't work.
Do you have any idea why?
I am trying to work with Google App Engine. The servlet class I want to extend is already in the lib folder I specified.
Which version of lein are you using ?
I downloaded jetty from here, and lein version1 worked for me with project.clj
(defproject st2 "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:library-path "/Users/Niko/Downloads/jetty-hightide-8.1.7.v20120910/lib"
:aot [st2.core]
:dependencies [[org.clojure/clojure "1.3.0"]])
with st2.core the same as your code:
(ns st2.core
(:import (java.io PrintWriter) (javax.servlet.http HttpServlet))
(:gen-class :name test.TestServlet :extends javax.servlet.http.HttpServlet))
(defn -doGet[request response]
(let [wrtr (.getWriter response)]
(.println wrtr "hello from clojure")))
If you are using lein2, :library-path is not supported so I suspect you would have to add the dependencies "a-la-maven" and add them to your project dependencies.