search term:

sbt 0.13.0 の変更点

概要

互換性に影響のある新機能、変更点、バグ修正

新機能

バグ修正

改善点

その他

大きな変更点の詳細

キャメルケースのキー名

これまでのキー名の書き方は、Scala の識別子はキャメルケースで、コマンドラインではハイフンでつなげた小文字という慣用だったが、これからは両方ともキャメルケースに統一する。既存のハイフン付けされたキー名に対してもキャメルケースを使うことができ、また既にあるタスクやセッティングに対しては継続してハイフン付けされた形式も受け付ける。しかし、タブ補完はキャメルケースのみを表示するようになる。

新しいキー定義メソッド

キーを定義するときに、キー名を二回書かなくても済む新しいメソッドが追加された:

val myTask = taskKey[Int]("myTask の説明文。(省略不可)")

キーの名前は taskKey マクロによって val の識別子から抜き出されるため、リフレクションや実行時のオーバーヘッドはかからない。説明文は省略不可であり、taskKey メソッドが小文字の t から始まることに注意。セッティングとインプットタスクのための類似メソッド settingKeyinputKey もある。

タスク、セッティングのための新しい構文

好きな時に新しい構文に移行できるように、旧構文もちゃんとサポートされていることを言っておきたい。多少は非互換性があるかもしれないし、これらは避ける事ができないかもしれないが、既存のビルドがそのまま動作しない場合は是非報告して欲しい。

新しい構文は :=+=++= をマクロとして実装しており、この3つだけを必須の代入メソッドとするように設計されている。他のセッティングやタスクの値を参照するには、セッティングやタスクに対して value メソッドを呼び出す。このメソッドはコンパイル時にマクロによって削除され、タスクやセッティングの実装は旧構文に変換される。

例えば、以下に scalaVersion の値を使って scala-reflect を依存ライブラリとして宣言する具体例をみてみる:

libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value

この value メソッドは :=+=、もしくは ++= の呼び出しの中だけで使うことができる。これらのメソッド外でセッティングやタスクを構築するには Def.taskDef.setting を使う。以下に具体例で説明する。

val reflectDep = Def.setting { "org.scala-lang" % "scala-reflect" % scalaVersion.value }

libraryDependencies += reflectDep.value

類似のメソッドとして Parser[T]Initialize[Parser[T]] (パーサを提供するセッティング)、そして Initialize[State => Parser[T]] (現在の State を基に Parser[T] を提供するセッティング) に parsed メソッドが定義されている。このメソッドを使ってユーザからのインプットを使ったインプットタスクを定義する。

myInputTask := {
     // 標準的な空文字区切り引数パーサを定義する。
   val args = Def.spaceDelimited("<args>").parsed
     // セッティングの値とタスクの結果を使ってみる:
   println("Project name: " + name.value)
   println("Classpath: " + (fullClasspath in Compile).value.map(_.file))
   println("Arguments:")
   for(arg <- args) println("  " + arg)
}

詳細は、Input Tasks 参照。

タスクの失敗を期待して、失敗時の例外を受け取るには、value の代わりに failure メソッドを使う。これは Incomplete 型の値を返し、これは例外をラップする。成否にかかわらずタスクの結果を取得するには Result[T] を返す result を使う。

動的なセッティングとタスク (flatMap) は身ぎれいにされた。Def.taskDynDef.settingDyn メソッドを使って定義することができる。これらはそれぞれタスクとセッティングを返すことを期待する。

.sbt 形式の改善

.sbt ファイル内に valdef が書けるようになった。これらもセッティング同様に空行のルールに従う必要があるが、複数の定義をまとめて書くことができる。

val n = "widgets"
val o = "org.example"

name := n

organization := o

全ての定義はセッティングの前にコンパイルされるが、定義は全てまとめて書くのがベスト・プラクティスだろう。現行では、定義が見える範囲はそれが定義された .sbt ファイル内に制限されている。これらは今のところは consoleProjectset コマンドからも見ることができない。全ての .sbt ファイルから見えるようにするには project/ 内で Scala ファイルを使う。

Project 型の val は Build に追加されるため、.sbt ファイルだけを用いてマルチプロジェクトビルドが定義できるようになった。具体例で説明しよう。

lazy val a = Project("a", file("a")).dependsOn(b)

lazy val b = Project("b", file("sub")).settings(
   version := "1.0"
)

今のところは、これらはルートプロジェクトの .sbt ファイルからのみ定義するべきだ。

