Upload page content

You can upload content for the page named below. If you change the page name, you can also upload content for another page. If the page name is empty, we derive the page name from the file name.

File to load page content from
Page name
Comment

Locked History Actions

sbt/Getting-Started-Scopes

スコープ

https://github.com/harrah/xsbt/wiki/Getting-Started-Scopesの訳(2011/10/27時点)

ここではスコープを説明する。前のページ「.sbtビルド定義」を読んで理解していることを前提にするよ。

キーについてのすべて

nameのようなキーはsbtのキー・値マップの一つのエントリになるといった説明を以前にしたが、これは話を単純化したものだ。 実を言えば、それぞれのキーは複数のコンテキスト、これをスコープというのだが、に関連した値を持つことができるんだ。

例を示そう。

  • ビルド定義の中に複数のプロジェクトがあるなら、キーはそれぞれのプロジェクトについて別の値を持ちうる。
  • compileキーはメインソースについてと、テスト用ソースについては別の値を持ちうる。もし違ったやり方でコンパイルしたいのであれば。
  • package-optionsキー(jarパッケージ生成時のオプションを含んでいる)はクラスファイルのパッケージング(package-bin)時とソースコードのパッケージング(package-src)時では異なる値を持つ。

キー名称に対する唯一の値は無く、スコープによって異なるんだ。

しかし、スコープ付のキーには唯一の値がある。

以前に説明したように、sbtがプロジェクトを表現するキー・値マップを作成するためにsettingのリストを処理するのだとしたら、そのキー・値マップの中のキーはスコープ付のキーだ。 ビルド定義(例えばbuild.sbt)中のそれぞれのsettingもまたスコープ付キーに適用される。

スコープはデフォルトのあることが多いのだが、しかしそれが間違っているなら、build.sbt中で望むスコープを指定しなければならない。

スコープ軸

スコープ軸とはタイプであり、そのタイプの各インスタンスはそれ自身のスコープを定義する。(つまり、各インスタンスはキーに対するそれ自身のユニークな値を持つ)。

スコープ軸としては三つある。

  • プロジェクト
  • コンフィギュレーション
  • タスク

プロジェクト軸によるスコープ

もし一つのビルド中に複数のプロジェクトを格納するなら、各プロジェクトはそれ自身のsettingを持たなければならない。 つまり、キーはプロジェクトによってスコープされるわけだ。

プロジェクト軸は、「全体ビルド」に設定されることもある。このとき、settingは個々のプロジェクトではなく、全体ビルドに適用される。 ビルドレベルsettingは、しばしばプロジェクト特有のsettingの無いときのフォールバックとして使用される。

コンフィギュレーション軸によるスコープ

コンフィギュレーションはビルドの「フレーバー」を定義する。 多くは、それ自身のクラスパス、ソース、生成パッケージ等を持つ。 コンフィギュレーションという概念はIvyから借りてきている which sbt uses for managed dependencies, and from MavenScopes.

sbtにあるコンフィギュレーションの一部としては以下だ。

  • Compileはメインビルドを定義する(src/main/scala).
  • Testはテストノビルド方法を定義する(src/test/scala).
  • Runtimeはrun taskのためのクラスパスを定義する

コンパイル、パッケージング、実行に関連するキーは、デフォルトではコンフィギュレーションにスコープ付されており、それぞれのコンフィギュレーションで異なる働きをする。 最も明らかな例としては、compile,package,runというタスクキーだが、これらに影響を与えるキー(source-directories、scalac-options、full-classpathなど) もまたコンフィギュレーションにスコープ付されている。

タスク軸によるスコープ

settingはタスクの働きに影響を与えることがある。例えば、packag-optionsというsettingはpackage-srcタスクに影響を与える。

これを実現するため、タスクキー(package-srcのような)は別のキー(package-optionのような)のスコープになるようにしてある。

パッケージビルドのためのタスク(package-src, package-bin, package-doc)は関連するパッケージングに関するキー、つまりartifact-namやpackag-optionsを共有できるが、これらのキーは各パッケージタスクで異なる値を持つこともできる。

グローバルスコープ

各スコープ軸は、その軸タイプのインスタンスを代入しうるが(例えば、タスク軸はタスクを代入しうる)、軸には特別な値Globalを代入することもできる。

Globalはその名の通りの働きだ。settingの値は、その軸のすべてのインスタンスについて適用される。例えば、タスク軸がGlobalの場合、settingは全タスクに適用される。

委譲

スコープ付きキーは、そのスコープに対応する値を持たなければ未定義となる。

それぞれのスコープについて、sbtは、他のスコープからなるフォールバックサーチパスを持つ。 典型的には、キーが特化的なキーについて関連づけされた値を持たないのであれば、より一般的なスコープからそれを取得しようとする。 これにはGlobalスコープや全体ビルドスコープがある。

