= .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のこと)。 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」はSetting[Task[T]]を結果とし、「settingKey := 42」はSetting[T]を結果とする。 たいていの場合には、何の違いもない。タスクが実行されると、単にタイプTの結果を生成するだけだ。 TとTask[T]のタイプの違いは以下を意味している。 setting keyはtask keyに依存することはできない。なぜなら、setting keyはプロジェクトロード時に一度だけ評価され、再実行されないからだ。 settingの詳細については、また後で説明するよ。 == 対話モード中でのキー == sbtの対話モードでは、タスクの名称を入力することによって、それを実行することができる。 compileと入力すれば、コンパイルタスクが実行される。compileはタスクキーだ。 タスクキーではなくsettingキーを入力すると、その値が表示される。 タスクキーはタスクを実行するものの結果値は表示しない。これを見るには、ただ<タスク名>と入力するのではなくshow <タスク名>とする必要がある。 ビルド定義中では、scalaのコンベンションに習ってキーの名称はキャメルケースになっているが、コマンドライン中ではハイフンで分離されたワードになっている。ハイフン分離された文字列はキー定義の中にある。例えば、Keys.scalaには以下のようなキーがある。 {{{ val scalacOptions = TaskKey[Seq[String]]("scalac-options", "Options for the Scala compiler.") }}} sbt(の対話モード)ではscalac-optionsと入力するが、ビルド定義中ではscalacOptionsとしなければならない。 キーについて知りたい場合は、対話モードで「inspect <キー名>」と入力すること。 inspectの表示するいくつかの情報はまだ意味をなさないものもあるが、しかしトップにはsettingの値タイプとsettingの管理説明がある。 == build.sbtにおけるインポート == build.sbtのトップにはインポート文を入れることができる。これらは空行で分割されていなくていい。 以下のインポートは暗黙的に行われている。 {{{ import sbt._ import Process._ import Keys._ }}} (加えて、.scalaファイルがあるなら、その中のビルド定義やプラグインをインポートすることができる。 これについては「.scala build definitions」で説明する)。 == ライブラリ依存を追加する == サードパーティ製のライブラリに依存する際には二つの選択肢がある。 一つはlib/ディレクトリ(管理されていない依存)にそれを格納すること。 もう一つは管理依存に追加することだ。 後者の場合、build.sbtに次のように記述する。 {{{ libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" }}} こうすると、Apache Derby library, version 10.4.1.3への管理された依存を追加することができる。 libraryDependenciesキーは二つの複雑さを持つ。:=ではなく、+=であることと、%というメソッドだ。 +=は置換ではなく、キーの古い値に追加するということ。 これについては後述する。 %メソッドは、IvyモジュールIDを作成するものだが、これについても後述する。 ライブラリ依存についての詳細は、このガイドの後の方になるまでスキップする。 まるまる1ページ使って詳細を説明するつもりだ。 次は[[sbt/Getting-Started-Scopes|スコープ]]