sbt 0.12.0 の変更点

in

ついに final がリリースされた、sbt 0.12.0 の変更点を訳しました。
バイナリバージョンという概念が導入されることで、Scala 2.9.0 で入ったけどあまり活用されていない Scala の後方バイナリ互換性がより正面に出てくるキッカケとなると思います。

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

  • Scala 2.10 以降の Scala 及び sbt プラグインのクロスバージョン規約の変更。 (詳細は以下の項目 )
  • 直接実行された場合、強制的に update を実行するようにした。 #335
  • sbt プラグインリポジトリがプラグインとプラグインの定義にデフォルトで加わった。 #380
  • プラグイン設定ディレクトリの優先順位。 (詳細は以下の項目 )
  • ソース依存性の修正。 (詳細は以下の項目 )
  • 集約がより柔軟になった。 (詳細は以下の項目 )
  • タスク軸の構文が key(for task) から task::key へと変更された。 (詳細は以下の項目 )
  • sbt の organization が org.scala-sbt へと変更された。(元は、org.scala-tools.sbt) 特に、scripted プラグインのユーザはこの影響を受ける。
  • artifactName の型が (ScalaVersion, ModuleID, Artifact) => String となった。
  • javacOptions がタスクとなった。
  • session savebuild.sbt 内の設定を(適切な時に)上書きするようにした。#369