この機能により、一般的なスコープについての値を設定しておけば、複数のより特化したスコープにそれを継承させることができるんだ。

inpectコマンドを使用して、フォールバックサーチパスあるいは「委譲」を調べることができる。 これを以下に述べるので読み進めて欲しい。

sbt実行時のスコープ付キーの参照

コマンドラインや対話モードにおいて、sbtはスコープ付キーを以下のように表示(あるいは解析)する

{<build-uri>}<project-id>/config:key(for task-key)
  • {<build-uri>}<project-id> はプロジェクト軸を指定する。プロジェクト軸が全体ビルドスコープの場合には、<project-id>パートは存在しない

  • configはコンフィギュレーション軸を指定する
  • (for task-key)はタスク軸を指定する
  • keyはスコープ付されるキーを指定する

「*」はどの軸にも指定することができ、これはGlobalスコープを表す。

もしスコープ付キーのパートを省略した場合、それらは以下のように推測される。

  • プロジェクトを省略すると、「現在のプロジェクト」が使用される。
  • コンフィギュレーションを省略すると、キー依存コンフィギュレーションが自動検出される。
  • タスクを省略すると、Globalタスクスコープが使用される。

詳細については「Inspecting Settings」を参照のこと。

スコープ付キーの記述例

  • full-classpath キーのみなのでデフォルトスコープが使用される。つまり、現在のプロジェクト、キー依存のコンフィギュレーション、グローバルタスクスコープだ。
  • test:full-classpath  コンフィギュレーションが指定されているので、testコンフィギュレーションにおけるfull-classpathになる。他の二つのスコープ軸はデフォルトだ。
  • *:full-classpath コンフィギュレーションについてGlobalを指定している。
  • full-classpath(for doc) docタスクにスコープ付されたfull-classpathを指定している。プロジェクトとコンフィギュレーションについてはデフォルトだ。
  • {file:/home/hp/checkout/hello/}default-aea33a/test:full-classpath specifies a project, {file:/home/hp/checkout/hello/}default-aea33a, where the project is identified with the build {file:/home/hp/checkout/hello/} and then a project id inside that build default-aea33a. Also specifies configuration test, but leaves the default task axis.

  • {file:/home/hp/checkout/hello/}/test:full-classpath sets the project axis to "entire build" where the build is {file:/home/hp/checkout/hello/}

  • {.}/test:full-classpath sets the project axis to "entire build" where the build is {.}. {.} can be written ThisBuild in Scala code.

  • {file:/home/hp/checkout/hello/}/compile:full-classpath(for doc) sets all three scope axes.

スコープの調査

対話モードでinspectコマンドを使うことにより、キーとそのスコープを調査することができる。 test:full-classpathをinspectしてみよう。

$ sbt
> inspect test:full-classpath
[info] Task: scala.collection.Seq[sbt.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]  {file:/home/hp/checkout/hello/}default-aea33a/test:full-classpath
[info] Dependencies:
[info]  test:exported-products
[info]  test:dependency-classpath
[info] Reverse dependencies:
[info]  test:run-main
[info]  test:run
[info]  test:test-loader
[info]  test:console
[info] Delegates:
[info]  test:full-classpath
[info]  runtime:full-classpath
[info]  compile:full-classpath
[info]  *:full-classpath
[info]  {.}/test:full-classpath
[info]  {.}/runtime:full-classpath
[info]  {.}/compile:full-classpath
[info]  {.}/*:full-classpath
[info]  */test:full-classpath
[info]  */runtime:full-classpath
[info]  */compile:full-classpath
[info]  */*:full-classpath
[info] Related:
[info]  compile:full-classpath
[info]  compile:full-classpath(for doc)
[info]  test:full-classpath(for doc)
[info]  runtime:full-classpath

最初の行は、これがタスクであることを示している(「.sbt build definition」で説明したようにsettingではない)。 また、タスクの結果値が「scala.collection.Seq[sbt.Attributed[java.io.File]]」というタイプであることを示している。

"Provided by"はこの値を定義するスコープ付キーを示している。 この場合には

{file:/home/hp/checkout/hello/}default-aea33a/test:full-classpath

になるが、「test」コンフィギュレーションスコープと「{file:/home/hp/checkout/hello/}default-aea33a」というプロジェクトスコープが付いている。

"Dependencies"についてはまだ説明していない。チャンネルはそのままだよ。

委譲(delegates)も表示されている。値が定義されていなければ、sbtは以下を検索する。

  • two other configurations (runtime:full-classpath, compile:full-classpath). In these scoped keys, the project is unspecified meaning "current project" and the task is unspecified meaning Global
  • configuration set to Global (*:full-classpath), since project is still unspecified it's "current project" and task is still unspecified so Global
  • project set to {.} or ThisBuild (meaning the entire build, no specific project)

  • project axis set to Global (*/test:full-classpath) (remember, an unspecified project means current, so searching Global here is new; i.e. * and "no project shown" are different for the project axis; i.e. */test:full-classpath is not the same as test:full-classpath)
  • both project and configuration set to Global (*/*:full-classpath) (remember that unspecified task means Global already, so */*:full-classpath uses Global for all three axes)

