{:paths ["src"] ;; project paths
:deps {} ;; project deps
:aliases
{;; Run with clj -T:build function-in-build
:build {:deps {io.github.clojure/tools.build {:git/tag "TAG" :git/sha "SHA"}}
:ns-default build}}}
tools.build は、Clojureプロジェクトをビルドするための関数のライブラリです。ユーザーが呼び出し可能なターゲット関数を作成するためのビルドプログラムで使用されることを意図しています。また、APIドキュメントも参照してください。
tools.buildの背後にある哲学は、プロジェクトのビルドは本質的にプログラムであるということです。つまり、プロジェクトのソースファイルから1つ以上のプロジェクト成果物を作成するための一連の命令です。このプログラムを、お気に入りのプログラミング言語であるClojureで記述したいと考えており、tools.buildは、ビルドで一般的に必要な関数をまとめたライブラリであり、柔軟な方法で接続できます。ビルドプログラムの記述には、他の宣言型アプローチよりも少し多くのコードが必要になりますが、将来にわたって簡単に拡張またはカスタマイズでき、プロジェクトとともに成長するビルドを作成できます。
インストール手順はありません。tools.buildは、ビルドプログラムが使用する単なるライブラリです。deps.edn
にエイリアスを作成し、tools.buildを依存関係として含め、ビルドプログラムへのソースパスを含めます。ビルドは、Clojure CLI(-Tを使用)でプロジェクト「ツール」として簡単に実行できるように設計されています。Clojure CLIでは、「ツール」は機能を提供するプログラムであり、プロジェクトのdepsやクラスパスは使用しません。-T:an-alias
で実行されるツールは、すべてのプロジェクトのdepsとパスを削除し、"."
をパスとして追加し、:an-alias
で定義されている他のdepsまたはパスを含めます。
そのため、deps.edn
に、ビルドクラスパスを定義し、ビルドソースへのパスを含むエイリアスが必要です。例を次に示します。
{:paths ["src"] ;; project paths
:deps {} ;; project deps
:aliases
{;; Run with clj -T:build function-in-build
:build {:deps {io.github.clojure/tools.build {:git/tag "TAG" :git/sha "SHA"}}
:ns-default build}}}
https://github.com/clojure/tools.build#release-informationで、使用する最新のTAGとSHAを見つけてください。
このガイドのgit depとClojure CLIの例では、Clojure CLI 1.10.3.933以上を使用していることを前提としています。 |
上記のように、-Tを使用してツールを実行すると、プロジェクトの:pathsと:depsを含まないクラスパスが作成されます。-T:build
を使用すると、:build
エイリアスからの:paths
と:deps
のみが使用されます。ルートのdeps.ednは依然として含まれており、Clojureもプルインされます(ただし、tools.buildの依存関係としても含まれます)。:paths
はここでは指定されていないため、追加のパスは追加されませんが、-T
はプロジェクトルート"."
をデフォルトでパスとして含めます。
したがって、clj -T:build jar
を実行すると、次の有効なクラスパスが使用されます。
"."
(-Tによって追加)
org.clojure/clojure (ルートのdeps.edn :deps
から) および推移的なdeps
org.clojure/tools.build (:build
エイリアスの:deps
から) および推移的なdeps
:ns-default
は、クラスパスで指定された関数を見つけるためのデフォルトのClojure名前空間を指定します。ローカルパスはデフォルトの"."
のみであるため、プロジェクトのルートにあるbuild.clj
にビルドプログラムがあるはずです。パスのルート(:build
エイリアスの:paths
経由)と、それらのパスルートに対するビルドプログラム自体の名前空間は、完全にあなたの制御下にあることに注意してください。それらをプロジェクトのサブディレクトリに配置することもできます。
そして最後に、コマンドラインでビルドで実行する関数(ここではjar
)を指定します。その関数は、build
名前空間で実行され、-X
と同じ引数渡しスタイルを使用して構築されたマップが渡されます。引数は、交互のキーと値として提供されます。
このガイドの残りの部分では、個々の一般的なユースケースと、tools.buildプログラムでそれらをどのように満たすかを示します。
最も一般的なClojureビルドでは、Clojureソースコードを含むjarファイルを作成します。tools.buildでこれを行うには、次のタスクを使用します。
create-basis
- プロジェクトのベースを作成するため(注:これにより、副作用としてdepsがダウンロードされます)
copy-dir
- Clojureソースとリソースをワーキングディレクトリにコピーするため
write-pom
- ワーキングディレクトリにpomファイルを書き込むため
jar
- ワーキングディレクトリをjarファイルにまとめるため
build.cljは次のようになります。
(ns build
(:require [clojure.tools.build.api :as b]))
(def lib 'my/lib1)
(def version (format "1.2.%s" (b/git-count-revs nil)))
(def class-dir "target/classes")
(def jar-file (format "target/%s-%s.jar" (name lib) version))
;; delay to defer side effects (artifact downloads)
(def basis (delay (b/create-basis {:project "deps.edn"})))
(defn clean [_]
(b/delete {:path "target"}))
(defn jar [_]
(b/write-pom {:class-dir class-dir
:lib lib
:version version
:basis @basis
:src-dirs ["src"]})
(b/copy-dir {:src-dirs ["src" "resources"]
:target-dir class-dir})
(b/jar {:class-dir class-dir
:jar-file jar-file}))
注意すべき点
これは通常のClojureコードです。エディタでこの名前空間をロードし、REPLでインタラクティブに開発できます。
単一目的のプログラムとして、上部のvarのセットで共有データを構築してもかまいません。
「target」ディレクトリでビルドし、「target/classes」にjarの内容をアセンブルすることを選択していますが、これらのパスには特別なものはありません。完全にあなたの制御下にあります。また、これらのパスや他のパスをここで複数の場所で繰り返していますが、重複を適切に感じる程度に削除できます。
ユーザーが呼び出すbuild/jar
のような大きな関数を組み立てるために、tools.buildのタスク関数を使用しました。これらの関数はパラメーターマップを受け取り、ここでは構成可能なパラメーターを提供しないことを選択しましたが、提供することもできます!
deps.ednファイルは次のようになります。
{:paths ["src"]
:aliases
{:build {:deps {io.github.clojure/tools.build {:git/tag "TAG" :git/sha "SHA"}}
:ns-default build}}}
そして、このビルドは次のように実行できます。
clj -T:build clean
clj -T:build jar
これらをコマンドラインで同時に実行できるようにしたいと考えていますが、これは現在進行中です。
アプリケーションを準備するときは、アプリ全体とライブラリをコンパイルし、全体を単一のuberjarとしてアセンブルするのが一般的です。
メインのClojure名前空間には(:gen-class)
が必要です。例:
(ns my.lib.main
;; any :require and/or :import clauses
(:gen-class))
また、その名前空間には次のような関数が必要です。
(defn -main [& args]
(do-stuff))
コンパイルされたuberjarのビルド例は次のようになります。
(ns build
(:require [clojure.tools.build.api :as b]))
(def lib 'my/lib1)
(def version (format "1.2.%s" (b/git-count-revs nil)))
(def class-dir "target/classes")
(def uber-file (format "target/%s-%s-standalone.jar" (name lib) version))
;; delay to defer side effects (artifact downloads)
(def basis (delay (b/create-basis {:project "deps.edn"})))
(defn clean [_]
(b/delete {:path "target"}))
(defn uber [_]
(clean nil)
(b/copy-dir {:src-dirs ["src" "resources"]
:target-dir class-dir})
(b/compile-clj {:basis @basis
:ns-compile '[my.lib.main]
:class-dir class-dir})
(b/uber {:class-dir class-dir
:uber-file uber-file
:basis @basis
:main 'my.lib.main}))
この例では、compile-clj
にメインの名前空間をコンパイルするように指示しています(デフォルトでは、ソースはベースの:pathsからロードされます)。コンパイルは推移的であり、コンパイルされた名前空間によってロードされたすべての名前空間もコンパイルされます。コードが動的にまたはオプションでロードされる場合は、追加の名前空間を追加する必要がある場合があります。
deps.ednとビルドの実行は、前の例と同じになります。
次のコマンドでuberjarビルドを作成できます。
clj -T:build uber
このビルドの出力は、target/lib1-1.2.100-standalone.jar
にあるuberjarになります。そのjarには、このプロジェクトのコンパイル済みバージョンと、そのすべての依存関係の両方が含まれています。uberjarには、my.lib.main
名前空間(-main
メソッドを持つ必要があります)を参照するマニフェストが含まれており、次のように呼び出すことができます。
java -jar target/lib1-1.2.100-standalone.jar
上記のビルドでは、ビルドの側面をパラメータ化せず、呼び出す関数を選択しただけでした。ビルドをパラメータ化して、dev/qa/prod、またはバージョン、またはその他の要因を区別すると便利な場合があります。コマンドラインでの関数チェーンを考慮して、ビルド関数全体で使用する共通のパラメータセットを確立し、各関数がパラメータを渡すようにすることをお勧めします。
たとえば、ローカル開発環境を設定するために追加の開発リソースセットを含むパラメータ化を考えてみましょう。これを表すために、単純な:env :dev
のキーと値のペアを使用します。
(ns build
(:require [clojure.tools.build.api :as b]))
(def lib 'my/lib1)
(def version (format "1.2.%s" (b/git-count-revs nil)))
(def class-dir "target/classes")
(def jar-file (format "target/%s-%s.jar" (name lib) version))
(def copy-srcs ["src" "resources"])
;; delay to defer side effects (artifact downloads)
(def basis (delay (b/create-basis {:project "deps.edn"})))
(defn clean [params]
(b/delete {:path "target"})
params)
(defn jar [{:keys [env] :as params}]
(let [srcs (if (= env :dev) (cons "dev-resources" copy-srcs) copy-srcs)]
(b/write-pom {:class-dir class-dir
:lib lib
:version version
:basis @basis
:src-dirs ["src"]})
(b/copy-dir {:src-dirs srcs
:target-dir class-dir})
(b/jar {:class-dir class-dir
:jar-file jar-file})
params))
deps.ednと呼び出しのその他の側面は同じままです。
:dev
環境をアクティブにする呼び出しは次のようになります。
clj -T:build jar :env :dev
キーと値のパラメータは、jar
関数に渡されます。
発生する一般的なケースとして、主にClojureプロジェクトにJava実装クラスを1つまたは2つ導入する必要がある場合があります。この場合、Javaクラスをコンパイルし、Clojureソースに含める必要があります。このセットアップでは、Clojureソースがsrc/
にあり、Javaソースがjava/
にあると想定します(これらを実際にどこに配置するかは、もちろんあなた次第です)。
このビルドでは、JavaソースからコンパイルされたクラスとClojureソースを含むjarを作成します。
(ns build
(:require [clojure.tools.build.api :as b]))
(def lib 'my/lib1)
(def version (format "1.2.%s" (b/git-count-revs nil)))
(def class-dir "target/classes")
(def jar-file (format "target/%s-%s.jar" (name lib) version))
;; delay to defer side effects (artifact downloads)
(def basis (delay (b/create-basis {:project "deps.edn"})))
(defn clean [_]
(b/delete {:path "target"}))
(defn compile [_]
(b/javac {:src-dirs ["java"]
:class-dir class-dir
:basis @basis
:javac-opts ["--release" "11"]}))
(defn jar [_]
(compile nil)
(b/write-pom {:class-dir class-dir
:lib lib
:version version
:basis @basis
:src-dirs ["src"]})
(b/copy-dir {:src-dirs ["src" "resources"]
:target-dir class-dir})
(b/jar {:class-dir class-dir
:jar-file jar-file}))
ここのcompile
タスクは、このライブラリの準備タスクとしても使用できます。
詳細なタスクドキュメントについては、APIドキュメントを参照してください。
オリジナル著者: Alex Miller