Revision 4 as of 2011-10-28 08:10:19

Clear message
Locked History Actions

sbt/Getting-Started-Basic-Def

.sbtビルド定義

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

ここではsbtビルド定義について説明するが、いくつかの「理屈」とbuild.sbtの構文が含まれる。 sbtの使い方とこれまでのページを読んだことを前提とするよ。

.sbtと.scala定義の違い

sbtのビルド定義はベースディレクトリに置かれた.sbtファイルに記述してもよいし、projectサブディレクトリに置かれた.scalaファイルに記述してもよい。

どちらか一方をつかってもいいし、両方を使ってもいい。通常は.sbtファイルを利用し、.sbtでは実現できない場合に.scalaを使うというのが良いアプローチだろう。 後者を使うのは、

  • sbtをカスタマイズする。つまり、新しいsettingやtaskを作る。
  • ネストされたサブプロジェクトを定義する。

ここでは、.sbtファイルについて説明する。.scalaについては「.scala build definition」で説明する。

ビルド定義とは何か?

ここは絶対に読むべし

sbtはプロジェクトを検証してビルド定義ファイルを処理したあと、ビルドを記述したイミュータブルなマップ(キー・値ペアの集合)を固定する。

例えば、nameというキーは文字列値をマップするが、それは君のプロジェクトの名前を表す。

ビルド定義ファイルはsbtマップに直接影響するわけではない。

そうではなく、ビルド定義はSetting[T]型の巨大なオブジェクトリストを生成する。Tとはマップ中の値の型だ(ScalaでのSetting[T]とは、JavaでいうSetting<T>のこと)。 Settingはマップに対する変換を表現している。例えば、(マップに対して)新たなキー・値ペアを追加したり、既存の値に追加したりすることだ (関数プログラミングの精神においては、変換は新しいマップを返す。古いマップを更新したりはしない)。

.sbtファイルの中では、プロジェクト名を表すSetting[String]を次のように作成することができる。

name := "hello"

このSetting[String]がマップに新しいnameというキーを追加し(あるいは置換し)、それに"hello"という名前を与える。 変換されたマップはsbtの新たなマップになる。

マップを作成するにあたり、事前にsbtはsettingのリストをソートするので同一のキーに対する変更は束ねられる。 そして、値が他のキーに依存するのであれば、その依存先がまず処理される。 そうしておいて、sbtはSettingのソート済リストをたどり、それぞれをマップに適用する。

サマリ:ビルド定義とはSetting[T]のリストを定義することであり、Setting[T]はsbtのキー・値マップペアへの変換であり、 Tとは各値のタイプである。

build.sbtにおけるsettingの定義方法

以下に例をあげる

name := "hello"

version := "1.0"

scalaVersion := "2.9.1"

build.sbtファイルは空行で区切られたsettingのリストだ。 それぞれのsettingはScalaの式で記述される。

build.sbtにおける式は、他との依存を持たない。また、それらは「式」であって、完全なScalaの「文」ではない。 だから、build.sbtの中でトップレベルのval、オブジェクト、クラス、メソッドを定義することはできない。

左側のname、version、scalaVersionはキーだ。 キーはSettingKey[T]、TaskKey[T]、InputKey[T]のいずれかで、Tは値の型だ。 キーの種類については後で説明する。

キーは「:=」というメソッドを持つのだが、この返値はSetting[T]になる。 このメソッドを呼び出すには、Javaのように以下のような書き方でもいい。

name.:=("hello")

Scalaでは「name := "hello"」という書き方でもいい(Scalaでは、どんなメソッドもどちらの書き方も許される)。

nameキーの:=メソッドはSettingを返すのだが、ここでは当然Setting[String]になる。 Stringはキー自身のタイプに現れる、つまりSettingKey[String]だ。 この場合には、返されたSetting[String]はsbtマップにnameキーを追加するか置換するものになる。その値は"hello"だ。

もし値のタイプを間違えるとビルド定義はコンパイルされない。

name := 42  // コンパイルできない

キーはKeysオブジェクトの中で定義されている

ビルトインキーはKeysというオブジェクト中に定義されている単なるフィールドだ。 build.sbtでは暗黙的にsbt.Keys._がインポートされているので、sbt.Keys.nameを単にnameのみで参照することができる。

