Skip to content

Commit

Permalink
Added support for multiple cljsbuild builds in the same project.clj
Browse files Browse the repository at this point in the history
This is extremely convenient for doing library development in
ClojureScript. This allows cljsbuild to compile in all 4 optimization
levels at once, for easier testing, or to compile a test suite
alongside the library code.

Each build runs in parallel, with appropriate locking to ensure output
isn't garbled. It seems to work well in both 'once' and 'auto' modes.
  • Loading branch information
Luke VanderHart committed Jan 20, 2012
1 parent d551393 commit 5b1e9d1
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 38 deletions.
22 changes: 17 additions & 5 deletions src/cljsbuild/core.clj
Expand Up @@ -7,6 +7,14 @@
[clojure.string :as string]
[fs.core :as fs]))

(def lock (Object.))

(defn- println-safe
[& args]
(locking lock
(apply println args)
(flush)))

(defn- join-paths [& paths]
(apply str (interpose "/" paths)))

Expand Down Expand Up @@ -54,16 +62,16 @@
(defn- compile-cljs [cljs-path compiler-options]
(let [output-file (:output-to compiler-options)
output-dir (fs/parent output-file)]
(print (str "Compiling " output-file " from " cljs-path "..."))
(println-safe (str "Compiling " output-file " from " cljs-path "..."))
(flush)
(when output-dir
(fs/mkdirs output-dir))
(let [started-at (. System (nanoTime))]
(try
(build cljs-path compiler-options)
(println (str " Done in " (elapsed started-at) "."))
(println-safe (str output-file " compiled in " (elapsed started-at) "."))
(catch Throwable e
(println " Failed!")
(println-safe " Failed!")
(pst+ e))))))

(defn- is-macro-file? [file]
Expand Down Expand Up @@ -137,8 +145,13 @@
(spit to-file (filtered-crossover-file from-resource))
:updated))))

(defn in-threads
"Given a seq and a function, applies the function to each item in a different thread
and returns a seq of the results. Launches all the threads at once."
[f s]
(doall (map deref (doall (map #(future (f %)) s)))))

(defn run-compiler [cljs-path crossovers compiler-options watch?]
(println "Compiling ClojureScript.")
(loop [last-dependency-mtimes {}]
(let [output-file (:output-to compiler-options)
output-mtime (if (fs/exists? output-file) (fs/mod-time output-file) 0)
Expand All @@ -163,7 +176,6 @@
(recur dependency-mtimes)))))

(defn cleanup-files [cljs-path crossovers compiler-options]
(println "Deleting generated files.")
(fs/delete (:output-to compiler-options))
(fs/delete-dir (:output-dir compiler-options))
(let [from-resources (find-crossovers crossovers)
Expand Down
83 changes: 50 additions & 33 deletions src/leiningen/cljsbuild.clj
Expand Up @@ -65,34 +65,54 @@
(assoc-in {} dest value)))))
(keys relocations)))

(defn- run-local-project [project options form]
(defn- run-local-project [project option-seq form]
(lcompile/eval-in-project
{:local-repo-classpath true
:source-path (:source-path project)
:extra-classpath-dirs (conj
:extra-classpath-dirs (concat
(:extra-classpath-dirs project)
(:source-path options))
(map :source-path option-seq))
:dependencies cljsbuild-dependencies}
form
nil
nil
'(require 'cljsbuild.core))
exit-success)

(defn- run-compiler [project options watch?]
(run-local-project project options
`(cljsbuild.core/run-compiler
~(:source-path options)
'~(:crossovers options)
~(:compiler options)
~watch?)))

(defn- cleanup-files [project options]
(run-local-project project options
`(cljsbuild.core/cleanup-files
~(:source-path options)
'~(:crossovers options)
~(:compiler options))))
(defn- run-compiler [project option-seq watch?]
(run-local-project project option-seq
`(do
(println "Compiling ClojureScript")
(cljsbuild.core/in-threads
(fn [opts#] (cljsbuild.core/run-compiler
(:source-path opts#)
(:crossovers opts#)
(:compiler opts#)
~watch?))
'~option-seq)
(shutdown-agents))))

(defn- cleanup-files [project option-seq]
(run-local-project project option-seq
`(do
(println "Deleting generated files.")
(cljsbuild.core/in-threads
(fn [opts#] (cljsbuild.core/cleanup-files
(:source-path opts#)
(:crossovers opts#)
(:compiler opts#)))
'~option-seq)
(shutdown-agents))))

(defn- normalize-options
"Sets default options and accounts for backwards compatibility"
[orig-options]
(let [compat-options (backwards-compat orig-options)]
(when (not= orig-options compat-options)
(warn (str
"your deprecated :cljsbuild config was interpreted as:\n"
compat-options)))
(deep-merge default-options compat-options)))

(defn cljsbuild
"Run the cljsbuild plugin.
Expand All @@ -106,22 +126,19 @@ Usage: lein cljsbuild [once|auto|clean]
(usage)
exit-failure)
([project mode]
(let [orig-options (:cljsbuild project)]
(when (nil? orig-options)
(warn "no :cljsbuild entry found in project definition."))
(let [compat-options (backwards-compat orig-options)
options (deep-merge default-options compat-options)]
(when (not= orig-options compat-options)
(warn (str
"your deprecated :cljsbuild config was interpreted as:\n"
compat-options)))
(case mode
"once" (run-compiler project options false)
"auto" (run-compiler project options true)
"clean" (cleanup-files project options)
(do
(usage)
exit-failure))))))
(when (nil? (:cljsbuild project))
(warn "no :cljsbuild entry found in project definition."))
(let [raw-options (:cljsbuild project)
option-seq (if (map? raw-options)
[(normalize-options raw-options)]
(map normalize-options raw-options))]
(case mode
"once" (run-compiler project option-seq false)
"auto" (run-compiler project option-seq true)
"clean" (cleanup-files project option-seq)
(do
(usage)
exit-failure)))))

(defn compile-hook [task & args]
(cljsbuild (first args) "once")
Expand Down

0 comments on commit 5b1e9d1

Please sign in to comment.