新機能

  • テストのフォークのサポート。 #415
  • test-quick。 (#393)
  • リポジトリ設定のグローバルなオーバライドをサポートした。 #472
  • 再コンパイルをせずに unchecked と deprecation の警告を表示する print-warnings タスクを追加した。(Scala 2.10+ のみ)
  • Ivy 設定ファイルを URL から読み込めるようにした。
  • projects add/remove <URI> で一時的に他のビルドと作業できるようになった。
  • 並列実行の制御の改善。 (詳細は以下の項目 )
  • inspect tree <key>inspect を再帰的に呼べるようになった。#274

バグ修正

  • 再帰的にディレクトリを削除するときに、シンボリックリンクが指す先のコンテンツを削除しないようにした。
  • Java ソースの親の検知の修正。
  • update-sbt-classifiers で用いられる resolver の修正。#304
  • プラグインの自動インポートの修正。#412
  • 引数のクオート #396
  • Ctrl+Z で停止した後 JLine を正しくリセットするようにした。(Unix のみ) #394

改善点

  • ランチャーが 0.7.0 以降全ての sbt を起動できるようになった。
  • スタックトレースが抑制された場合、last を呼ぶようにより洗練されたヒントが表示されるようになった。
  • Java 7 の Redirect.INHERIT を用いて子プロセスの入力ストリームを継承するようになった。 #462, #327 これでインタラクティブなプログラムをフォークした場合に起こる問題が解決されるはず。 (@vigdorchik)
  • helptask コマンドの様々な改善、および新たな settings コマンド。#315
  • jsch バージョンを 0.1.46 へと更新。 #403
  • JLine バージョンを 1.0 へと変更。 (詳細は以下の項目 )
  • その他の修正および機能改善: #368, #377, #378, #386, #387, #388, #389

実験的、もしくは開発途中

  • 差分コンパイルを組み込むための API。このインターフェイスは今後変更する可能性があるが、既に scala-maven-plugin のブランチで利用されている。
  • Scala コンパイラの常駐の実験的サポート。 sbt に -Dsbt.resident.limit=n を渡すことで設定を行う。n は常駐させるコンパイラの最大数。
  • 新サイトhowto ページを読みやすくした。

大きな変更の詳細点

プラグインの設定ディレクトリ

0.11.0 においてプラグインの設定ディレクトリは project/plugins/ からただの project/ へと移行し、project/plugins/ は非推奨となった。0.11.2 において非推奨のメッセージが表示されたが、全ての 0.11.x においては旧スタイルの project/plugins/ が新しいスタイルよりも高い優先された。0.12.0 では新しいスタイルが優先される。旧スタイルのサポートは 0.13.0 が出るまで廃止されない。

  1. 理想的には、プロジェクトは設定の衝突がないことを保証すべきだ。両方のスタイルがサポートされているため、設定に衝突がある場合の振る舞いのみが変更されることになる。
  2. 実際にこれが起こりえる状況としては、古いブランチから新しいブランチに切り替えた場合に空の project/plugins/ が残ってしまい何も設定が無いにも関わらず旧スタイルが使われてしまうということがある。
  3. そのため、この変更は飽くまで新スタイルへ移行中のプロジェクトのための改善であり、他のプロジェクトには気付かれないことを意図している。

JLine

JLine 1.0 への移行。これはいくつかの顕著な修正を含む比較的新しいリリースだが、見たところ今まで使われていた 0.9.94 とバイナリ互換がある。具体的には、

  1. Unix 上で stty へフォークしたストリームを正しく閉じる。
  2. Linux での Delete キーへの対応。これが実際に動作するかは各自確認して欲しい。
  3. 行の折り返しが正しくなっているように思える。

タスク軸のパーシング

セッティングやタスクのタスク軸のパーシングに関して重要な変更が行われた。 #202

  1. 0.12 以前の構文は {build}project/config:key(for task) だった。
  2. 提案され(採用された)0.12 からの構文は {build}project/config:task::key だ。
  3. タスク軸をキーの前に移動することで特にプラグインからの(タブ補完を用いた)キーの発見が容易にする。
  4. 旧構文はサポートされない予定だ。理想的は非推奨に一度すべきだが、その実装に手間がかかりすぎる。

集約

集約がより柔軟になった。これは過去にメーリングリストで議論されたのと同様の方向だ:

  1. 0.12 以前は、セッティングは現行プロジェクトに基づいてパースされ、全く同様のセッティングのみが集約された。
  2. タブ補完は集約を考慮に入れていなかった。
  3. これは、セッティングもしくはタスクが現行プロジェクトに無かった場合は集約されたプロジェクトにそのセッティング/タスクがあったとしてもパーシングが失敗することになった。
  4. また、現行プロジェクトに compile:package があり、集約されたプロジェクトに *:package があり、ユーザが (コンフィギュレーション無しで) package を実行した場合 (compile:package じゃないため) *:package が集約されたプロジェクトで実行されなかった。
  5. 0.12 ではこのような状況において集約されたセッティングが選択されるようになった。具体的には、
    1. root というプロジェクトが子プロジェクトの sub を集約すると仮定する。
    2. root*:package を定義する。
    3. subcompile:packagecompile:package を定義する。
    4. root/package を実行すると root/*:packagesub/compile:package が実行される。
    5. root/compile を実行すると sub/compile:compile が実行される。
  6. この変更点はタスク軸のパーシングの変更に依存する。

並列実行

並列実行の細かい制御がサポートされる。詳細は Parallel Execution 参照。

  1. デフォルトの振る舞いは、parallelExecution のセッティングも含め以前と同じ。
  2. このシステムの新しい機能は実験段階だと考えるべき。
  3. そのため parallelExecution は現段階では非推奨ではない。

ソース依存性

#329 に対する修正が含まれた。この修正により前プロジェクトに渡ってプラグイン一つにつき唯一のバージョンのみが読み込まれることが保証されるようになった。これは、二部に分かれる。

  1. プラグインのバージョンは最初に読み込んだビルドに確定する。特に、(sbt が起動した) ルートのビルドで使われたプラグインのバージョンは依存性により使われるものよりも常に優先される。
  2. 全てのビルドのプラグインは同一のクラスローダにより読み込まれる。

さらに Sanjin のパッチにより hg と svn の URI へのサポートが追加された。

  1. sbt は svn もしくは svn+ssh から始まる URI を subversion を用いて取得する。省略可能なフラグメントにより特定のリビジョンを指定できる。
  2. Mercurial は特定のスキームを持たないため、sbt は Mercurial のリポジトリの URI に hg: をプレフィックスとして付けることを要求する。
  3. .git で終わる URI の処理が修正された。

クロスビルド

Scala のバージョン 2.10 シリーズと sbt のバージョン 0.12 シリーズ以降に関して、クロスバージョンのサフィックスがメジャー番号とマイナー番号のみに短縮された。具体的には、普通のライブラリだと sbinary_2.10、sbt プラグインだと sbt-plugin_2.10_0.12 のようになる。これは Scala と sbt がその中間リリースにおいて前方互換性と後方互換性を維持することを前提とする。

  1. これは待ちわびていた変更だが、これはオープンソースプロジェクト作者の皆が Scala 2.10 向けのものを公開する前に 0.12 に切り替えるか、ビルドのクロスバージョンのサフィックスを適宜変更することを必要とする。
  2. 0.12 を用いて Scala 2.10 向けのライブラリを公開するには、0.12.0 が Scala 2.10 よりも前にリリースされることが求められる。
  3. 同時に、sbt 0.12.0 が Scala 2.10.0 向けに公開されなければ 0.12.x シリーズに渡って Scala 2.9.x を使わなければいけないことになる。
  4. バイナリバージョン (binary version) という新しい概念を導入する。これはフルバージョン (full version) 文字列のサブセットで、バイナリ互換性を表す。つまり、同じバイナリバージョンはバイナリ互換性を意味する。以前の sbt の振る舞いに合わせて 2.10 以前の全ての Scala バージョンはフルバージョンをもってバイナリバージョンとする。Scala 2.10 以降はバイナリバージョンは <major>.<minor> だ。
  5. 公開されるアーティファクトのクロスバージョンの振る舞いは crossVersion セッティングで設定される。ModuleID に対して cross メソッドを用いるか、今まで通りの依存性構築子である %% を用いて依存ライブラリごとに設定を変えることができる。デフォルトでは、単一の % を使った場合は依存性のクロスバージョンは無効にされ、%% を使った場合は Scala のバイナリバージョンを用いる。
  6. artifactName 関数は第一引数として ScalaVersion を受け取るようになった。型は (ScalaVersion, ModuleID, Artifact) => String となった。ScalaVersion は Scala のフルバージョン (例: 2.10.0) とバイナリバージョン (例: 2.10) を保持する。
  7. Indrajit により追加された柔軟なバージョンのマッピングが cross メソッドに追加され、複数の引数を取る %% の変種は非推奨となった。以下に具体例で説明する。

以下は等価だ:

"a" % "b" % "1.0"
"a" % "b" % "1.0" cross CrossVersion.Disabled

以下は等価だ:

"a" %% "b" % "1.0"
"a" % "b" % "1.0" cross CrossVersion.binary

これは、Scala のバイナリバージョンの代わりにフルバージョンを使う:

"a" % "b" % "1.0" cross CrossVersion.full

これは Scala のバイナリバージョンを元にカスタム関数を使って Scala バージョンを決定する:

"a" % "b" % "1.0" cross CrossVersion.binaryMapped {
  case "2.9.1" => "2.9.0" // 2.10 以前なのでバイナリ==フル
  case x => x
}

これは Scala のフルバージョンを元にカスタム関数を使って Scala バージョンを決定する:

"a" % "b" % "1.0" cross CrossVersion.fullMapped {
  case "2.9.1" => "2.9.0"
  case x => x
}

全ての Scala バージョンに対して公開されていない依存ライブラリを用いてクロスビルドするときにカスタム関数を使うことができる。バイナリバージョンに移行することで、この機能の必要性が徐々に減っていくはずだ。

グローバルなリポジトリ設定

リポジトリ設定のグローバルなオーバライドをサポートした。 #472 [repositories] 項目を ~/.sbt/repositoreies に書いて、sbt に -Dsbt.override.build.repos=true を渡すことでリポジトリを定義する。([Launcher] のページを参照) ランチャーが sbt と Scala を取得し、sbt がプロジェクトの依存性を取得するのにファイルで指定されたリポジトリが使われるようになる。

test-quick

test-quick (#393) は引数で指定されたテスト(引数がない場合は全てのテスト)のうち以下の条件を一つでも満たすものを実行する:
1. まだ実行されていない。
2. 前回実行時に失敗した。
3. 最後に成功した後で間接的にでも依存するコードが再コンパイルされた場合。

引数のクォート

引数のクォート #396

  1. > command "空白 のある 引数\n エスケープは解釈される"
  2. > command """空白 のある 引数\n エスケープは解釈されない"""
  3. 最初のリテラルは Windows のパス記号であるバックスラッシュをエスケープ (\) する必要があることに注意。2つ目のリテラルを使えばその必要は無い。
  4. バッチモードから使う場合は、ダブルクオートそのものをシェルからエスケープする必要がある。