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-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」は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ページ使って詳細を説明するつもりだ。

次はスコープ