Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

123 lines (112 sloc) 5.395 kb
(ns leiningen.javac
"Compile Java source files."
(:require [leiningen.classpath :as classpath]
[main :as main]
[eval :as eval]
[project :as project]]
[ :as io])
(defn- stale-java-sources
"Returns a lazy seq of file paths: every Java source file within
dirs modified since it was most recently compiled into
[dirs compile-path]
(for [dir dirs
^File source (filter #(-> ^File % (.getName) (.endsWith ".java"))
(file-seq (io/file dir)))
:let [rel-source (.substring (.getPath source) (inc (count dir)))
rel-compiled (.replaceFirst rel-source "\\.java$" ".class")
compiled (io/file compile-path rel-compiled)]
:when (>= (.lastModified source) (.lastModified compiled))]
(.getPath source)))
(def ^{:private true
:doc "Legacy (Lein1/Ant task) javac options that do not translate
to the new (JDK's javac) format as key-value pairs. For example,
:debug \"off\" needs to be translated to -g:none."}
special-ant-javac-keys [:destdir :debug :debugLevel])
(defn- normalize-specials
"Handles legacy (Lein1/Ant task) javac options that do not translate
to the new (JDK's javac) format as key-value pairs"
[{:keys [debug debugLevel]}]
;; debug "off" => -g:none
;; debugLevel "source,lines" => -g:source-lines
(if (or (= "off" debug) (false? debug))
(if debugLevel
[(str "-g:" debugLevel)]
(defn normalize-javac-options
"Converts :javac-opts in Leiningen 1 format (passed as a map) into
Leiningen 2 format (a vector).
Options in Leiningen 2 format are returned unmodified"
(if (map? opts)
(let [special-opts (select-keys opts special-ant-javac-keys)
other-opts (apply dissoc (concat [opts] special-ant-javac-keys))
specials (normalize-specials special-opts)
others (flatten (vec (map (fn [[k v]]
[(str "-" (name k)) v]) other-opts)))]
(vec (map (comp name str) (flatten (concat specials others)))))
;; Tool's .run method expects the last argument to be an array of
;; strings, so that's what we'll return here.
(defn- javac-options
"Compile all sources of possible options and add important defaults.
Result is a String java array of options."
[project files args]
(concat (normalize-javac-options (:javac-options project))
["-cp" (classpath/get-classpath-string project)
"-d" (:compile-path project)]
;; 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-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
[project args]
(let [compile-path (:compile-path project)
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 (ToolProvider/getSystemJavaCompiler)
;; compiler will be available from subprocess if available from here
(project/merge-profiles project [subprocess-profile])
(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.")))))
(defn javac
"Compile Java source files.
Add a :java-source-paths key to project.clj to specify where to find them.
Options passed in on the command line as well as options from the :javac-opts
vector in project.clj will be given to the compiler; e.g. `lein javac -verbose`.
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-subprocess project args))
Jump to Line
Something went wrong with that request. Please try again.