= スコープ = [[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はスコープ付キーを以下のように表示(あるいは解析)する {{{ {}/config:key(for task-key) }}} * {} はプロジェクト軸を指定する。プロジェクト軸が全体ビルドスコープの場合には、パートは存在しない * 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タスクになる) 次は[[sbt/Getting-Started-More-About-Settings|Settingについてもっと]]