Missing `project`.version system property during compilation #318

Closed
jeluard opened this Issue Jun 13, 2014 · 22 comments

Comments

Projects
None yet
3 participants
Contributor

jeluard commented Jun 13, 2014

The project.version system property set by leiningen is not set during ClojureScript compilation.
There's a .version property set though so it looks like the project name is omitted.

Collaborator

cemerick commented Jun 13, 2014

I did a quick look through cljsbuild's interactions with leiningen, and I don't see any place where we explicitly pass through any properties (and thus neglect others). We're just using eval-in-project, innocuously so AFAICT.

I didn't know about this project.version system property; I don't see it set in a REPL…

Contributor

jeluard commented Jun 13, 2014

I can see it in a REPL using leiningen 2.4.0 when evaluating (keys (System/getProperties)). I don't think leiningen sets any other property.

Collaborator

cemerick commented Jun 13, 2014

2.4.0 here as well, but no dice. (System/getProperty "project.version") => nil

A grep on the leiningen sources doesn't show any "project.version" string. Can you point to some docs or source that indicates what should be happening?

Contributor

jeluard commented Jun 13, 2014

Ah yes my bad, project is actually your project name. So you can try something like (System/getProperty "friend.version")

Collaborator

cemerick commented Jun 13, 2014

Ah, OK. Well, I can only presume that somehow the subproject ns is stripping the project name, though I don't immediately see how. I'll take a look at some point, but patch welcome in the meantime.

Contributor

jeluard commented Jun 13, 2014

Sure I'll take a look! Some findings so far in case it rings a bell.

From what I understand from here this property is only set when eval-in multimethod is called for :classloader or :leiningen. cljsbuild is probably calling the default one, :subprocess, which does not set this variable.

Not sure if it's an issue leiningen side or why cljsbuild is using the :subprocess variant, if it does.

Collaborator

cemerick commented Jun 13, 2014

Use of the default (:subprocess) is How It's Always Been. :leiningen won't work. :classloader should work, and it should speed things up (reusing lein's JVM, etc), but I'd be really surprised if there weren't unintended (breaking) consequences of making that change.

I've had v2 stuff sitting on a local branch for way too long. I'll get that pushed up, and if a change to :classloader does prove disruptive, then that branch would be the place to work things out.

Contributor

jeluard commented Jun 14, 2014

It looks like starting version 2.4.1 leiningen will have access to the version information from pom.properties at runtime too (see this change).

This issue is probably not relevant anymore.

Contributor

jeluard commented Jun 19, 2014

Now that leiningen 2.4.2 is released version can be accessed from pom.properties. Just add target/classes to source-paths and you can access it from a macro during ClojureScript compilation.

It doesn't work when used with target-path though as target/classes now contains some runtime generated part.
I wonder if this is something that could be improved at lein-cljsbuild level?

Collaborator

cemerick commented Jun 19, 2014

What do you mean by "runtime-generated part"?

Contributor

jeluard commented Jun 19, 2014

When you rely on target-path, e.g. :target-path "target/%s" target/classes becomes target/base+system+user+dev+repl/classes/ with the middle part depending on the task you run (it can also be some random string apparently).
Somehow it is transparent for regular leiningen usages so I was wondering if this is something cljsbuild could handle?

Collaborator

cemerick commented Jun 19, 2014

Oh, right, that. It's called "profile isolation" in the Leiningen docs/changelog.

The big problem with this w.r.t. cljsbuild (and other tasks that produce results on disk) is that it's impossible for non-Leiningen processes (or even other leiningen processes) run later to know where to pick up generated files, since — if cljsbuild were to implicitly resolve :output-to within a profile-isolated :target-path — their location is determined by the profiles used. IIRC from talking with Phil about it prior to it being turned off by default, the motivating case for profile isolation is avoiding problems with AOT compilation, which is (an antipattern itself in most cases that shouldn't be relevant at all when compiling ClojureScript).

I could rant about this at length, but I won't. :-) Short advice: don't add a string interpolation marker to your :target-path value, if you set it at all.

Contributor

jeluard commented Jun 19, 2014

Right, I am using this option when creating an uberjar. I guess I can just move it to a dedicated profile.

Now your remark makes me wonder if I am doing the right thing. Is there an alternative to aot when creating an uberjar? Do you recommend something else?

Collaborator

cemerick commented Jun 20, 2014

If your objective is to make it so that a Clojure program can be run from the command line without mentioning Clojure itself (e.g. java clojure.main -m your.namespace), then I'd much rather either:

  1. Write a very simple entry point in Java with a public static void main that loads the requisite Clojure namespace, resolves the "real" entry point, and invokes it. This can easily compiled via javac via Leiningen, and the rest of your project will not require AOT at all. Or,
  2. There are at least two projects on Github that provide a class like this, along with configuration mechanisms of varying sophistication for controlling which namespaces to load, bindings to set initially, etc. Unfortunately, I can't recall these projects' names.

Anyway, the creation of an uberjar is typically the last step before deployment, somewhere. IMO, that kind of release (or, near-release) kind of task should always include a clean, which subsumes the effects of profile isolation.

Contributor

jeluard commented Jun 20, 2014

Thanks for the details, very helpful as always.

Summary before closing the issue: if you need to access leiningen version information during ClojureScript compilation, use a macro and read pom.properties as detailed in the FAQ (starting 2.4.2).

jeluard closed this Jun 20, 2014

Contributor

danielcompton commented Jul 21, 2015

Running this now, .version is null in the System properties, and there doesn't seem to be a pom.properties resource available at cljsbuild macro expansion time. Is there another way around this?

I'm using leiningen 2.5.0, and cljsbuild 1.0.5.

Collaborator

cemerick commented Jul 21, 2015

The pom.properties is in META-INF, (clojure.java.io/resource "META-INF/maven/group/artifact/pom.properties"). More here: https://github.com/technomancy/leiningen/blob/master/doc/FAQ.md

Contributor

danielcompton commented Jul 21, 2015

Sorry I wasn't very clear in what I tried. I followed the FAQ instructions, but at the time when the ClojureScript macros are being compiled and I try to load the pom.properties file, io/resource returns nil. It seems like this is because the pom.properties file doesn't exist yet, as the same code works running from a Clojure REPL.

Does that sound right that pom.properties wouldn't exist at this point in the cljsbuild process?

Collaborator

cemerick commented Jul 22, 2015

It certainly used to? I can't say I've ever done this (looked for it during CLJS macroexpansion), but others apparently have. Perhaps this is a change since e.g. lein 2.4.2, which was in use just prior to this issue being closed.

Contributor

danielcompton commented Jul 22, 2015

Hmm, @jeluard could you offer any insight on this?

Contributor

jeluard commented Jul 22, 2015

I don't rely on this anymore but I was using a variation of:

(defn read-properties
  [s]
  (let [p (Properties.)]
    (if-let [r (clojure.java.io/resource s)]
      (.load p (clojure.java.io/reader r)))
    (into {} p)))

(defmacro read-configuration
  []
  (read-properties "META-INF/maven/YOUR-GROUP/YOUR-PROJECT/pom.properties"))

It might not work anymore. I don't recommend it anyway, too flaky.

Contributor

danielcompton commented Jul 22, 2015

Hmm, it doesn't seem to work for the current project at build time, though it will happily get the pom.properties of other JARs. I think I'll investigate another solution, thanks for your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment