Skip to content

Commit

Permalink
Make javac run in a subprocess. Fixes technomancy#809.
Browse files Browse the repository at this point in the history
If we're not making javac run in a subprocess, libraries the project
depends on which leiningen has another version of will clash. This is
because leiningen append its standalone to java's bootclasspath, which
makes the ToolProvider add these classpaths automatically into the java
compiler it returns. By starting a subprocess without leiningen added to
the bootclasspath, we avoid this library clashing.

UI changes as a result of this patch: Whenever javac fails, the task one
wanted to run will be responsible for aborting Leiningen, whereas javac
did this itself when it discovered that the compilation failed. As we're
running in a subprocess, an abort message such as

    Uberjar aborting because jar/compilation failed: Subprocess failed

will appear, though this will only be appended to the javac error
message and the "Compilation of Java sources (lein javac) failed."
message.

This commit also adds the overhead of starting a subprocess when one
have to compile java sources, but it will not start one if no
.class-files are outdated/not existing.

Not tested on Windows.
  • Loading branch information
hypirion committed Dec 13, 2012
1 parent b4558ec commit 377a98f
Showing 1 changed file with 32 additions and 12 deletions.
44 changes: 32 additions & 12 deletions src/leiningen/javac.clj
@@ -1,7 +1,10 @@
(ns leiningen.javac
"Compile Java source files."
(:require [leiningen.classpath :as classpath]
[leiningen.core.main :as main]
[leiningen.core
[main :as main]
[eval :as eval]
[project :as project]]
[clojure.java.io :as io])
(:import java.io.File
javax.tools.ToolProvider))
Expand Down Expand Up @@ -66,24 +69,41 @@
"-d" (:compile-path project)]
files)))

;; Pure java projects will not have Clojure on the classpath. As such, we need
;; to put it there ourselves for compiling.
(def subprocess-profile
{:dependencies [['org.clojure/clojure (clojure-version)]]
:eval-in :subprocess})

;; We can't really control what is printed here. We're just going to
;; allow `.run` to attach in, out, and err to the standard streams. This
;; should have the effect of compile errors being printed. javac doesn't
;; actually output any compilation info unless it has to (for an error)
;; or you make it do so with `-verbose`.
(defn- run-javac-task
"Run javac to compile all source files in the project."
(defn- run-javac-subprocess
"Run javac to compile all source files in the project. The compilation is run
in a subprocess to avoid it from adding the leiningen standalone to the
classpath, as leiningen adds itself to the classpath through the
bootclasspath."
[project args]
(let [compile-path (:compile-path project)
files (stale-java-sources (:java-source-paths project) compile-path)]
files (stale-java-sources (:java-source-paths project) compile-path)
javac-opts (vec (javac-options project files args))
form `(do (println "Compiling" ~(count files)
"source files to" ~compile-path)
(.mkdirs (io/file ~compile-path))
(when-not (zero? (.run (ToolProvider/getSystemJavaCompiler)
nil nil nil
(into-array String ~javac-opts)))
(.println System/err
"Compilation of Java sources(lein javac) failed.")
(System/exit 1)))] ; Ok here, as we're in a subprocess.
(when (seq files)
(if-let [compiler (ToolProvider/getSystemJavaCompiler)]
(do
(main/info "Compiling" (count files) "source files to" compile-path)
(.mkdirs (io/file compile-path))
(when-not (zero? (.run compiler nil nil nil
(javac-options project files args)))
(main/abort "Compilation of Java sources (lein javac) failed.")))
(if (ToolProvider/getSystemJavaCompiler)
;; compiler will be available from subprocess if available from here
(eval/eval-in
(project/merge-profiles project [subprocess-profile])
form)
(main/abort "lein-javac: system java compiler not found;"
"Be sure to use java from a JDK\nrather than a JRE by"
"either modifying PATH or setting JAVA_CMD.")))))
Expand All @@ -99,4 +119,4 @@ Like the compile and deps tasks, this should be invoked automatically when
needed and shouldn't ever need to be run by hand. By default it is called before
compilation of Clojure source; change :prep-tasks to alter this."
[project & args]
(run-javac-task project args))
(run-javac-subprocess project args))

0 comments on commit 377a98f

Please sign in to comment.