Locked History Actions

sbt/Getting-Started-Full-Def

.scalaビルド定義

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

This page assumes you've read previous pages in the Getting Started Guide, especially .sbt build definition and more about settings.

sbtは再帰的である

build.sbtはとても単純だ。sbtが実際にどのように動作するかは隠されている。 sbtのビルドはScalaコードで定義される。 このコードは、それ自体がビルドされる必要があるのだが、それを行うのにsbt以外の適任者があるだろうか?

君自身のプロジェクトの中にはprojectというディレクトリがあるはずだが、これがもう一つのプロジェクトであり、それが君のプロジェクトのビルドの仕方を決めている。プロジェクト中のプロジェクトは(理屈の上では)、他のプロジェクトが可能な、いかなることも可能だ。 君のビルド定義はsbtプロジェクトなのだ。

そして、これを更に掘り下げることもできる。望むなら、project/projectディレクトリを作って、ビルド定義プロジェクトのビルド定義を記述することもできる。以下に示そう。

 hello/                  # 君のプロジェクトのベースディレクトリ

     Hello.scala         # 君のプロジェクトのソースファイル (src/main/scalaに
                         #   あってもよい)

     build.sbt           # build.sbtはproject/下にあるビルド定義プロジェクトのソースコードの一つ
                         # 

     project/            # ビルド定義プロジェクトのベースディレクトリ

         Build.scala     # project/プロジェクトのソースファイルの一つ
                         #   つまり、ビルド定義のソースファイルの一つ

         build.sbt       # これはproject/projectというプロジェクトのビルド定義の一部
                         #   つまり、ビルド定義のビルド定義
                         #


         project/        # ビルド定義のビルド定義のベースディレクトリ
                         #

             Build.scala # project/project/プロジェクトのソースファイル

心配しないで!たいていは、こんなものは必要ないよ。 理屈を知ることは助けになるだろうという程度だ。

ところで、.scalaや.sbtで終わるいかなる名前のファイルでもいい。 build.sbtやBuild.scalaという名前は便宜的なものだ。 これはつまり、複数のファイルが存在しうるということだ。

ビルド定義プロジェクト中の.scalaソースファイル

.sbtファイルはその兄弟プロジェクトディレクトリにマージされる。 先のプロジェクトレイアウトに戻ろう。

 hello/                  # 君のプロジェクトのベースディレクトリ

     Hello.scala         # 君のプロジェクトのソースファイル (src/main/scalaに
                         #   あってもよい)

     build.sbt           # build.sbtはproject/下にあるビルド定義プロジェクトのソースコードの一つ
                         # 

     project/            # ビルド定義プロジェクトのベースディレクトリ

         Build.scala     # project/プロジェクトのソースファイルの一つ
                         #   つまり、ビルド定義のソースファイルの一つ

build.sbt中のScala式はコンパイルされて、Build.scala(あるいはproject/ディレクトリ中の.scalaファイル)にマージされる。

.sbt files in the base directory for a project become part of the project build definition project also located in that base directory.

The .sbt file format is a convenient shorthand for adding settings to the build definition project.

build.sbtのBuild.scalaへの関連付け

ビルド定義中で、.sbtと.scalaファイルをミックスするには、まずそれらがどのように関連付けられるかを知っておく必要がある。 二つのファイルで示そう。 第一に、君のプロジェクトがhelloにあり、hello/project/Build.scalaというファイルを次のように作成したとする。

import sbt._
import Keys._

object HelloBuild extends Build {

    val sampleKeyA = SettingKey[String]("sample-a", "demo key A")
    val sampleKeyB = SettingKey[String]("sample-b", "demo key B")
    val sampleKeyC = SettingKey[String]("sample-c", "demo key C")
    val sampleKeyD = SettingKey[String]("sample-d", "demo key D")

    override lazy val settings = super.settings ++
        Seq(sampleKeyA := "A: in Build.settings in Build.scala", resolvers := Seq())

    lazy val root = Project(id = "hello",
                            base = file("."),
                            settings = Project.defaultSettings ++ Seq(sampleKeyB := "B: in the root project settings in Build.scala"))
}

そして、hello/build.sbtを次のように記述する。

sampleKeyC in ThisBuild := "C: in build.sbt scoped to ThisBuild"

sampleKeyD := "D: in build.sbt"

sbt対話モードを開始しよう。「inspect sample-a」とタイプすると、以下が表示されるだろう。

