Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions jank-build/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# jank-build
[![Clojars Project](https://img.shields.io/clojars/v/org.jank-lang.commons/jank-build.svg)]
Build script helper functions.

## Usage
Add this project to your Leiningen `:build-dependencies` vector:

```clojure
[org.jank-lang.commons/jank-build "0.1-SNAPSHOT"]
```
9 changes: 9 additions & 0 deletions jank-build/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(defproject org.jank-lang.commons/jank-build "0.1-SNAPSHOT"
:description "Build script helper functions"
:url "https://github.com/jank-lang/commons"
:license {:name "MPL 2.0"
:url "https://www.mozilla.org/en-US/MPL/2.0/"}
:source-paths ["src" "test"]
:profiles {:dev {:dependencies [[org.clojure/clojure "1.12.6"]
[babashka/fs "0.5.34"]
[babashka/process "0.6.25"]]}})
32 changes: 32 additions & 0 deletions jank-build/src/jank/build/cmake.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
(ns jank.build.cmake
(:require [babashka.process :as proc]
[jank.build.util :as util]))

(defn install-rpath []
(when (util/linux?) "$ORIGIN"))

(defn default-defines [{:keys [out-dir optimization-level static?]
:or {optimization-level 0}}]
(let [rpath (install-rpath)]
(merge {"BUILD_SHARED_LIBS" (if static? "OFF" "ON")
"CMAKE_BUILD_TYPE" (if (pos? optimization-level) "Release" "Debug")
"CMAKE_INSTALL_PREFIX" out-dir
;; Make sure these paths are standardized, otherwise libs may
;; sometimes go into lib64/ instea of lib/, etc.
"CMAKE_INSTALL_BINDIR" "bin"
"CMAKE_INSTALL_LIBDIR" "lib"
"CMAKE_INSTALL_INCLUDEDIR" "include"}
Comment on lines +16 to +18

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New default so we don't have to specify these in build files.

(when (and (not static?) rpath)
{"CMAKE_INSTALL_RPATH" rpath}))))

(defn cmake

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function is now cmake instead of build, to match the pkg-config function. I think we will :refer these in build scripts.

"Run the `cmake` tool in the src-dir, building to the intermediate build-dir,
and installing artifacts into the out-dir.

No jank-build directives are output, so make sure to specify the correct
link/include directories, link libraries, etc. yourself."
[{:keys [src-dir build-dir] :as input} & {:keys [defines target] :or {target "install"}}]
(let [d-flags (map (fn [[k v]] (str "-D" (name k) "=" v))
(merge (default-defines input) defines))]
(proc/shell (concat ["cmake"] d-flags ["-B" build-dir src-dir]))
(proc/shell ["cmake" "--build" build-dir "--parallel" "--target" target])))
66 changes: 66 additions & 0 deletions jank-build/src/jank/build/pkg_config.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
(ns jank.build.pkg-config
(:require [clojure.string :as string]
[babashka.process :as proc]
[babashka.fs :as fs]
[jank.build.util :as util]))

(defn parse-prefixed [prefix s]
(for [entry (string/split s #"\s+")
:when (string/starts-with? entry prefix)]
(subs entry (count prefix))))

(defn has-static-lib? [lib link-dirs]
; TODO: Windows?
(let [lib-name (str "lib" lib ".a")]
(some (fn [link-dir]
(let [link-dir+lib (fs/path link-dir lib-name)]
(when (fs/exists? link-dir+lib)
true)))
link-dirs)))

(defn brew-pkg-config-path [build-input pkg]
(when (and (util/mac?) (fs/which "brew"))
(let [{:keys [exit out]} (proc/shell {:out :string
:err :string
:continue true
; Disable brew's cache and use the already-authorized
; working directory rather than /private/tmp.
:extra-env {"HOMEBREW_NO_BOOTSNAP" "1"
"HOMEBREW_NO_AUTO_UPDATE" "1"
"HOMEBREW_TEMP" (:out-dir build-input)}}
"brew" "--prefix")
prefix (string/trim out)]
(when (and (zero? exit) (not (string/blank? prefix)))
(str prefix "/opt/" pkg "/lib/pkgconfig")))))

(defn pkg-config-path
"Rebuild the PKG_CONFIG_PATH string with additional paths prepended. nil
entries are ignored."
[& paths]
(let [sep (System/getProperty "path.separator")
from-env (some-> (System/getenv "PKG_CONFIG_PATH")
(not-empty)
(string/split (re-pattern sep)))
fresh (filter some? paths)]
(string/join sep (concat fresh from-env))))

(defn pkg-config
"Call the `pkg-config` tool and parse link directories, include directories,
and link libraries.

jank-build directives will automatically be output for these values."
[build-input pc-name]
;; TODO: parse preprocessor defines from cflags
(let [pc-cmd (cond-> ["pkg-config" pc-name "--libs" "--cflags"]
(:static? build-input) (conj "--static"))
brew-path (brew-pkg-config-path build-input pc-name)
pc-opts {:extra-env {"PKG_CONFIG_PATH" (pkg-config-path brew-path)}
:out :string}
pc-output (->> pc-cmd (apply proc/shell pc-opts) :out)
link-dirs (parse-prefixed "-L" pc-output)]
(run! util/link-dir link-dirs)
(run! util/include-dir (parse-prefixed "-I" pc-output))
(run! #(if (and (:static? build-input) (has-static-lib? % link-dirs))
(util/link-static-library %)
(util/link-library %))
(parse-prefixed "-l" pc-output))))
54 changes: 54 additions & 0 deletions jank-build/src/jank/build/util.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
(ns jank.build.util
(:require [clojure.string :as string]))

;; OS identification functions are adapted from the Apache Commons
;; Lang SystemUtils class.
;;
;; https://github.com/apache/commons-lang/blob/32dc4c501e3acba29d53a466eebd611bb2f093a5/src/main/java/org/apache/commons/lang3/SystemUtils.java

(def os-name (System/getProperty "os.name"))

(defn linux? []
(-> (string/lower-case os-name)
(string/starts-with? "linux")))

(defn mac? []
(-> (string/lower-case os-name)
(string/starts-with? "mac")))

(defn windows? []
(-> (string/lower-case os-name)
(string/starts-with? "windows")))

;; Emit common "jank-build::"-prefixed items.

(defn define
([k] (println (str "jank-build::define=" k)))
([k v] (println (str "jank-build::define=" k "=" v))))

(defn include-dir
"Add a jank include-dir directive (a -I flag). If a relative path is given
then it is assumed to be relative to the build output directory."
[dir]
(println (str "jank-build::include-dir=" dir)))

(defn link-dir
"Add a jank link-dir directive (a -L flag). If a relative path is given then
it is assumed to be relative to the build output directory."
[dir]
(println (str "jank-build::link-dir=" dir)))

(defn link-library [lib]
(println (str "jank-build::link-library=" lib)))

(defn link-static-library [lib]
(println (str "jank-build::link-static-library=" lib)))

(defn link-framework [framework]
(println (str "jank-build::link-framework=" framework)))

(defn rerun-if-changed [path]
(println (str "jank-build::rerun-if-changed=" path)))

(defn rerun-if-env-changed [k]
(println (str "jank-build::rerun-if-env-changed=" k)))
3 changes: 3 additions & 0 deletions jank-build/test-data/cmake-project/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.12)
project(cmake_project VERSION 1.0 LANGUAGES CXX)
add_library(foo foo.cpp)
4 changes: 4 additions & 0 deletions jank-build/test-data/cmake-project/foo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
int foo()
{
return 42;
}
15 changes: 15 additions & 0 deletions jank-build/test/jank/build/cmake_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(ns jank.build.cmake-test
(:require [clojure.test :refer [deftest is]]
[clojure.string :as string]
[babashka.fs :as fs]
[jank.build.cmake :as cm]))

(def test-project-dir (fs/path "test-data/cmake-project"))

(deftest build-example-project-test
;; should not throw
(cm/cmake {:src-dir test-project-dir
:build-dir (fs/create-temp-dir {:prefix "jank-build-cmake-test"})
:out-dir (fs/create-temp-dir {:prefix "jank-build-cmake-test"})}
{:defines {:SOME_DEFINE true}
:target "all"}))
11 changes: 11 additions & 0 deletions jank-build/test/jank/build/pkg_config_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(ns jank.build.pkg-config-test
(:require [clojure.test :refer [deftest is]]
[clojure.string :as string]
[jank.build.pkg-config :as pc]))

;; zlib should be on all jank-enabled systems.
(deftest find-zlib-test
(let [out (with-out-str *out* (pc/pkg-config {} "zlib"))]
(is (string/includes? out "jank-build::link-dir="))
(is (string/includes? out "jank-build::include-dir="))
(is (string/includes? out "jank-build::link-library=z"))))
35 changes: 35 additions & 0 deletions jank-build/test/jank/build/util_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
(ns jank.build.util-test
(:require [clojure.test :refer [deftest is]]
[jank.build.util :as util]))

(deftest os-detection-test
(with-redefs [util/os-name "Linux"]
(is (util/linux?))
(is (not (util/mac?)))
(is (not (util/windows?))))
(with-redefs [util/os-name "Mac OS X"]
(is (util/mac?))
(is (not (util/linux?)))
(is (not (util/windows?))))
(with-redefs [util/os-name "Windows XP"]
(is (util/windows?))
(is (not (util/linux?)))
(is (not (util/mac?)))))

(deftest build-directives-test
(is (= (with-out-str (util/define "KEY"))
"jank-build::define=KEY\n"))
(is (= (with-out-str (util/define "KEY" "VAL"))
"jank-build::define=KEY=VAL\n"))
(is (= (with-out-str (util/include-dir "/usr/include"))
"jank-build::include-dir=/usr/include\n"))
(is (= (with-out-str (util/link-dir "/usr/lib64"))
"jank-build::link-dir=/usr/lib64\n"))
(is (= (with-out-str (util/link-library "m"))
"jank-build::link-library=m\n"))
(is (= (with-out-str (util/link-framework "Cocoa"))
"jank-build::link-framework=Cocoa\n"))
(is (= (with-out-str (util/rerun-if-changed "/home"))
"jank-build::rerun-if-changed=/home\n"))
(is (= (with-out-str (util/rerun-if-env-changed "PATH"))
"jank-build::rerun-if-env-changed=PATH\n")))
Loading