カスタムキーは.scalaファイルかプラグインで定義することができる。

settingを変換する他の方法

:=による置換は最も単純な変換であり、他にも方法はある。 例えば、リスト値に+=で追加する方法だ。

その他の変換については、スコープを理解しておく必要がある。 次のページではスコープについて学習し、その後でsettingの詳細を説明する。

タスクキー

キーには三種類ある。

  • SettingKey[T]: 一度だけ計算される値を持つキー(値は、プロジェクトのロード時にだけ計算され、その後保持され続ける)

  • TaskKey[T]: 必要なときに再計算される値を持つキー。多くの場合副作用を持つ。

  • InputKey[T]: 入力としてコマンドライン引数を受け取るTaskKey。「Getting Started Guide」では、これについては説明しないが、このガイドを終了したら「Input Tasks」を参照して欲しい。

TaskKey[T]はタスクを定義するものだ。 タスクとは、コンパイルやパッケージング等の操作を意味する。 それらは、Unit(Javaでいうvoid)か、あるいはタスクに関連する何らかの値を返す。 例えば、packageはTaskKey[File]であり、作成したjar fileを返す。

いつでもタスクの実行を指示することができる。例えば、sbtの対話プロンプトでcompileと入力すると、それに含まれるタスクをただ一度再実行する。

sbtのマップはプロジェクトを記述するものであるが、ここにはnameのような固定的な文字列が保持されるほか、 compileのようなタスクの実行コードも保持される。この実行コードが最終的には文字列を返すには、毎回再実行する必要がある。

あるキーは、タスクかあるいはプレーンなsettingのいずれかを参照する。 つまり、taskiness(毎回再実行が可能であるか)はキーの性質であって、値ではない。

訳注:要するに、SettingKeyの値として実行コードを代入しても、実行されないことに注意しろという意味?

:=を使ってタスクに計算を代入することができ、その計算は毎回実行することができる。

hello := { println("Hello!") }

タイプシステムの観点から言えば、タスクキーから作成されたSettingはSetting Keyから作成されたものとは少々異なる。

taskKey := 42 results in a Setting[Task[T]] while settingKey := 42 results in a Setting[T]. For most purposes this makes no difference; the task key still creates a value of type T when the task executes.

The T vs. Task[T] type difference has this implication: a setting key can't depend on a task key, because a setting key is evaluated only once on project load, and not re-run. More on this in more about settings, coming up soon. Keys in sbt interactive mode

In sbt's interactive mode, you can type the name of any task to execute that task. This is why typing compile runs the compile task. compile is a task key.

If you type the name of a setting key rather than a task key, the value of the setting key will be displayed. Typing a task key name executes the task but doesn't display the resulting value; to see a task's result, use show <task name> rather than plain <task name>.

In build definition files, keys are named with camelCase following Scala convention, but the sbt command line uses hyphen-separated-words instead. The hyphen-separated string used in sbt comes from the definition of the key (see Keys). For example, in Keys.scala, there's this key:

val scalacOptions = TaskKey[Seq[String]]("scalac-options", "Options for the Scala compiler.")

In sbt you type scalac-options but in a build definition file you use scalacOptions.

To learn more about any key, type inspect <keyname> at the sbt interactive prompt. Some of the information inspect displays won't make sense yet, but at the top it shows you the setting's value type and a brief description of the setting.

build.sbtにおけるインポート

You can place import statements at the top of build.sbt; they need not be separated by blank lines.

There are some implied default imports, as follows:

import sbt._
import Process._
import Keys._

(In addition, if you have .scala files, the contents of any Build or Plugin objects in those files will be imported. More on that when we get to .scala build definitions.) Adding library dependencies

To depend on third-party libraries, there are two options. The first is to drop jars in lib/ (unmanaged dependencies) and the other is to add managed dependencies, which will look like this in build.sbt:

libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3"

This is how you add a managed dependency on the Apache Derby library, version 10.4.1.3.

The libraryDependencies key involves two complexities: += rather than :=, and the % method. += appends to the key's old value rather than replacing it, this is explained in more about settings. The % method is used to construct an Ivy module ID from strings, explained in library dependencies.

We'll skip over the details of library dependencies until later in the Getting Started Guide. There's a whole page covering it later on.