Skip to content
Snippets Groups Projects
  • Braden Shepherdson's avatar
    19beda53
    Make namespace aliasing consistent everywhere; enforce with clj-kondo (#21738) · 19beda53
    Braden Shepherdson authored
    * Make namespace aliasing consistent everywhere; enforce with clj-kondo
    
    See the table of aliases in .clj-kondo/config.edn
    
    Notable patterns:
    - `[metabase.api.foo :as api.foo]`
    - `[metabase.models.foo :as foo]`
    - `[metabase.query-processor.foo :as qp.foo]`
    - `[metabase.server.middleware.foo :as mw.foo]`
    - `[metabase.util.foo :as u.foo]`
    - `[clj-http.client :as http]` and `[metabase.http-client :as client]`
    
    Fixes #19930.
    Make namespace aliasing consistent everywhere; enforce with clj-kondo (#21738)
    Braden Shepherdson authored
    * Make namespace aliasing consistent everywhere; enforce with clj-kondo
    
    See the table of aliases in .clj-kondo/config.edn
    
    Notable patterns:
    - `[metabase.api.foo :as api.foo]`
    - `[metabase.models.foo :as foo]`
    - `[metabase.query-processor.foo :as qp.foo]`
    - `[metabase.server.middleware.foo :as mw.foo]`
    - `[metabase.util.foo :as u.foo]`
    - `[clj-http.client :as http]` and `[metabase.http-client :as client]`
    
    Fixes #19930.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
build.clj 5.55 KiB
(ns build
  (:require [clojure.java.io :as io]
            [clojure.string :as str]
            [clojure.tools.build.api :as b]
            [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]
            [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! []
  (u/step "Clean"
    (u/step (format "Delete %s" class-dir)
      (b/delete {:path class-dir}))
    (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))))

(defn compile-sources! [basis]
  (u/step "Compile Clojure source files"
    (let [paths    (all-paths basis)
          _        (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})
        (u/announce "Finished compilation in %.1f seconds." (/ duration-ms 1000.0))))))

(defn copy-resources! [edition basis]
  (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)]
      (u/step (format "Copy %s" path)
        (b/copy-dir {:target-dir class-dir, :src-dirs [path]})))))

(defn create-uberjar! [basis]
  (u/step "Create uberjar"
    (with-duration-ms [duration-ms]
      (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")
   "Main-Class"       "metabase.core"})

(defn manifest ^Manifest []
  (doto (Manifest.)
    (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! []
  (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}}]
  (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!))
      (u/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