このページでは、単一のビルドに複数のサブプロジェクトを導入します。
最初に「入門ガイド」の前のページを読んでください。特に、このページを読む前にbuild.sbtを理解する必要があります。
特に、複数のサブプロジェクトが相互に依存し、一緒に変更することが多い場合は、単一のビルドで複数の関連するサブプロジェクトを保持すると便利な場合があります。
ビルド内の各サブプロジェクトには独自のソースディレクトリがあり、パッケージを実行すると独自の jar ファイルが生成され、一般的には他のプロジェクトと同じように機能します。
プロジェクトは、Project型の lazy val を宣言することで定義されます。例:
lazy val util = (project in file("util"))
lazy val core = (project in file("core"))
val の名前は、サブプロジェクトの ID として使用され、sbt シェルでサブプロジェクトを参照するために使用されます。
オプションで、ベースディレクトリが val の名前と同じ場合は、省略できます。
lazy val util = project
lazy val core = project
複数のサブプロジェクトで共通の設定を抽出するには、ThisBuild
にスコープされた設定を定義します。ThisBuild
は、ビルドのデフォルト値を定義するために使用できる特別なサブプロジェクト名として機能します。1 つ以上のサブプロジェクトを定義し、サブプロジェクトが scalaVersion
キーを定義していない場合、ThisBuild / scalaVersion
が検索されます。
制限は、右辺が純粋な値または Global
または ThisBuild
にスコープされた設定である必要があり、サブプロジェクトにスコープされたデフォルト設定がないことです。(「スコープ」を参照)
ThisBuild / organization := "com.example"
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / scalaVersion := "2.12.18"
lazy val core = (project in file("core"))
.settings(
// other settings
)
lazy val util = (project in file("util"))
.settings(
// other settings
)
これで、version
を 1 か所で変更でき、ビルドをリロードするとサブプロジェクト全体に反映されます。
複数のプロジェクトで共通の設定を抽出する別の方法は、commonSettings
という名前のシーケンスを作成し、各プロジェクトで settings
メソッドを呼び出すことです。
lazy val commonSettings = Seq(
target := { baseDirectory.value / "target2" }
)
lazy val core = (project in file("core"))
.settings(
commonSettings,
// other settings
)
lazy val util = (project in file("util"))
.settings(
commonSettings,
// other settings
)
ビルド内のプロジェクトは互いに完全に独立している可能性がありますが、通常は依存関係によって相互に関連付けられます。依存関係には、集約とクラスパスの 2 種類があります。
集約とは、集約プロジェクトでタスクを実行すると、集約されたプロジェクトでもタスクが実行されることを意味します。例:
lazy val root = (project in file("."))
.aggregate(util, core)
lazy val util = (project in file("util"))
lazy val core = (project in file("core"))
上記の例では、ルートプロジェクトは util
と core
を集約します。例のように 2 つのサブプロジェクトで sbt を起動し、コンパイルを試してください。3 つのプロジェクトすべてがコンパイルされるはずです。
この場合、ルートプロジェクトである*集約を実行するプロジェクト*では、タスクごとに集約を制御できます。たとえば、update
タスクの集約を回避するには
lazy val root = (project in file("."))
.aggregate(util, core)
.settings(
update / aggregate := false
)
[...]
update / aggregate
は、update
タスクにスコープされた集約キーです。(「スコープ」を参照)
注: 集約は、集約されたタスクを並行して実行し、それらの間に定義された順序はありません。
プロジェクトは、別のプロジェクトのコードに依存する場合があります。これは、dependsOn
メソッド呼び出しを追加することで実行されます。たとえば、core がクラスパスで util を必要とする場合は、core を次のように定義します。
lazy val core = project.dependsOn(util)
これで、core
のコードは util
のクラスを使用できるようになります。これにより、プロジェクトをコンパイルする際の順序も作成されます。core
をコンパイルする前に、util
を更新してコンパイルする必要があります。
複数のプロジェクトに依存するには、dependsOn(bar, baz)
のように、dependsOn
に複数の引数を使用します。
core dependsOn(util)
は、core
の compile
構成が util
の compile
構成に依存することを意味します。これを明示的に dependsOn(util % "compile->compile")
と記述することもできます。
"compile->compile"
の ->
は「依存する」を意味するため、"test->compile"
は、core
の test
構成が util
の compile
構成に依存することを意味します。
->config
部分を省略すると、->compile
が暗示されるため、dependsOn(util % "test")
は、core
の test
構成が util
の Compile
構成に依存することを意味します。
便利な宣言は "test->test"
で、test
が test
に依存することを意味します。これにより、たとえば、util/src/test/scala
にテスト用のユーティリティコードを配置し、core/src/test/scala
でそのコードを使用できます。
依存関係に対して複数の構成を設定でき、それらはセミコロンで区切られます。たとえば、dependsOn(util % "test->test;compile->compile")
のようにします。
ファイルとサブプロジェクトが非常に多い大規模なプロジェクトでは、sbt は変更されたファイルを継続的に監視する際に最適に動作しなくなる可能性があり、多くのディスクおよびシステム I/O を使用します。
sbt には、trackInternalDependencies
および exportToInternal
設定があります。これらを使用すると、compile
を呼び出すときに、依存サブプロジェクトのコンパイルをトリガーするかどうかを制御できます。どちらのキーも、3 つの値 (TrackLevel.NoTracking
、TrackLevel.TrackIfMissing
、および TrackLevel.TrackAlways
) のいずれかを受け取ります。デフォルトでは、両方とも TrackLevel.TrackAlways
に設定されています。
trackInternalDependencies
が TrackLevel.TrackIfMissing
に設定されている場合、出力ディレクトリに *.class
ファイル (または exportJars
が true
の場合は JAR ファイル) がない限り、sbt は内部 (プロジェクト間) の依存関係を自動的にコンパイルしようとしなくなります。
設定が TrackLevel.NoTracking
に設定されている場合、内部依存関係のコンパイルはスキップされます。クラスパスは引き続き追加され、依存性グラフには依然として依存関係として表示されることに注意してください。動機は、開発中に多くのサブプロジェクトを含むビルドで変更をチェックする I/O オーバーヘッドを削減することです。すべてのサブプロジェクトを TrackIfMissing
に設定する方法を次に示します。
ThisBuild / trackInternalDependencies := TrackLevel.TrackIfMissing
ThisBuild / exportJars := true
lazy val root = (project in file("."))
.aggregate(....)
exportToInternal
設定を使用すると、依存先のサブプロジェクトが内部トラッキングをオプトアウトできます。これは、いくつかのサブプロジェクトを除いてほとんどのサブプロジェクトを追跡する場合に役立ちます。trackInternalDependencies
と exportToInternal
の設定の交差部分を使用して、実際の追跡レベルが決定されます。1 つのプロジェクトをオプトアウトする例を次に示します。
lazy val dontTrackMe = (project in file("dontTrackMe"))
.settings(
exportToInternal := TrackLevel.NoTracking
)
ビルドのルートディレクトリに対してプロジェクトが定義されていない場合、sbt はビルド内の他のすべてのプロジェクトを集約するデフォルトのプロジェクトを作成します。
プロジェクト hello-foo
は base = file("foo")
で定義されているため、サブディレクトリ foo に含まれます。そのソースは、foo/Foo.scala
のように foo
の直下、または foo/src/main/scala
にあります。通常の sbt ディレクトリ構造は、ビルド定義ファイルを除き、foo
の下で適用されます。
sbt インタラクティブプロンプトで、projects
と入力してプロジェクトを一覧表示し、project <projectname>
と入力して現在のプロジェクトを選択します。compile
などのタスクを実行すると、現在のプロジェクトで実行されます。したがって、必ずしもルートプロジェクトをコンパイルする必要はなく、サブプロジェクトのみをコンパイルすることもできます。
subProjectID/compile
のように、プロジェクト ID を明示的に指定することで、別のプロジェクトでタスクを実行できます。
.sbt
ファイル内の定義は、他の.sbt
ファイルからは参照できません。.sbt
ファイル間でコードを共有するには、ビルドルートのproject/
ディレクトリに1つ以上のScalaファイルを定義してください。
詳細は、ビルドの構成を参照してください。
例えば、foo
ディレクトリ内の.sbt
ファイル(例えば、foo/build.sbt
)は、ビルド全体のビルド定義にマージされますが、hello-foo
プロジェクトにスコープされます。
プロジェクト全体が hello にある場合、hello/build.sbt
、hello/foo/build.sbt
、および hello/bar/build.sbt
に異なるバージョン(version := "0.6"
)を定義してみてください。次に、sbt のインタラクティブプロンプトで show version
を実行します。次のような結果が得られるはずです(定義したバージョンに応じて異なります)。
> show version
[info] hello-foo/*:version
[info] 0.7
[info] hello-bar/*:version
[info] 0.9
[info] hello/*:version
[info] 0.5
hello-foo/*:version
は hello/foo/build.sbt
で定義され、hello-bar/*:version
は hello/bar/build.sbt
で定義され、hello/*:version
は hello/build.sbt
で定義されました。スコープ付きキーの構文を思い出してください。各 version
キーは、build.sbt
の場所に基づいてプロジェクトにスコープされます。ただし、3つの build.sbt
はすべて同じビルド定義の一部です。
スタイルの選択
*.sbt
ファイルに記述できます。一方、ルートの build.sbt
は、設定なしで lazy val foo = (project in file("foo"))
の形式で最小限のプロジェクト宣言のみを行います。build.sbt
ファイルにすべてのプロジェクト宣言と設定を記述することをお勧めします。ただし、それはあなた次第です。注意:サブプロジェクト内にプロジェクトサブディレクトリや project/*.scala
ファイルを含めることはできません。foo/project/Build.scala
は無視されます。