Newer
Older
(ns build
(:require [clojure.java.io :as io]
[clojure.string :as str]
[clojure.tools.build.api :as b]
Braden Shepherdson
committed
[clojure.tools.build.util.zip :as build.zip]
[clojure.tools.namespace.dependency :as ns.deps]
[clojure.tools.namespace.find :as ns.find]
[clojure.tools.namespace.parse :as ns.parse]
Braden Shepherdson
committed
[hf.depstar.api :as depstar]
[metabuild-common.core :as u])
(: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! []
Braden Shepherdson
committed
(u/step "Clean"
(u/step (format "Delete %s" class-dir)
Braden Shepherdson
committed
(u/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)
(cons 'metabase.bootstrap))))
Braden Shepherdson
committed
(u/step "Compile Clojure source files"
Braden Shepherdson
committed
_ (u/announce "Compiling Clojure files in %s" (pr-str paths))
ns-decls (u/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})
Braden Shepherdson
committed
(u/announce "Finished compilation in %.1f seconds." (/ duration-ms 1000.0))))))
Braden Shepherdson
committed
(u/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)]
Braden Shepherdson
committed
(u/step (format "Copy %s" path)
(b/copy-dir {:target-dir class-dir, :src-dirs [path]})))))
(defn create-uberjar! [basis]
Braden Shepherdson
committed
(u/step "Create uberjar"
Braden Shepherdson
committed
(depstar/uber {:class-dir class-dir
:uber-file uberjar-filename
:basis basis})
(u/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")
(defn manifest ^Manifest []
(doto (Manifest.)
Braden Shepherdson
committed
(build.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! []
Braden Shepherdson
committed
(u/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}}]
Braden Shepherdson
committed
(u/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!))
Braden Shepherdson
committed
(u/announce "Built target/uberjar/metabase.jar in %.1f seconds."