Skip to content

Commit 409d1ec

Browse files
Deraendnolen
authored and
dnolen
committed
Refactor build pipeline
This separates dependency finding and ordering from compiling the files. The file dependencies are searched for using new -find-sources method in Compilable protocol. Build function now first searches for all sources in given inputs, then adds dependency sources to those (e.g. from jars), orders the sources, and then compiles them all.
1 parent 62ae63b commit 409d1ec

File tree

4 files changed

+146
-18
lines changed

4 files changed

+146
-18
lines changed

src/main/clojure/cljs/analyzer.cljc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,6 +1637,12 @@
16371637
resource on the classpath or file from the root of the build."
16381638
[ns]
16391639
(or (util/ns->source ns)
1640+
;; Find sources available in inputs given to cljs.closure/build - Juho Teperi
1641+
(some (fn [source]
1642+
(if (= ns (:ns source))
1643+
(:source-file source)))
1644+
(:sources @env/*compiler*))
1645+
;; Find sources in directory given to cljs.compiler/compile-root - Juho Teperi
16401646
(let [rootp (when-let [root (:root @env/*compiler*)]
16411647
(.getPath ^File root))
16421648
cljsf (io/file rootp (ns->relpath ns :cljs))

src/main/clojure/cljs/build/api.clj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,9 @@
183183
(if (sequential? compiled)
184184
compiled
185185
[compiled])))]
186-
(mapcat compile-input xs)))))
186+
(mapcat compile-input xs)))
187+
(-find-sources [_ opts]
188+
(mapcat #(closure/-find-sources % opts) xs))))
187189

188190
(defn compile
189191
"Given a Compilable, compile it and return an IJavaScript."

src/main/clojure/cljs/closure.clj

Lines changed: 127 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
1515
Use the 'build' function for end-to-end compilation.
1616
17-
build = compile -> add-dependencies -> optimize -> output
17+
build = find-sources -> add-dependencies -> compile -> optimize -> output
1818
1919
Two protocols are defined: IJavaScript and Compilable. The
2020
Compilable protocol is satisfied by something which can return one
@@ -389,7 +389,8 @@
389389
(-paths [this] [this]))
390390

391391
(defprotocol Compilable
392-
(-compile [this opts] "Returns one or more IJavaScripts."))
392+
(-compile [this opts] "Returns one or more IJavaScripts.")
393+
(-find-sources [this opts] "Returns one or more IJavascripts, without compiling them."))
393394

394395
(defn compile-form-seq
395396
"Compile a sequence of forms to a JavaScript source string."
@@ -477,40 +478,59 @@
477478
(let [file-on-disk (jar-file-to-disk this (util/output-directory opts))]
478479
(-compile file-on-disk opts))))
479480

481+
(defn find-jar-sources
482+
[this opts]
483+
[(comp/find-source (jar-file-to-disk this (util/output-directory opts)))])
484+
480485
(extend-protocol Compilable
481486

482487
File
483488
(-compile [this opts]
484489
(if (.isDirectory this)
485490
(compile-dir this opts)
486491
(compile-file this opts)))
492+
(-find-sources [this _]
493+
(if (.isDirectory this)
494+
(comp/find-root-sources this)
495+
[(comp/find-source this)]))
487496

488497
URL
489498
(-compile [this opts]
490499
(case (.getProtocol this)
491500
"file" (-compile (io/file this) opts)
492501
"jar" (compile-from-jar this opts)))
502+
(-find-sources [this opts]
503+
(case (.getProtocol this)
504+
"file" (-find-sources (io/file this) opts)
505+
"jar" (find-jar-sources this opts)))
493506

494507
clojure.lang.PersistentList
495508
(-compile [this opts]
496509
(compile-form-seq [this]))
510+
; FIXME: -find-sources
497511

498512
String
499513
(-compile [this opts] (-compile (io/file this) opts))
514+
(-find-sources [this opts] (-find-sources (io/file this) opts))
500515

501516
clojure.lang.PersistentVector
502517
(-compile [this opts] (compile-form-seq this))
518+
; FIXME: -find-sources
503519
)
504520

505521
(comment
506522
;; compile a file in memory
507523
(-compile "samples/hello/src/hello/core.cljs" {})
524+
(-find-sources "samples/hello/src/hello/core.cljs" {})
508525
;; compile a file to disk - see file @ 'out/clojure/set.js'
509526
(-compile (io/resource "clojure/set.cljs") {:output-file "clojure/set.js"})
527+
(-find-sources (io/resource "clojure/set.cljs") {:output-file "clojure/set.js"})
510528
;; compile a project
511529
(-compile (io/file "samples/hello/src") {})
530+
(-find-sources (io/file "samples/hello/src") {})
512531
;; compile a project with a custom output directory
513532
(-compile (io/file "samples/hello/src") {:output-dir "my-output"})
533+
(-find-sources (io/file "samples/hello/src") {:output-dir "my-output"})
514534
;; compile a form
515535
(-compile '(defn plus-one [x] (inc x)) {})
516536
;; compile a vector of forms
@@ -639,6 +659,28 @@
639659
(cljs-dependencies {} ["cljs.core" "clojure.string"])
640660
)
641661

662+
(defn find-cljs-dependencies
663+
"Given set of cljs namespace symbols, find IJavaScript objects for the namespaces."
664+
[requires]
665+
(letfn [(cljs-deps [namespaces]
666+
(->> namespaces
667+
(remove #(or ((@env/*compiler* :js-dependency-index) %)
668+
(deps/find-classpath-lib %)))
669+
(map cljs-source-for-namespace)
670+
(remove (comp nil? :uri))))]
671+
(loop [required-files (cljs-deps requires)
672+
visited (set required-files)
673+
cljs-namespaces #{}]
674+
(if (seq required-files)
675+
(let [next-file (first required-files)
676+
ns-info (ana/parse-ns (:uri next-file))
677+
new-req (remove #(contains? visited %) (cljs-deps (cond-> (deps/-requires ns-info)
678+
(= 'cljs.js (:ns ns-info)) (conj "cljs.core$macros"))))]
679+
(recur (into (rest required-files) new-req)
680+
(into visited new-req)
681+
(conj cljs-namespaces ns-info)))
682+
(disj cljs-namespaces nil)))))
683+
642684
(defn add-dependencies
643685
"Given one or more IJavaScript objects in dependency order, produce
644686
a new sequence of IJavaScript objects which includes the input list
@@ -670,6 +712,77 @@
670712
required-cljs
671713
inputs)))))
672714

715+
(comment
716+
(alter-var-root #'env/*compiler* (constantly (env/default-compiler-env)))
717+
;; only get cljs deps
718+
(find-cljs-dependencies ["goog.string" "cljs.core"])
719+
;; get transitive deps
720+
(find-cljs-dependencies ["clojure.string"])
721+
;; don't get cljs.core twice
722+
(find-cljs-dependencies ["cljs.core" "clojure.string"])
723+
)
724+
725+
(defn add-dependency-sources
726+
"Given list of IJavaScript objects, produce a new sequence of IJavaScript objects
727+
of all dependencies of inputs."
728+
[inputs]
729+
(let [inputs (set inputs)
730+
requires (set (mapcat deps/-requires inputs))]
731+
(into inputs (find-cljs-dependencies requires))))
732+
733+
(defn check-unprovided
734+
[inputs]
735+
(let [requires (set (mapcat deps/-requires inputs))
736+
provided (set (mapcat deps/-provides inputs))
737+
unprovided (clojure.set/difference requires provided #{"constants-table"})]
738+
(when (seq unprovided)
739+
(ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)}))
740+
inputs))
741+
742+
(defn compile-sources
743+
"Takes dependency ordered list of IJavaScript compatible maps from parse-ns
744+
and compiles them."
745+
[inputs compiler-stats opts]
746+
(util/measure compiler-stats
747+
"Compile sources"
748+
(binding [comp/*inputs* (zipmap (map :ns inputs) inputs)]
749+
(doall
750+
(for [ns-info inputs]
751+
; TODO: compile-file calls parse-ns unnecessarily to get ns-info
752+
; TODO: we could mark dependent namespaces for recompile here
753+
(-compile (:source-file ns-info)
754+
; - ns-info -> ns -> cljs file relpath -> js relpath
755+
(merge opts {:output-file (comp/rename-to-js (util/ns->relpath (:ns ns-info)))})))))))
756+
757+
(defn add-goog-base
758+
[inputs]
759+
(cons (javascript-file nil (io/resource "goog/base.js") ["goog"] nil)
760+
inputs))
761+
762+
(defn add-js-sources
763+
"Given list of IJavaScript objects, add foreign-deps and constants-table
764+
IJavaScript objects to the list."
765+
[inputs opts]
766+
(let [requires (set (mapcat deps/-requires inputs))
767+
required-js (js-dependencies opts requires)]
768+
(concat
769+
(map
770+
(fn [{:keys [foreign url file provides requires] :as js-map}]
771+
(let [url (or url (io/resource file))]
772+
(merge
773+
(javascript-file foreign url provides requires)
774+
js-map)))
775+
required-js)
776+
[(when (-> @env/*compiler* :options :emit-constants)
777+
(let [url (deps/to-url (str (util/output-directory opts) "/constants_table.js"))]
778+
(javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)))]
779+
inputs)))
780+
781+
(comment
782+
(comp/find-sources-root "samples/hello/src")
783+
(find-dependency-sources (find-sources-root "samples/hello/src"))
784+
(find-sources "samples/hello/src"))
785+
673786
(defn preamble-from-paths [paths]
674787
(when-let [missing (seq (remove io/resource paths))]
675788
(ana/warning :preamble-missing @env/*compiler* {:missing (sort missing)}))
@@ -1653,7 +1766,9 @@
16531766
#(-> %
16541767
(update-in [:options] merge all-opts)
16551768
(assoc :target (:target opts))
1656-
(assoc :js-dependency-index (deps/js-dependency-index all-opts))))
1769+
(assoc :js-dependency-index (deps/js-dependency-index all-opts))
1770+
;; Save list of sources for cljs.analyzer/locate-src - Juho Teperi
1771+
(assoc :sources (-find-sources source all-opts))))
16571772
(binding [comp/*dependents* (when-not (false? (:recompile-dependents opts))
16581773
(atom {:recompile #{} :visited #{}}))
16591774
ana/*cljs-static-fns*
@@ -1683,20 +1798,15 @@
16831798
compile-opts (if one-file?
16841799
(assoc all-opts :output-file (:output-to all-opts))
16851800
all-opts)
1686-
compiled (util/measure compiler-stats
1687-
"Compile basic sources"
1688-
(doall (-compile source compile-opts)))
1689-
js-sources (util/measure compiler-stats
1690-
"Add dependencies"
1691-
(doall
1692-
(concat
1693-
(apply add-dependencies all-opts
1694-
(concat
1695-
(if (sequential? compiled) compiled [compiled])
1696-
(when (= :nodejs (:target all-opts))
1697-
[(-compile (io/resource "cljs/nodejs.cljs") all-opts)])))
1698-
(when (= :nodejs (:target all-opts))
1699-
[(-compile (io/resource "cljs/nodejscli.cljs") all-opts)]))))
1801+
js-sources (-> (-find-sources source all-opts)
1802+
add-dependency-sources
1803+
deps/dependency-order
1804+
(compile-sources compiler-stats compile-opts)
1805+
(add-js-sources all-opts)
1806+
(cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejs.cljs") all-opts)]))
1807+
deps/dependency-order
1808+
add-goog-base
1809+
(cond-> (= :nodejs (:target all-opts)) (concat [(-compile (io/resource "cljs/nodejscli.cljs") all-opts)])))
17001810
_ (when (:emit-constants all-opts)
17011811
(comp/emit-constants-table-to-file
17021812
(::ana/constant-table @env/*compiler*)

src/main/clojure/cljs/compiler.cljc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,16 @@
13911391
(assoc ijs :file-name (.getPath output-file)))))
13921392
compiled)))))))
13931393

1394+
#?(:clj
1395+
(defn find-source [file]
1396+
(ana/parse-ns file)))
1397+
1398+
#?(:clj
1399+
(defn find-root-sources
1400+
[src-dir]
1401+
(let [src-dir-file (io/file src-dir)]
1402+
(map find-source (cljs-files-in src-dir-file)))))
1403+
13941404
;; TODO: needs fixing, table will include other things than keywords - David
13951405

13961406
(defn emit-constants-table [table]

0 commit comments

Comments
 (0)