このページでは、スコープについて説明します。ここでは、前のページであるビルド定義とタスクグラフを読んだことを前提とします。
以前、name
のようなキーは、sbt のキーと値のペアのマップ内の 1 つのエントリに対応すると説明しました。これは簡略化した説明でした。
実際には、各キーは、スコープと呼ばれる複数のコンテキストで関連付けられた値を持つことができます。
具体的な例
compile
キーは、メインソースとテストソースで異なる値を持ち、それらを異なる方法でコンパイルすることができます。packageOptions
キー (jar パッケージを作成するためのオプションを含む) は、クラスファイルをパッケージ化するとき (packageBin
) とソースコードをパッケージ化するとき (packageSrc
) で異なる値を持つ場合があります。特定のキー name
の単一の値はありません。これは、値がスコープによって異なる場合があるためです。
ただし、特定のスコープ付きキーには単一の値があります。
sbt がプロジェクトを記述するキーと値のマップを生成するために設定リストを処理することを考えると、前述のように、そのキーと値のマップのキーはスコープ付きキーです。ビルド定義 (たとえば build.sbt
) で定義された各設定は、スコープ付きキーにも適用されます。
多くの場合、スコープは暗黙的であるか、デフォルトがありますが、デフォルトが間違っている場合は、build.sbt
で必要なスコープを指定する必要があります。
スコープ軸は、Option[A]
に似た型コンストラクターであり、スコープ内のコンポーネントを形成するために使用されます。
スコープ軸は 3 つあります
軸の概念に慣れていない場合は、RGB カラーキューブを例として考えることができます
RGB カラーモデルでは、すべての色は、軸が赤、緑、青のコンポーネントに対応するキューブ内の点で表され、数値でエンコードされます。同様に、sbt の完全なスコープは、サブプロジェクト、構成、タスク値のタプルによって形成されます。
projA / Compile / console / scalacOptions
これは sbt 1.1 で導入されたスラッシュ構文です。
scalacOptions in (
Select(projA: Reference),
Select(Compile: ConfigKey),
Select(console.key)
)
1 つのビルドに複数のプロジェクトを配置する場合、各プロジェクトには独自の設定が必要です。つまり、キーはプロジェクトに応じてスコープ指定できます。
プロジェクト軸は ThisBuild
に設定することもできます。これは「ビルド全体」を意味するため、設定は単一のプロジェクトではなくビルド全体に適用されます。ビルドレベルの設定は、プロジェクトがプロジェクト固有の設定を定義していない場合のフォールバックとしてよく使用されます。ビルドレベルの設定については、このページで後ほど詳しく説明します。
依存関係構成 (または略して「構成」) は、独自のクラスパス、ソース、生成されたパッケージなどを持つ可能性のあるライブラリ依存関係のグラフを定義します。依存関係構成の概念は、sbt が管理された依存関係に使用していた Ivy に由来し、ライブラリ依存関係、およびMaven スコープに由来します。
sbt で見られる構成のいくつか
src/main/scala
) を定義する Compile
。src/test/scala
) のビルド方法を定義する Test
。run
タスクのクラスパスを定義する Runtime
。デフォルトでは、コンパイル、パッケージング、実行に関連するすべてのキーは構成にスコープ指定されるため、各構成で異なる動作をする可能性があります。最も明白な例は、タスクキーの compile
、package
、run
です。ただし、それらのキーに影響を与えるすべてのキー (sourceDirectories
、scalacOptions
、fullClasspath
など) も構成にスコープ指定されます。
構成についてもう 1 つ注意すべき点は、他の構成を拡張できるということです。次の図は、最も一般的な構成間の拡張関係を示しています。
Test
および IntegrationTest
は Runtime
を拡張し、Runtime
は Compile
を拡張し、CompileInternal
は Compile
、Optional
、および Provided
を拡張します。
設定は、タスクの動作に影響を与える可能性があります。たとえば、packageSrc
タスクは packageOptions
設定の影響を受けます。
これをサポートするために、タスクキー (packageSrc
など) は、別のキー (packageOptions
など) のスコープになる可能性があります。
パッケージをビルドするさまざまなタスク (packageSrc
、packageBin
、packageDoc
) は、artifactName
や packageOptions
などのパッケージングに関連するキーを共有できます。これらのキーは、各パッケージングタスクで異なる値を持つことができます。
各スコープ軸は、軸型のインスタンス (Some(_)
に類似) で埋めるか、軸を特別な値 Zero
で埋めることができます。したがって、Zero
を None
として考えることができます。
Zero
はすべてのスコープ軸のユニバーサルフォールバックですが、ほとんどの場合、その直接的な使用は sbt およびプラグイン作成者専用にする必要があります。
Global
は、すべての軸に Zero
を設定するスコープです: Zero / Zero / Zero
。つまり、Global / someKey
は Zero / Zero / Zero / someKey
の省略形です。
build.sbt
で裸のキーを使用して設定を作成した場合、それは (現在のサブプロジェクト / 構成 Zero
/ タスク Zero
) にスコープ指定されます
lazy val root = (project in file("."))
.settings(
name := "hello"
)
sbt を実行し、inspect name
を実行すると、ProjectRef(uri("file:/private/tmp/hello/"), "root") / name
によって提供されていることがわかります。つまり、プロジェクトは ProjectRef(uri("file:/Users/xxx/hello/"), "root")
であり、構成もタスクスコープも表示されていません (つまり、Zero
)。
右側の裸のキーも (現在のサブプロジェクト / 構成 Zero
/ タスク Zero
) にスコープ指定されます
organization := name.value
スコープ軸の型は、/
演算子を持つようにメソッドが拡張されました。/
への引数には、キーまたは別のスコープ軸を指定できます。したがって、例えば、これを行う正当な理由はありませんが、Compile
構成にスコープされたname
キーのインスタンスを持つことができます。
Compile / name := "hello"
または、packageBin
タスクにスコープされた名前を設定することもできます(無意味です!単なる例です)。
packageBin / name := "hello"
または、複数のスコープ軸を持つname
を設定することもできます。例えば、Compile
構成のpackageBin
タスクで設定できます。
Compile / packageBin / name := "hello"
または、Global
を使用することもできます。
// same as Zero / Zero / Zero / concurrentRestrictions
Global / concurrentRestrictions := Seq(
Tags.limitAll(1)
)
(Global / concurrentRestrictions
は、暗黙的にZero / Zero / Zero / concurrentRestrictions
に変換され、すべての軸をZero
スコープコンポーネントに設定します。タスクと構成はデフォルトですでにZero
であるため、ここではプロジェクトをZero
にする効果があります。つまり、ProjectRef(uri("file:/tmp/hello/"), "root") / Zero / Zero / concurrentRestrictions
ではなく、Zero / Zero / Zero / concurrentRestrictions
を定義します。)
コマンドラインおよびsbtシェルでは、sbtは次のようにスコープ付きキーを表示(および解析)します。
ref / Config / intask / key
ref
は、サブプロジェクト軸を識別します。これは、<project-id>
、ProjectRef(uri("file:..."), "id")
、または「ビルド全体」スコープを示すThisBuild
のいずれかです。Config
は、大文字のScala識別子を使用して、構成軸を識別します。intask
は、タスク軸を識別します。key
は、スコープ設定されるキーを識別します。各軸にZero
が表示される可能性があります。
スコープ付きキーの一部を省略すると、次のように推論されます。
詳細については、設定システムとの対話を参照してください。
fullClasspath
はキーのみを指定しているため、デフォルトのスコープ(現在のプロジェクト、キーに依存する構成、およびZero
タスクスコープ)が使用されます。Test / fullClasspath
は構成を指定しているため、これはTest
構成でのfullClasspath
であり、他の2つのスコープ軸はデフォルトになります。root / fullClasspath
は、プロジェクトIDで識別されるプロジェクトroot
を指定します。root / Zero / fullClasspath
は、プロジェクトroot
を指定し、デフォルトの構成ではなく、構成にZero
を指定します。doc / fullClasspath
は、doc
タスクにスコープされたfullClasspath
キーを指定し、プロジェクト軸と構成軸はデフォルトになります。ProjectRef(uri("file:/tmp/hello/"), "root") / Test / fullClasspath
は、プロジェクトProjectRef(uri("file:/tmp/hello/"), "root")
を指定します。また、構成Testを指定し、デフォルトのタスク軸のままにします。ThisBuild / version
は、サブプロジェクト軸を「ビルド全体」に設定します。ここで、ビルドはThisBuild
であり、デフォルトの構成になります。Zero / fullClasspath
は、サブプロジェクト軸をZero
に設定し、デフォルトの構成になります。root / Compile / doc / fullClasspath
は、3つのスコープ軸すべてを設定します。sbtシェルでは、inspect
コマンドを使用して、キーとそのスコープを理解できます。inspect Test/fullClasspath
を試してください。
$ sbt
sbt:Hello> inspect Test / fullClasspath
[info] Task: scala.collection.Seq[sbt.internal.util.Attributed[java.io.File]]
[info] Description:
[info] The exported classpath, consisting of build products and unmanaged and managed, internal and external dependencies.
[info] Provided by:
[info] ProjectRef(uri("file:/tmp/hello/"), "root") / Test / fullClasspath
[info] Defined at:
[info] (sbt.Classpaths.classpaths) Defaults.scala:1639
[info] Dependencies:
[info] Test / dependencyClasspath
[info] Test / exportedProducts
[info] Test / fullClasspath / streams
[info] Reverse dependencies:
[info] Test / testLoader
[info] Delegates:
[info] Test / fullClasspath
[info] Runtime / fullClasspath
[info] Compile / fullClasspath
[info] fullClasspath
[info] ThisBuild / Test / fullClasspath
[info] ThisBuild / Runtime / fullClasspath
[info] ThisBuild / Compile / fullClasspath
[info] ThisBuild / fullClasspath
[info] Zero / Test / fullClasspath
[info] Zero / Runtime / fullClasspath
[info] Zero / Compile / fullClasspath
[info] Global / fullClasspath
[info] Related:
[info] Compile / fullClasspath
[info] Runtime / fullClasspath
最初の行には、これが(.sbtビルド定義で説明されているように、設定とは対照的に)タスクであることがわかります。タスクの結果の値の型は、scala.collection.Seq[sbt.Attributed[java.io.File]]
になります。
「Provided by」は、値を定義するスコープ付きキーを指します。この場合は、ProjectRef(uri("file:/tmp/hello/"), "root") / Test / fullClasspath
(Test
構成とProjectRef(uri("file:/tmp/hello/"), "root")
プロジェクトにスコープされたfullClasspath
キー)です。
「Dependencies」は、前のページで詳細に説明しました。
「Delegates」については後で説明します。
inspect fullClasspath
(上記の例とは対照的に、inspect Test / fullClasspath
)を試して、違いを感じてください。構成が省略されているため、Compile
として自動検出されます。したがって、inspect Compile / fullClasspath
はinspect fullClasspath
と同じように見えるはずです。
別のコントラストとして、inspect ThisBuild / Zero / fullClasspath
を試してください。fullClasspath
は、デフォルトではZero
構成スコープでは定義されていません。
繰り返しますが、詳細については、設定システムとの対話を参照してください。
問題のキーが通常スコープされている場合は、スコープを指定する必要があります。例えば、compile
タスクは、デフォルトではCompile
構成とTest
構成にスコープされており、これらのスコープの外には存在しません。
compile
キーに関連付けられた値を変更するには、Compile / compile
またはTest / compile
と記述する必要があります。単にcompile
を使用すると、構成にスコープされた標準のコンパイルタスクを上書きするのではなく、現在のプロジェクトにスコープされた新しいコンパイルタスクが定義されます。
「未定義の設定への参照」のようなエラーが発生した場合は、スコープの指定に失敗したか、間違ったスコープを指定したことがよくあります。使用しているキーは、他のスコープで定義されている可能性があります。sbtは、エラーメッセージの一部として、何を意味したかを示唆しようとします。「Did you mean Compile / compile?」を探してください。
それを考える1つの方法は、名前はキーの一部にすぎないということです。実際には、すべてのキーは名前とスコープの両方で構成されています(スコープには3つの軸があります)。式全体Compile / packageBin / packageOptions
は、言い換えれば、キー名です。単にpackageOptions
もキー名ですが、別のものです(スラッシュのないキーの場合、スコープが暗黙的に想定されます:現在のプロジェクト、Zero
構成、Zero
タスク)。
サブプロジェクト間で共通の設定をファクタリングするための高度なテクニックは、ThisBuild
にスコープされた設定を定義することです。
特定のサブプロジェクトにスコープされたキーが見つからない場合、sbtはフォールバックとしてThisBuild
でそれを探します。このメカニズムを使用すると、version
、scalaVersion
、organization
などの頻繁に使用されるキーに対して、ビルドレベルのデフォルト設定を定義できます。
ThisBuild / organization := "com.example",
ThisBuild / scalaVersion := "2.12.18",
ThisBuild / version := "0.1.0-SNAPSHOT"
lazy val root = (project in file("."))
.settings(
name := "Hello",
publish / skip := true
)
lazy val core = (project in file("core"))
.settings(
// other settings
)
lazy val util = (project in file("util"))
.settings(
// other settings
)
便宜上、キーと設定式の本体の両方をThisBuild
にスコープ設定するinThisBuild(...)
関数があります。設定式をそこに入れることは、可能な限りThisBuild /
を前に付けるのと同じです。
後で説明するスコープ委譲の性質上、ビルドレベルの設定は、純粋な値、またはGlobal
またはThisBuild
スコープからの設定のみに設定する必要があります。
スコープ付きキーは、そのスコープに値が関連付けられていない場合、未定義になる可能性があります。
sbtには、各スコープ軸に対して、他のスコープ値で構成されるフォールバック検索パスがあります。通常、キーがより具体的なスコープに関連付けられた値を持たない場合、sbtはThisBuild
スコープなどのより一般的なスコープから値を取得しようとします。
この機能により、より一般的なスコープで値を1回設定することで、複数のより具体的なスコープが値を継承できます。後でスコープ委譲について詳しく説明します。