(上記の例のinspect test:full-classpathとは異なる)「inspect full-classpath」を実行して、違いを確認してみよう。 コンフィギュレーションが省略されているが、compileであると自動推論される。 したがって、「inspect compile:full-classpath」と入力しても同じ結果が現れる。

さらに「inspect *:full-classpath」としてみよう。 full-classpathはデフォルトではGlobalコンフィギュレーションでは定義されていない。

さらなる詳細は「Inspecting Settings」を参照して欲しい

ビルド定義中でのスコープの参照

build.sbt中において、裸のキーでsettingを作成すると、それは現在のプロジェクトにスコープされ、コンフィギュレーション及びタスクはGlobalになる。

name := "hello"

sbtを起動してnameをinspectし、そのProvided byを見てみよう。

{file:/home/hp/checkout/hello/}default-aea33a/*:name

となるが、これはプロジェクトが「{file:/home/hp/checkout/hello/}default-aea33a」であり、コンフィギュレーションが「*」つまりGlobalであり、タスクは示されていない(これもGlobalを意味する)。

build.sbtは常に単一プロジェクトのsettingしか定義しない。したがって、「現在のプロジェクト」とは、そのbuild.sbtの存在するプロジェクトのことだ。 (マルチプロジェクトビルドでは、それぞれのプロジェクトはそれ自身のbuild.sbtを持つ)。

キーは「in」というオーバロードされたメソッドを持ち、これはスコープを設定する。 inの引数には、どのスコープ軸のインスタンスをも与えることができる。 例えば、こんなことはしないと思うけど、Compileコンフィギュレーションスコープを指定してnameを設定することができる。

name in Compile := "hello"

あるいは、package-binタスクのスコープをつけることもできる(意味が無い!ただの例だよ)。

name in packageBin := "hello"

あるいは、複数のスコープ軸をつけることができる。例えばpackageBinタスクとCompileコンフィギュレーションにするには、

name in (Compile, packageBin) := "hello"

すべての軸についてGlobalにするには、

name in Global := "hello"

(「name in Global」は暗黙的にすべての軸をGlobalにする。タスクとコンフィゆレーションは既にGlobalだからだ。 だから、「*/*:name」になる。「{file:/home/hp/checkout/hello/}default-aea33a/*:name」ではなく)。

もしScalaを使ったことが無いのなら覚えておこう。 「in」や「:=」はただのメソッドであって、マジックではないことを理解するのは重要だ。 Scalaはそれらのナイスな書き方を許しているのだが、Javaスタイルにすることも可能だ。

name.in(Compile).:=("hello")

醜い書き方をする必要はないが、しかしそれらが確かにメソッドであることはわかるだろう。

スコープをいつ指定するべきか?

キーが通常スコープ付されるものであれば、スコープを指定すべきだ。 例えば、compileタスクは通常Compile及びTestコンフィギュレーションにスコープ付されており、これらのスコープ外では存在しようがない。

compileキーの値を変更するには、「compile in Compile」あるいは「compile in Test」と記述する必要がある。 単に「compile」と記述してしまうと、現在のプロジェクトにスコープ付された新しいcompileタスクを定義してしまうことになり、 コンフィギュレーションにスコープ付された標準的なcompileタスクをオーバライドすることにはならない。

もし"Reference to undefined setting"などというエラーが発生した場合、それはたいていスコープを指定し忘れているか、あるいは間違ったスコープをし指定しているかのどちらかだ。 君のキーは何か他のスコープで定義されているのかもしれない。sbtはエラーメッセージの中で示唆しようとする。"Did you mean compile:compile?"等のメッセージをよく見てみよう。

「名前」がキーのほんの一部でしか無いことを意識しよう。 実際に、すべてのキーは名前とスコープから構成される(しかもスコープは三つの軸からなる)。 言い換えれば「packageOptions in (Compile, packageBin)」という式がキー名称なのだ。 単純には、packageOptionもキー名称なのだが、しかし異なるものだ (もしin無しで使用すれば、スコープは暗黙的に、現在のプロジェクト、Globalコンフィギュレーション、Globalタスクになる)

次はSettingについてもっと