Project を定義する略記法として project というマクロが提供されている。これは構築される Project が直接 val に代入されることを要求する。この val の名前がプロジェクトID とベースディレクトリとしても使われる。上の例は以下のようにも書ける:

lazy val a = project.dependsOn(b)

lazy val b = project in file("sub") settings(
  version := "1.0"
)

このマクロは Scala ファイル内からも使うことができる。

自動的に追加されるセッティングの制御

sbt は Project.settings フィールドで明示的に定義されたセッティング以外にもいくつかの場所からセッティングを読み込む。プラグイン、グローバルセッティング、そして .sbt ファイルなどだ。新たに追加された Project.autoSettings メソッドはこれらの出どころを設定して、プロジェクトに含めるか否か、どの順番かを制御することができる。

Project.autoSettingsAddSettings型の値の列を受け取る。AddSettings のインスタンスは AddSettings コンパニオンオブジェクト内のメソッドによって構築される。設定可能なのはユーザ毎のセッティング (例えば、~/.sbt からなど)、.sbt ファイルからのセッティング、そしてプラグインのセッテイング (プロジェクトレベルのみ) だ。これらのインスタンスが autoSettings に渡された順に Project.settings に明示的に提供されたセッティングに追加される。

.sbt ファイルに関しては、AddSettings.defaultSbtFiles が通常通りプロジェクトのベースディレクトリ内の全ての .sbt ファイルを追加する。代替として AddSettings.sbtFilesFile の列を受け取り、標準的な .sbt フォーマットに従って読み込まれる。相対的なファイルはプロジェクトのベースディレクトリに対して解決される。

AddSettings.plugins メソッドに Plugin => Boolean を渡すことでプラグインセッティングはプラグイン毎に含めることができる。ここで制御されるセッティングはプロジェクト毎での自動セッティングのみだ。ビルド毎のセッティングとグローバルセッティングは常に含まれる。プラグインが手動で追加することを必要とするセッティングは手動で追加される必要がある。

例えば、

import AddSettings._

lazy val root = Project("root", file(".")) autoSettings(
   userSettings, allPlugins, sbtFiles(file("explicit/a.txt"))
)

lazy val sub = Project("sub", file("Sub")) autoSettings(
   defaultSbtFiles, plugins(includePlugin)
)

def includePlugin(p: Plugin): Boolean =
   p.getClass.getName.startsWith("org.example.")

Scala 依存性の解決

(scala-libraryscala-compiler のような) Scala 依存性は通常の update タスクを用いて解決されるようになった。そのため、

  1. sbt を実行するのに必要なもの以外は Scala の jar ファイル群をブートディレクトリにコピーしなくなった。
  2. Scala の SNAPSHOT は普通の SNAPSHOT 同様の振る舞いをする。特に、update を実行することで動的リビジョンを再解決するようになった。
  3. Scala の jar ファイル群は他の依存ライブラリと同じリポジトリや設定で解決されるようになった。
  4. scalaHome が設定されている場合は Scala 依存性は update 経由では解決せず指定されたディレクトリから取得するようになった。
  5. sbt のための Scala バージョンは、引き続きランチャのために設定されたリポジトリ経由で解決される。

sbt が compileconsole などの Scala に基づいたタスクを実行するためには、これまで通りコンパイラ及びその依存ライブラリにアクセスする必要がある。そのため、Scala コンパイラの jar とその依存ライブラリ (scala-reflect.jarscala-library.jar など) は scala-tool コンフィグレーションにて定義され解決される (scalaHome が定義されていなければ)。デフォルトでは、このコンフィグレーションと依存ライブラリは自動的に sbt によって追加される。これは依存ライブラリが pom.xmlivy.xml で設定されていたとしても発生するため、プロジェクトで使われる Scala のバージョンがプロジェクトに設定された resolver から解決可能であることを必要とする。

コンパイル、REPL などの Scala タスクに使われる Scala コンパイラとライブラリを sbt がどこから取得するかを手動で設定する場合は以下のうち 1つを実行する:

  1. scalaHome を設定して特定のディレクトリにある既存の Scala jar ファイル群を使う。autoScalaLibrarytrue なら、ここで見つかった library の jar はアンマネージクラスパスに追加される。
  2. managedScalaInstance := false に設定して、明示的に scalaInstance を定義する。これは ScalaInstance 型で、コンパイラ、ライブラリ、その他の Scala を構成する jar 群を定義する。autoScalaLibrarytrue なら、定義された ScalaInstance からの library の jar がアンマネージクラスパスに追加される。

より詳しくは Configuring Scala 参照。