create multiple uberjars in leiningen - clojure

I want to create a number of uberjars with different main entrypoints from a single codebase. I see you can specify the main namespace as an argument to lein uberjar but I don't see a way to specify the resulting filename or path, so they'll just overwrite one another. Is there a way to override the output filename or path from the command line?
Or is there a better way of doing this? Have separate project files that all reference a central "library" project? If so, what would the physical structure of this look like and how to get it to build?

You can use multiple Leiningen profiles to accomplish what you are talking about.
(defproject project1 "0.1.0-SNAPSHOT"
:description "Something Amazing!"
:dependencies [[org.clojure/clojure "1.5.1"]]
:profiles {:v1 {:main project1.core1
:uberjar-name "uberjar1.jar"}
:v2 {:main project1.core2
:uberjar-name "uberjar2.jar"}
:v3 {:main project1.core3
:uberjar-name "uberjar3.jar"}})
And, you can build them with:
$ lein with-profile v1:v2:v3 uberjar

Here is an annotated reference source where you can find an option for specifying a name of your output jar or uberjar file and any other options that may be set in a project.clj file.
;;; Jar Output
;; Name of the jar file produced. Will be placed inside :target-path.
;; Including %s will splice the project version into the filename.
:jar-name "sample.jar"
;; As above, but for uberjar.
:uberjar-name "sample-standalone.jar"

Related

How to access log4j variables in log4j.properties file

I am unable to access variables in my log4j.properties file from a clojure.clj file.
Specifically, I need to be able to use some clojure logic to see what level my log4j.rootLogger level is set to. Is there a way to reference/import/:require my log4j file within my clj file so that I can use some logic on the variables setup in the log4jfile?
I have the following in my log4j.properties file.
log4j.rootLogger=INFO, stdout
I want to be able to be able to use an if statement in my exceptions.clj file to be able to confirm when the log4j.rootlogger value in my log4j.properties file is set to INFO or OFF or DEBUG.
(if (log4j.rootlogger=INFO) (prn "rootlogger is set to info") (prn "rootlogger is set to something else"))
I gave it a quick try assuming the following:
If you are configuring Log4j using a log4j.properties it means you are using Log4j 1.2 (which has reached end-of-life BTW)
I created a new project with lein new app log4jdemo, added a dependency on the Log4j JAR (got the artifact name from this page):
project.clj
(defproject log4jdemo "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.10.0"]
[log4j/log4j "1.2.17"]]
:main ^:skip-aot log4jdemo.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
Next, I modified the file core.clj generated from the template with the following:
src/log4jdemo/core.clj
(ns log4jdemo.core
(:gen-class)
(:import [org.apache.log4j Logger]))
(defn get-logging-level []
(-> (Logger/getRootLogger)
.getEffectiveLevel
str))
(defn -main
[& args]
(println "Effective level:" (get-logging-level)))
Finally, let's add a configuration file. Note that the properties file needs to be placed in the src folder so that when we bundle the whole thing, it can be found as a resource (I'm using the WARN log level, but you can try any other Level):
src/log4j.properties
log4j.rootLogger=WARN, stdout
Finally, we build the project as an Uberjar and run it:
$ lein uberjar
Compiling log4jdemo.core
Created /tmp/log4jdemo/target/uberjar/log4jdemo-0.1.0-SNAPSHOT.jar
Created /tmp/log4jdemo/target/uberjar/log4jdemo-0.1.0-SNAPSHOT-standalone.jar
$ java -jar target/uberjar/log4jdemo-0.1.0-SNAPSHOT-standalone.jar
log4j:ERROR Could not find value for key log4j.appender.stdout
log4j:ERROR Could not instantiate appender named "stdout".
Effective level: WARN
... and that is correct, the Log4j properties file had been set to WARN in the example (lein run would also work, BTW).

exclude certain clj namespaces from compilation in leiningen

