forked from metabase/metabase
-
Notifications
You must be signed in to change notification settings - Fork 1
/
build.clj
160 lines (143 loc) · 6.47 KB
/
build.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
(ns build
(:require [clojure.java.io :as io]
[clojure.string :as str]
[clojure.tools.build.api :as b]
[clojure.tools.build.util.zip :as b.zip]
[clojure.tools.namespace.dependency :as ns.deps]
[clojure.tools.namespace.find :as ns.find]
[clojure.tools.namespace.parse :as ns.parse]
[hf.depstar.api :as d]
[metabuild-common.core :as c])
(:import java.io.OutputStream
java.net.URI
[java.nio.file Files FileSystems OpenOption StandardOpenOption]
java.util.Collections
java.util.jar.Manifest))
(def class-dir "target/classes")
(def uberjar-filename "target/uberjar/metabase.jar")
(defn do-with-duration-ms [thunk f]
(let [start-time-ms (System/currentTimeMillis)
result (thunk)
duration (- (System/currentTimeMillis) start-time-ms)]
(f duration)
result))
(defmacro with-duration-ms [[duration-ms-binding] & body]
(let [[butlast-forms last-form] ((juxt butlast last) body)]
`(do-with-duration-ms
(fn [] ~@butlast-forms)
(fn [~duration-ms-binding]
~last-form))))
(defn create-basis [edition]
{:pre [(#{:ee :oss} edition)]}
(b/create-basis {:project "deps.edn", :aliases #{edition}}))
(defn all-paths [basis]
(concat (:paths basis)
(get-in basis [:classpath-args :extra-paths])))
(defn clean! []
(c/step "Clean"
(c/step (format "Delete %s" class-dir)
(b/delete {:path class-dir}))
(c/step (format "Delete %s" uberjar-filename)
(b/delete {:path uberjar-filename}))))
;; this topo sort order stuff is required for stuff to work correctly... I copied it from my Cloverage PR
;; https://github.com/cloverage/cloverage/pull/303
(defn- dependencies-graph
"Return a `clojure.tools.namespace` dependency graph of namespaces named by `ns-symbol`."
[ns-decls]
(reduce
(fn [graph ns-decl]
(let [ns-symbol (ns.parse/name-from-ns-decl ns-decl)]
(reduce
(fn [graph dep]
(ns.deps/depend graph ns-symbol dep))
graph
(ns.parse/deps-from-ns-decl ns-decl))))
(ns.deps/graph)
ns-decls))
(defn metabase-namespaces-in-topo-order [basis]
(let [ns-decls (mapcat
(comp ns.find/find-ns-decls-in-dir io/file)
(all-paths basis))
ns-symbols (set (map ns.parse/name-from-ns-decl ns-decls))]
(->> (dependencies-graph ns-decls)
ns.deps/topo-sort
(filter ns-symbols))))
(defn compile-sources! [basis]
(c/step "Compile Clojure source files"
(let [paths (all-paths basis)
_ (c/announce "Compiling Clojure files in %s" (pr-str paths))
ns-decls (c/step "Determine compilation order for Metabase files"
(metabase-namespaces-in-topo-order basis))]
(with-duration-ms [duration-ms]
(b/compile-clj {:basis basis
:src-dirs paths
:class-dir class-dir
:ns-compile ns-decls})
(c/announce "Finished compilation in %.1f seconds." (/ duration-ms 1000.0))))))
(defn copy-resources! [edition basis]
(c/step "Copy resources"
;; technically we don't NEED to copy the Clojure source files but it doesn't really hurt anything IMO.
(doseq [path (all-paths basis)]
(c/step (format "Copy %s" path)
(b/copy-dir {:target-dir class-dir, :src-dirs [path]})))))
(defn create-uberjar! [basis]
(c/step "Create uberjar"
(with-duration-ms [duration-ms]
(d/uber {:class-dir class-dir
:uber-file uberjar-filename
:basis basis})
(c/announce "Created uberjar in %.1f seconds." (/ duration-ms 1000.0)))))
(def manifest-entries
{"Manifest-Version" "1.0"
"Created-By" "Metabase build.clj"
"Build-Jdk-Spec" (System/getProperty "java.specification.version")
"Main-Class" "metabase.core"
"Liquibase-Package" (str/join ","
["liquibase.change"
"liquibase.changelog"
"liquibase.database"
"liquibase.datatype"
"liquibase.diff"
"liquibase.executor"
"liquibase.ext"
"liquibase.lockservice"
"liquibase.logging"
"liquibase.parser"
"liquibase.precondition"
"liquibase.sdk"
"liquibase.serializer"
"liquibase.snapshot"
"liquibase.sqlgenerator"
"liquibase.structure"
"liquibase.structurecompare"])})
(defn manifest ^Manifest []
(doto (Manifest.)
(b.zip/fill-manifest! manifest-entries)))
(defn write-manifest! [^OutputStream os]
(.write (manifest) os)
(.flush os))
;; the customizations we need to make are not currently supported by tools.build -- see
;; https://ask.clojure.org/index.php/10827/ability-customize-manifest-created-clojure-tools-build-uber -- so we need
;; to do it by hand for the time being.
(defn update-manifest! []
(c/step "Update META-INF/MANIFEST.MF"
(with-open [fs (FileSystems/newFileSystem (URI. (str "jar:file:" (.getAbsolutePath (io/file "target/uberjar/metabase.jar"))))
Collections/EMPTY_MAP)]
(let [manifest-path (.getPath fs "META-INF" (into-array String ["MANIFEST.MF"]))]
(with-open [os (Files/newOutputStream manifest-path (into-array OpenOption [StandardOpenOption/WRITE
StandardOpenOption/TRUNCATE_EXISTING]))]
(write-manifest! os))))))
;; clojure -T:build uberjar :edition <edition>
(defn uberjar [{:keys [edition], :or {edition :oss}}]
(c/step (format "Build %s uberjar" edition)
(with-duration-ms [duration-ms]
(clean!)
(let [basis (create-basis edition)]
(compile-sources! basis)
(copy-resources! edition basis)
(create-uberjar! basis)
(update-manifest!))
(c/announce "Built target/uberjar/metabase.jar in %.1f seconds."
(/ duration-ms 1000.0)))))
;; TODO -- add `jar` and `install` commands to install Metabase to the local Maven repo (?) could make it easier to
;; build 3rd-party drivers the old way