[info] Setting: java.lang.String = A: in Build.settings in Build.scala
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}/*:sample-a

今度は「inspect sample-c」の結果だ。

[info] Setting: java.lang.String = C: in build.sbt scoped to ThisBuild
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}/*:sample-c

"Provided by"は、二つの値について全く同じスコープを示すことに注意すること。 つまり、.sbtファイル中のsampleKeyC in ThisBuildは、.scala中のBuild.settingsリスト中におかれたものと等価だということだ。sbtは両方からビルドスコープのsettingを取得してビルド定義を作成する。

次に「inspect sample-b」の結果だ。

[info] Setting: java.lang.String = B: in the root project settings in Build.scala
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}hello/*:sample-b

sample-bはプロジェクトにスコープ付けされていることに注意。 つまり、「{file:/home/hp/checkout/hello/}」ではなく「{file:/home/hp/checkout/hello/}hello」だ。

ご想像の通り、inspect sample-dはsample-bに一致する。

[info] Setting: java.lang.String = D: in build.sbt
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}hello/*:sample-d

sbtはBuild.settingsとProject.settingsから取得したsettingに.sbtファイルから取得したsettingを追加するが、 それは.sbtによるsettingを優先しているからだ。 Build.scalaにsample-cやsample-dを設定しても、それらはbuild.sbtでも設定されている。 build.sbtのsettinはBuild.scalaの設定に「勝つ」のだ。

もう一つ注意しなければならないことは、sampleKeyCとsampleKeyDはbuild.sbtの中でだけ有効ということ。 だから、sbtはBuildオブジェクトの中身を.sbtにインポートする。 このケースでは、build.sbtファイル中には暗黙的に「import HelloBuild._」がある。

サマリ:

  • .scal中でBuild.settingsにsettingを追加することができる。それらは自動的にビルドスコープになる。
  • .scala中でProject.settingにsettingを追加することができる。それらは自動的にプロジェクトスコープになる。
  • .scala中に記述したすべてのBuildオブジェクトは自動的に.sbtファイルにインポートされる。
  • .sbtに記述したsettingは.scalaに記述したsettingに追加される。
  • .sbtに記述したsettingは明示的にスコープを指定しない限りプロジェクトスコープになる。

いつ.scalaファイルを使うか

.scalaファイル中では、記述できるsetting式に制限はない。 val, object, methodなど、いかなるScalaコードも記述することができる。

おすすめのアプローチとしては、.sbtファイルをつかって、setting定義を書き、val, object, method定義が必要になったときに.scalaファイルを使うという方法だ。

.sbtでは単一の式しか許されないので、それらの式の間でのコードの共有はできない。 共有が必要になったとき、共通変数やメソッド定義のために.scalaファイルが必要になるだろう。

There's one build definition, which is a nested project inside your main project. .sbt and .scala files are compiled together to create that single definition.

一つのビルドで複数のプロジェクトを扱いたい場合にも.scalaファイルが必要になる。 これについてはMulti-Project Buildsを参照のこと。

(マルチプロジェクトで.sbtファイルを使うことのデメリットとしては、それらが異なるディレクトリに散らばってしまうことだ。 このような理由から、サブプロジェクトがある場合は、.scalaファイルにsettingを記述するのを好む人達もいる。 この点についてはmulti-project buildsを読めば明確になるだろう)。

対話モードでのビルド定義

You can switch the sbt interactive prompt to have the build definition project in project/ as the current project. To do so, type reload plugins.

> reload plugins
[info] Set current project to default-a0e8e4 (in build file:/home/hp/checkout/hello/project/)
> show sources
[info] ArrayBuffer(/home/hp/checkout/hello/project/Build.scala)
> reload return
[info] Loading project definition from /home/hp/checkout/hello/project
[info] Set current project to hello (in build file:/home/hp/checkout/hello/)
> show sources
[info] ArrayBuffer(/home/hp/checkout/hello/hw.scala)
>

As shown above, you use reload return to leave the build definition project and return to your regular project.

Reminder: it's all immutable

It would be wrong to think that the settings in build.sbt are added to the settings fields in Build and Project objects. Instead, the settings list from Build and Project, and the settings from build.sbt, are concatenated into another immutable list which is then used by sbt. The Build and Project objects are "immutable configuration" forming only part of the complete build definition.

In fact, there are other sources of settings as well. They are appended in this order:

  • Settings from Build.settings and Project.settings in your .scala files. Your user-global settings; for example in ~/.sbt/build.sbt you can define settings affecting all your projects. Settings injected by plugins, see using plugins coming up next. Settings from .sbt files in the project. Build definition projects (i.e. projects inside project) have settings from global plugins (~/.sbt/plugins) added. Using plugins explains this more.

Later settings override earlier ones. The entire list of settings forms the build definition. Next

次はプラグインの使用