I want to create an uberjar of a leiningen app. My config is:
:uberjar {:omit-source true
:aot :all
:uberjar-name "myapp.jar"
:source-paths ["env/prod/clj" ]
:resource-paths ["env/prod/resources"]}
But upon doing lein uberjar, I find the the files in the project are being compiled, but the compilation is stuck on the file that contains most of the code, for ten minutes and counting. This file doesn't contain more than 140 lines.
TL;DR: never def side-effects
As stated in the comments:
... I just figured that this line: (defonce server (http/start-server server-handler {:port 8982})) is causing the hang.
Don't put stuff like that at top-level.
defonce only means it will not be re-def-ed once it's there (so in this case
it would prevent some "port already in use" error on reloading.
Ways out of that dilemma
Write a function, that starts this server. Then call that from your main. For
development you can run that function from the REPL or you can sprinkle some
reload/restart logic in your user-ns.
Another option could be using delay: it will only execute the code once it gets derefed.
The more "binding of resources" you have to deal with, the more some systematic
approach will give your application a better structure. E.g. take a look at:
weavejester/integrant
stuartsierra/component
tolitius/mount
So why is putting blocking things or side-effects in a def problematic?
The way the Clojure compiler works, is by actually "running" the code. So compile basically is:
enable generation byte code and write it out as .class files
load the namespace and "run" it
This means, that at compile time, the top level side-effects are executed.
Therefor blocking operations in a def, will block compilation (which is quite
obvious), or your CI server will fail to compile, because it can not connect to
the database etc.
A great explanation of how the code generation in Clojure works:
What Are All These Class Files Even About? And Other Stories - Gary Fredericks
Related
I'm using tools.namespace to provide smart reloading of namespaces on the REPL. However, when calling refresh or refresh-all, it throws an error.
user=> (require '[clojure.tools.namespace.repl :as tn])
user=> (tn/refresh)
:reloading (ep31.common ep31.routes ep31.config ep31.application user ep31.common-test ep31.example-test)
:error-while-loading user
java.lang.Exception: No namespace: ep31.config, compiling:(user.clj:1:1)
And it seems to end up in this weird state where (require ep31.config) works without an error, but afterwards the namespace isn't actually defined.
I kind of figured this out, this seems to be a combination of circumstances
there were AOT compiled classes left in target/classes from doing lein uberjar previously
tools.namespace doesn't function correctly when loaded namespaces are AOT compiled
target/classes is by default on the classpath
So long story short, if you did a jar/uberjar build before, then remove target/ and things should start working again.
The question I haven't been able to solve yet is why target/classes is on the classpath to begin with. I'm suspecting it's being added by Leiningen, but haven't found yet where or why it's happening.
I learned this the hard way, documentation for :target-path says (https://github.com/technomancy/leiningen/blob/master/sample.project.clj#L309-L313):
;; All generated files will be placed in :target-path. In order to avoid
;; cross-profile contamination (for instance, uberjar classes interfering
;; with development), it's recommended to include %s in in your custom
;; :target-path, which will splice in names of the currently active profiles.
:target-path "target/%s/"
I guess there has to be legacy reasons that :target-path "target/%s/" isn't the default.
I have defined the following in one ns -
There is a services atom, and another function to add to that atom -
(ns ex.first)
(def services (atom []))
(defn add-service
[fns]
(swap! services conj fns))
In my code in another ns I do this -
(ns ex.second ..)
(add-service [fn1 fn2 fn3])
1) I am assuming that when I run my repl, which compiles the code, it should add the vector to the atom. However when I eval the #services it returns [].
2) The above works if I eval the (add-service [fn1 fn2 fn3]) in the repl.
3) I have also tried using converting the add-service fn to a macro. However still I find the #services to be empty.
So will appreciate if someone can help on these -
1) Why doesnt the add-service call add a vector to the atom on code compilation when -
add-service is defined as a fn.
add-service is defined as a macro.
2) How do I make it work :)
It depends on if you have set :aot to :all in project.clj or not.
If :aot is :all then the function call will execute as soon as repl is started, otherwise you will need to load the ex.second namespace (ex: using use). Loading will cause the ns to be compiled and the corresponding class to be loaded in jvm and the function call will get executed.
Also, the function call doesn't happen at the compile time, it happens when the compiled class (representing the namespace) is loaded by jvm.
UPDATED (based on comment):
If you make it a macro, then also you need to consider the aot thing.
If aot is set to compile the namespace then lein will create a jvm, load your code in it, call clojure compiler, which will read the code, execute the macro and compiles the code, at this time this jvm (which is used for compiling your code) will have services atom filled coz of macro execution, but this jvm was only for compilation. lein will then create another jvm for the run command and load the compiled class in that jvm but this jvm won't have the services filled in coz the classes doesn't have any code that fill it in.
If aot is not set then macro will work because the read,macroexpand,compile process will happen in the lein run jvm but only when you cause loading of the ns.
As far as "without loading the ns" is concern, what you can do is put the ex.second in the :aot of project.clj
I'm trying to create a text based Clojure game (inspired by Land of Lisp).
(def *nodes* {:living-room "you are in the living-room. a wizard is snoring loudly on the couch."
:garden "you are in a beautiful garden. there is a well in front of you."
:attic "you are in the attic. there is a giant welding torch in the corner."})
(defn describe-location [location nodes]
(nodes location))
The code is running in the REPL but if I saved the code to a file and trying to run:
(describe-location :attic *nodes*)
I got:
Exception in thread "main" java.lang.IllegalArgumentException: Wrong
number of args (1) passed to: user$describe-location (wizard-game.clj:
0)
What I'm doing wrong?
Here is the file: http://dl.dropbox.com/u/3630641/wizard-game.clj
You have too many parentheses. Instead of (describe-location(:garden *nodes*)), you want (describe-location :garden *nodes*).
Remember that the name of the function goes after the open paren, not before: you were calling (:garden *nodes*) and then calling describe-location on the result, which failed because describe-location wants two arguments, not one.
one potential problem is that the version of the function that is loaded into the repl in the 'user' name space may not be the one you expect, so you may want to (load "wizard-game.clj") into a fresh REPL. though many people are using leiningen for this these days, except for the good number of people using maven directly.
first give your're game a namespace
(ns com.me.myGame
....)
then you can load it into the repl by running
(use 'com.me.myGame)
and call the functions by either their name-space-qualified names
(com.me.myGame/describe-location :attic)
or from the repl switch into that namespace:
(in-ns 'com.me.myGame)
(describe-location :attic)
or you can use leiningen to create your project and name-space automatically.
leiningen is worth it in this case because it just took me longer to write this sentence than to make a project with lein. There are a lot of good tutorials for leiningen.
lein new wizard-game
and then edit src/wizard-game/core.clj. this will let you add dependencies later with out fuss if when the project grows to world-famous-success
I have a problem with the Enclojure REPL and using clojure modules from it. The Load/Change REPL to file/ns works fine with an isolated clojure file, but breaks with a file which has references to another clojure file which I try to use from my project.
Here are the exact steps:
Create a new project.
Create a clojure module foobar.clj (namespace com.acme.foobar)
Define a function which returns a value in foobar.clj:
(ns com.acme.foobar
(:use com.acme.othermodule))
(defn myfunc1 []
"a")
Open a Netbeans IDE REPL
From foobar.clj's context menu select:
Change REPL to file/ns
Load
From REPL call the (myfunc1) function. This works just fine:
com.acme.foobar=> (myfunc1)
"a"
The problems start when when I try to refer to other files from foobar. Here's what I do:
Create a new clojure module othermodule.clj
(ns com.acme.othermodule)
(defn fromothermodule []
"b")
Now try to call this from foobar.clj:
(defn myfunc2 []
(fromothermodule))
From othermodule.clj's context menu I select:
Change REPL to file/ns
Load
To make the REPL realize that there is new module it should be able to run.
I do same things things to foobar.clj which now refers to othermodule.clj, but I get:
CompilerException java.io.FileNotFoundException: Could not locate com/acme/othermodule__init.class or com/acme/othermodule.clj on classpath: (NO_SOURCE_FILE:50)
com.acme.foobar=>
This error message comes from both "Change REPL to file/ns" and "Load"
What am I missing? Should I do some other tricks to make this happen? Even the desperate measure of Run->Clean and Build the main project doesn't help (that would of course make the REPL business pretty painful anyway).
I am using NetBeans 6.7.1 and enclojure-plugin-2009-11-3.nbm.
Got the right solution from Eric Thorsen in the google group:
There are three ways to create the REPL from window-menu. Don't use any of those, instead right
click on the Project and "Start Project REPL". Now the paths are set up accordingly.
My first recommendation is to move from NetBeans/Enclojure to IDEA/La Clojure. JetBrains recently created an open source version of their IDE, and the Clojure plugin works fine in it. Since discovering this, I ditched NetBeans and Enclojure. I find La Clojure a pleasure to work in, but of course your mileage may vary.
Back from when I did this in NetBeans, I seem to remember Enclojure source code resides in a subdirectory called "lib". I think I solved a similar problem by fiddling with directory prefixes on the name of the file to load. Probably something like "../lib/YourName". I managed this by trial and error, so I can't relate the exact rules and syntax.
Two things that might help:
You can run something like (println (System/getProperty "java.user.dir")) to find out where Clojure thinks it's executing from.
You could start, as I did, with using an absolute path until you find your way to the correct directory name. Something like "/home/carl/NetbeansProjects/MyProject/lib/myfile.clj" .
I have a little script
(use
:reload-all
'com.example.package1
'com.example.package2
'com.example.package3
'com.example.testlib)
(run-tests
'com.example.package1
'com.example.package2
'com.example.package3)
that I use to quickly reload everything and fire off the unit tests.
trouble is that each time (deftest ... ) is evaluated as the files are read an additional test is created so after working hard all day each test is now being run 103 times, eek!
There is a flag *load-tests* which determines the behaviour of deftest. (doc deftest) seems to implicate that setting this flag to false could solve your problem.