I have a project that works fine using lein run. Now I want to compile it into a standalone jar using lein uberjar. However, there are a couple of source files in my src/projectname/ directory called e.g. playground.clj and stats.clj that I use for experimenting with emacs & the repl, but that I don't want to compile for the final project.
With something like make, I would specify all files that should be compiled. With clojure/leiningen, it seems, all files are compiled by default - how can I exclude files? I haven't found anything in the leiningen docs.
I am currently using :aot :all. Is this the place to change something? Again, I couldn't find detailed documentation on this.
UPDATE:
The suggestions so far haven't worked. What has worked, however, is to include all desired namespaces instead of excluding the ones that should not be compiled. E.g.:
(defproject myproject "version"
;; ...
:profiles {:uberjar {:aot [myproject.data
myproject.db
myproject.util]}})
Have a look at leiningen's sample project.clj, which describes how to use :jar-exclusions or :uberjar-exclusions to exclude arbitrary paths when creating jars (resp. uberjars).
;; Files with names matching any of these patterns will be excluded from jars.
:jar-exclusions [#"(?:^|/).svn/"]
;; Files with names matching any of these patterns will included in the jar
;; even if they'd be skipped otherwise.
:jar-inclusions [#"^\.ebextensions"]
;; Same as :jar-exclusions, but for uberjars.
:uberjar-exclusions [#"META-INF/DUMMY.SF"]
Old question, but I think I found the answer for those coming after me.
I found the answer in the link to the sample leiningen project from #amalloy's answer, except instead of :jar-exclusions I use source-paths, here.
The idea is to create two separate source directories, one for stuff you don't care to spread around and one for stuff you do:
dev-src/<your-project>/playground.clj
dev-src/<your-project>/stats.clj
src/<your-project>/<everything-else>
Then, in your project.clj, include src in source-paths normally, and include emacs-src in a special profile where your want it visible, say the usual :dev profile:
{
;; ...
:source-paths ["src"]
:profiles {
:dev {
:source-paths ["src" "dev-src"]
}
}
}
That way when you're messing around on your machine those files will be in the jar, and when you deploy to clojars or compile with uberjar they will not be included in the jar, nor compiled.
Try this (ns ^:skip-aot my-ns)
You can also do
(ns ^{:skip-aot true} my-ns
(require [...]))
Source

leinigen repl with profile

This question is a follow up to How does one pre-load a clojure file in the leiningen repl?.
My ~/.lein/profiles.clj looks as follows:
{
:user {:source-paths ["C:/Users/username/.lein/src"] }
}
My ~/.lein/src/user.clj might look as follows:
(ns user)
(println "user file loaded")
When I run lein repl within a folder containing a project.clj, then the user.clj file is executed, but when I run lein repl from another folder it doesn't load my user profile. Is there a workaround for this or is this behavior by design? In fact, I know that Leinigen is indeed loading my profile.clj (even when there is no project.clj) because there are also other things inside (taken from the excellent pimp my repl article). Leinigen is just having problems with my additional source-paths setting.
One other question related to this is the fact that I need to specify the full path of the folder for my user.clj file : "C:/Users/username/.lein/src". When I change that to "~/.lein/src" leiningen fails to load my file.
It sounds like you just want some code loaded for your lein repl sessions. This is done with the :init key of the :repl-options in your profiles.clj. You can load-file other files in init if you want to organize in that fashion.
{:user
{:repl-options
{:init (load-file "path-to-file/user.clj")}
...}}
Note: If you are using Windows-style path delimiters /, you'll need to escape them //.

Error building clojure project with lein

I'm not at all familiar with clojure, and I have the source for a project that I'm trying to build. The project has a project.clj file, which google says means I should use the lein build tool. However:
$ lein compile #lein jar does the same thing
Exception in thread "main" java.lang.RuntimeException: java.io.FileNotFoundException: Could not locate testui/core__init.class oCompiling testui.core
r testui/core.clj on classpath
I suspect that project.clj may be broken. core.clj is located in src/com/foodient/semanticanalysis/testui, and project.clj looks like this:
(defproject testui "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.3.0"]
[org.apache.poi/poi "3.8-beta4"]
[gate-clj "1.0.0"]]
:aot [testui.core]
:main testui.core
:run-aliases {:test testui.core/-main})
Any ideas?
If you set up a lein project and the name has that Clojuristic dash in it, like bene-csv (one of mine), then lein new bene-csv creates several directories and ./bene-csv/project.clj. My core.clj is located in ./bene-csv/src/bene_csv/core.clj. Note the dash is dropped in bene_csv in favor of an underscore.
As to your problem more than likely core.clj is not located where lein expects it, which should be ./testui/src/testui/core.clj. I hope this helps.
I think the issue is that the core.clj is not in the right directory. It should be in the src/testui directory.
My guess is that you should change references to your code from
testui.core
to
com.foodient.semanticanalysis.testui.core
The reason for it is that the part of a namespace before the last dot corresponds to a package name (this term comes from java and jvm)
You indicated that your sources are in:
src/com/foodient/semanticanalysis/testui
so the package name is com.foodient.semanticanalysis.testui
You should probably also update the namespace declaration in your clojure source files to match this convention (or move your source to src/testui).
Hope it helps.

What is an elegant way to set up a leiningen project that requires different dependencies based on the build platform?

In order to do some multi-platform GUI development, I have just switched from GTK + Clojure (because it looks like the Java bindings for GTK never got ported to Windows) to SWT + Clojure. So far, so good in that I have gotten an uberjar built for Linux.
The catch, though, is that I want to build an uberjar for Windows and I am trying to figure out a clean way to manage the project.clj file.
At first, I thought I would set the classpath to point to the SWT libraries and then build the uberjar. This would require that I set a classpath to the SWT libraries before running the jar, but I would likely need a launcher script, anyway. However, leiningen seems to ignore the classpath in this instance because it always reports that
Currently, project.clj looks like this for me:
(defproject alyra.mana-punk/character "1.0.0-SNAPSHOT"
:description "FIXME: write"
:dependencies [[org.clojure/clojure "1.2.0"]
[org.clojure/clojure-contrib "1.2.0"]
[org.eclipse/swt-gtk-linux-x86 "3.5.2"]]
:main alyra.mana-punk.character.core)
The relevant line is the org.eclipse/swt-gtk-linux-x86 line. If I want to make an uberjar for Windows, I have to depend on org.eclipse/swt-win32-win32-x86, and another one for x86-64, and so on and so forth.
My current solution is to simply create a separate branch for each build environment with a different project.clj. This seems kinda like using a semi to deliver a single gallon of milk, but I am using bazaar for version control, so branching and repeated integrations are easy. Maybe the better way is to have a project.linux.clj, project.win32.clj, etc, but I do not see any way to tell leiningen which project descriptor to use.
What are other (preferably more elegant) ways to set up such an environment?
Here's a quite elegant solution using Java system properties:
(let [properties (select-keys (into {} (System/getProperties))
["os.arch" "os.name"])
platform (apply format "%s (%s)" (vals properties))
swt (case platform
"Windows XP (x86)" '[org.eclipse/swt-win32-win32-x86 "3.5.2"]
"Linux (x86)" '[org.eclipse/swt-gtk-linux-x86 "3.5.2"])]
(defproject alyra.mana-punk/character "1.0.0-SNAPSHOT"
:description "FIXME: write"
:dependencies [[org.clojure/clojure "1.2.0"]
[org.clojure/clojure-contrib "1.2.0"]
~swt]
:main alyra.mana-punk.character.core))