Skip to content

Commit 9c86348

Browse files
committed
add aot compile task
1 parent 5e39be1 commit 9c86348

File tree

3 files changed

+201
-12
lines changed

3 files changed

+201
-12
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ pulled in code from the following projects (thanks, guys!)
376376
* [technomancy/leiningen][50]
377377
* [cemerick/pomegranate][51]
378378
* [Raynes/conch][52]
379+
* [Raynes/bultitude][60]
379380
* [tebeka/clj-digest][53]
380381
* [cldwalker/table][54]
381382
* [clojure/tools.cli][55]
@@ -415,3 +416,4 @@ Distributed under the Eclipse Public License, the same as Clojure.
415416
[57]: https://github.com/AvisoNovate/pretty
416417
[58]: https://github.com/google/hesokuri
417418
[59]: https://code.google.com/p/barbarywatchservice/
419+
[60]: https://github.com/Raynes/bultitude
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
(ns boot.from.bultitude.core
2+
{:boot/from :Raynes/bultitude}
3+
(:require [clojure.java.io :as io]
4+
[clojure.string :as string]
5+
[dynapath.util :as dp])
6+
(:import (java.util.jar JarFile JarEntry)
7+
(java.util.zip ZipException)
8+
(java.io File BufferedReader PushbackReader InputStreamReader)
9+
(clojure.lang DynamicClassLoader)))
10+
11+
(declare namespace-forms-in-dir
12+
file->namespace-forms)
13+
14+
(defn- clj? [^File f]
15+
(and (not (.isDirectory f))
16+
(.endsWith (.getName f) ".clj")))
17+
18+
(defn- clj-jar-entry? [^JarEntry f]
19+
(and (not (.isDirectory f))
20+
(.endsWith (.getName f) ".clj")))
21+
22+
(defn- jar? [^File f]
23+
(and (.isFile f) (.endsWith (.getName f) ".jar")))
24+
25+
(defn- read-ns-form
26+
"Given a reader on a Clojure source file, read until an ns form is found."
27+
([rdr] (read-ns-form rdr true))
28+
([rdr ignore-unreadable?]
29+
(let [form (try (read rdr false ::done)
30+
(catch Exception e
31+
(if ignore-unreadable?
32+
::done
33+
(throw e))))]
34+
(if (and (list? form) (= 'ns (first form)))
35+
form
36+
(when-not (= ::done form)
37+
(recur rdr ignore-unreadable?))))))
38+
39+
(defn ns-form-for-file
40+
([file] (ns-form-for-file file true))
41+
([file ignore-unreadable?]
42+
(with-open [r (PushbackReader. (io/reader file))]
43+
(read-ns-form r ignore-unreadable?))))
44+
45+
(defn namespaces-in-dir
46+
"Return a seq of all namespaces found in Clojure source files in dir."
47+
([dir] (namespaces-in-dir dir true))
48+
([dir ignore-unreadable?]
49+
(map second (namespace-forms-in-dir dir ignore-unreadable?))))
50+
51+
(defn namespace-forms-in-dir
52+
"Return a seq of all namespace forms found in Clojure source files in dir."
53+
([dir] (namespace-forms-in-dir dir true))
54+
([dir ignore-unreadable?]
55+
(for [^File f (file-seq (io/file dir))
56+
:when (and (clj? f) (.canRead f))
57+
:let [ns-form (ns-form-for-file f ignore-unreadable?)]
58+
:when ns-form]
59+
ns-form)))
60+
61+
(defn- ns-form-in-jar-entry
62+
([jarfile entry] (ns-form-in-jar-entry jarfile entry true))
63+
([^JarFile jarfile ^JarEntry entry ignore-unreadable?]
64+
(with-open [rdr (-> jarfile
65+
(.getInputStream entry)
66+
InputStreamReader.
67+
BufferedReader.
68+
PushbackReader.)]
69+
(read-ns-form rdr ignore-unreadable?))))
70+
71+
(defn- namespace-forms-in-jar
72+
([jar] (namespace-forms-in-jar jar true))
73+
([^File jar ignore-unreadable?]
74+
(try
75+
(let [jarfile (JarFile. jar)]
76+
(for [entry (enumeration-seq (.entries jarfile))
77+
:when (clj-jar-entry? entry)
78+
:let [ns-form (ns-form-in-jar-entry jarfile entry
79+
ignore-unreadable?)]
80+
:when ns-form]
81+
ns-form))
82+
(catch ZipException e
83+
(throw (Exception. (str "jar file corrupt: " jar) e))))))
84+
85+
(defn- split-classpath [^String classpath]
86+
(.split classpath (System/getProperty "path.separator")))
87+
88+
(defn loader-classpath
89+
"Returns a sequence of File objects from a classloader."
90+
[loader]
91+
(map io/as-file (dp/classpath-urls loader)))
92+
93+
(defn classpath-files
94+
"Returns a sequence of File objects of the elements on the classpath."
95+
([classloader]
96+
(map io/as-file (dp/all-classpath-urls classloader)))
97+
([] (classpath-files (clojure.lang.RT/baseLoader))))
98+
99+
(defn- classpath->collection [classpath]
100+
(if (coll? classpath)
101+
classpath
102+
(split-classpath classpath)))
103+
104+
(defn- classpath->files [classpath]
105+
(map io/file classpath))
106+
107+
(defn file->namespaces
108+
"Map a classpath file to the namespaces it contains. `prefix` allows for
109+
reducing the namespace search space. For large directories on the classpath,
110+
passing a `prefix` can provide significant efficiency gains."
111+
[^String prefix ^File f]
112+
(map second (file->namespace-forms prefix f)))
113+
114+
(defn file->namespace-forms
115+
"Map a classpath file to the namespace forms it contains. `prefix` allows for
116+
reducing the namespace search space. For large directories on the classpath,
117+
passing a `prefix` can provide significant efficiency gains."
118+
([prefix f] (file->namespace-forms prefix f true))
119+
([^String prefix ^File f ignore-unreadable?]
120+
(cond
121+
(.isDirectory f) (namespace-forms-in-dir
122+
(if prefix
123+
(io/file f (-> prefix
124+
(.replaceAll "\\." "/")
125+
(.replaceAll "-" "_")))
126+
f) ignore-unreadable?)
127+
(jar? f) (let [ns-list (namespace-forms-in-jar f ignore-unreadable?)]
128+
(if prefix
129+
(for [nspace ns-list
130+
:let [sym (second nspace)]
131+
:when (and sym (.startsWith (name sym) prefix))]
132+
nspace)
133+
ns-list)))))
134+
135+
(defn namespace-forms-on-classpath
136+
"Returs the namespaces forms matching the given prefix both on disk and
137+
inside jar files. If :prefix is passed, only return namespaces that begin with
138+
this prefix. If :classpath is passed, it should be a seq of File objects or a
139+
classpath string. If it is not passed, default to java.class.path and the
140+
current classloader, assuming it is a dynamic classloader."
141+
[& {:keys [prefix classpath ignore-unreadable?]
142+
:or {classpath (classpath-files) ignore-unreadable? true}}]
143+
(mapcat
144+
#(file->namespace-forms prefix % ignore-unreadable?)
145+
(->> classpath
146+
classpath->collection
147+
classpath->files)))
148+
149+
(defn namespaces-on-classpath
150+
"Return symbols of all namespaces matching the given prefix both on disk and
151+
inside jar files. If :prefix is passed, only return namespaces that begin with
152+
this prefix. If :classpath is passed, it should be a seq of File objects or a
153+
classpath string. If it is not passed, default to java.class.path and the
154+
current classloader, assuming it is a dynamic classloader."
155+
[& args]
156+
(map second (apply namespace-forms-on-classpath args)))
157+
158+
(defn path-for
159+
"Transform a namespace into a .clj file path relative to classpath root."
160+
[namespace]
161+
(str (-> (str namespace)
162+
(.replace \- \_)
163+
(.replace \. \/))
164+
".clj"))
165+
166+
(defn doc-from-ns-form
167+
"Extract the docstring from a given ns form without evaluating the form. The docstring returned should be the return value of (:doc (meta namespace-symbol)) if the ns-form were to be evaluated."
168+
[ns-form]
169+
(:doc (meta (second (second (second (macroexpand ns-form)))))))

boot/core/src/boot/task/built_in.clj

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
(ns boot.task.built-in
22
(:require
3-
[clojure.java.io :as io]
4-
[clojure.set :as set]
5-
[clojure.pprint :as pprint]
6-
[clojure.string :as string]
7-
[boot.pod :as pod]
8-
[boot.file :as file]
9-
[boot.core :as core]
10-
[boot.main :as main]
11-
[boot.util :as util]
12-
[boot.gitignore :as git]
13-
[boot.task-helpers :as helpers]
14-
[boot.from.table.core :as table])
3+
[clojure.java.io :as io]
4+
[clojure.set :as set]
5+
[clojure.pprint :as pprint]
6+
[clojure.string :as string]
7+
[boot.pod :as pod]
8+
[boot.file :as file]
9+
[boot.core :as core]
10+
[boot.main :as main]
11+
[boot.util :as util]
12+
[boot.gitignore :as git]
13+
[boot.task-helpers :as helpers]
14+
[boot.from.table.core :as table]
15+
[boot.from.bultitude.core :as btude])
1516
(:import
1617
[java.util.concurrent LinkedBlockingQueue TimeUnit]))
1718

@@ -306,6 +307,23 @@
306307
(pod/call-worker
307308
`(boot.web/spit-web! ~(.getPath xmlfile) ~serve ~create ~destroy))))))
308309

310+
(core/deftask aot
311+
"Perform AOT compilation of Clojure namespaces."
312+
313+
[a all bool "Compile all namespaces."
314+
n namespace NS #{sym} "The set of namespaces to compile."]
315+
316+
(let [tgt (core/mktgtdir! ::aot-tgt)]
317+
(core/with-pre-wrap
318+
(let [nses (if-not all
319+
namespace
320+
(->> (core/get-env :src-paths) (map io/file)
321+
(btude/namespaces-on-classpath :classpath)))]
322+
(binding [*compile-path* (.getPath tgt)]
323+
(doseq [ns nses]
324+
(util/info "Compiling %s...\n" ns)
325+
(compile ns)))))))
326+
309327
(core/deftask jar
310328
"Build a jar file for the project."
311329

0 commit comments

Comments
 (0)