これは sbt 1.x の 3 番目の機能リリースであり、バイナリ互換性のあるリリースで、新しい機能に焦点を当てています。sbt 1.x は**セマンティックバージョニング**の下でリリースされており、プラグインは 1.x シリーズ全体で動作することが期待されています。
sbt 1.3 の主要な機能は、すぐに使えるCoursier ライブラリ管理、ClassLoader レイヤリング、IO の改善、およびスーパーシェルです。これらを組み合わせることで、ビルドの実行におけるユーザーエクスペリエンスが向上することを期待しています。
sbt 1.3.0 は、ライブラリ管理にCoursier を採用しました。Coursier は Ivy のような依存関係レゾルバーであり、Alexandre Archambault (@alexarchambault) によって Scala で書き直され、より高速な代替を目指しています。
**注記**:状況によっては、Coursier の解決方法が Ivy と異なる場合があります(例:リモートの `-SNAPSHOT` は 24 時間キャッシュされます)。ライブラリ管理に Apache Ivy を使用する場合、次の行を `build.sbt` に追加してください。
ThisBuild / useCoursier := false
Coursier を sbt に導入する作業には、多くの人が関わっていました。2018 年初頭、Leonard Ehrenfried (@leonardehrenfried) は、lm#190 として Coursier ベースの LM API 実装を開始しました。秋の間、Andrea Peruffo (@andreaTP) によってさらに改善され、`lm-coursier` は最終的に Alex によって維持されている coursier/sbt-coursier リポジトリの一部となりました。この春、Eugene (@eed3si9n) はこれを再検討し、いくつかの変更を加え、#4614 で Alex の助けを借りて LM エンジンを交換できるようにしました。
sbt 1.3.0 は、「ターボ」モードを追加しました。これは、動作しない場合にビルドユーザーによるデバッグが必要になる可能性のある実験的な機能または高度な機能を有効にします。
ThisBuild / turbo := true
当初、レイヤード ClassLoader(`ClassLoaderLayeringStrategy.AllLibraryJars`)はこのフラグの背後に配置されています。
sbt は、`run` タスクと `test` タスクを評価する際に、常に 2 レイヤーの ClassLoader を作成してきました。ClassLoader のトップレイヤーには Scala ライブラリ jar が含まれており、scala パッケージ内のクラスは複数のタスク評価間で再利用できます。sbt 1.3.0 は、この概念をさらに発展させた**実験的な**`classLoaderLayeringStrategy` 機能を導入しました。
Compile / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat
// default
Compile / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ScalaLibrary
// enabled with turbo
Compile / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.AllLibraryJars
Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat
// default
Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ScalaLibrary
// enabled with turbo
Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.AllLibraryJars
`ClassLoaderLayeringStrategy.AllLibraryJars` は、`run` タスクと `test` タスクの応答時間を向上させるはずです。ライブラリ jar の classloader をキャッシュすることで、同じセッション内でタスクが複数回実行される場合、`run` タスクと `test` タスクの起動レイテンシを大幅に削減できます。GC の圧力も軽減されます。ライブラリ jar は、タスクが評価されるたびに再ロードされません。
**注記**:`ClassLoaderLayeringStrategy.AllLibraryJars` はテスト間でシングルトンオブジェクトを再利用するため、ライブラリは自身でクリーンアップする必要があります。
`ClassLoaderLayeringStrategy.Flat` は、レイヤード ClassLoader でうまく動作しない特定のアプリケーションに役立ちます。そのような例として、Scala コレクションで使用される Java シリアル化とシリアル化プロキシパターンがあります。
ClassLoader レイヤリングは、Ethan Atkins (@eatkins) によって #4476 として貢献されました。
ClassLoader レイヤリングに加えて、sbt 1.3.0 には、次のような多くのパフォーマンス強化が組み込まれています。
執筆時点では、5000 個のソースファイルに対する sbt 1.3.0 の編集・コンパイル・テストループは、sbt 0.13、Gradle、およびテストしたその他のビルドツール(3 つのソースファイルを使用)よりも高速です(詳細は ビルドパフォーマンス を参照してください)。これらの変更は、Ethan Atkins (@eatkins) によって貢献されました。
sbt 1.3.0 は、パス検索クエリを表す新しい型 `Glob` を導入しました。たとえば、プロジェクトディレクトリのすべての Scala ソースは、`Glob(baseDirectory.value, RecursiveGlob / "*.scala")` または `baseDirectory.value.toGlob / ** / "*.scala"` で記述できます。ここで `**` は `RecursiveGlob` のエイリアスです。Glob は PathFinders を拡張しますが、IO オーバーヘッドなしで合成できます。Glob は `FileTreeView` を使用して取得できます。たとえば、次のように記述できます。
val scalaSources = baseDirectory.value.toGlob / ** / "*.scala"
val javaSources = baseDirectory.value.toGlob / ** / "*.java"
val allSources = fileTreeView.value.list(Seq(scalaSources, javaSources))
そして、`FileTreeView` はベースディレクトリを一度だけトラバースします。Glob と FileTreeView は、Ethan Atkins (@eatkins) によって io#178、io#216、io#226 で追加されました。
sbt 1.3.0では、ファイル監視の実装が新しくなりました。OSイベントを用いたファイル変更イベントの追跡に強化されたAPIを使用しています。ソースファイルの監視対象となる特定のタスクを抽出する新しいパーサーが追加され、変更を検出すると再実行されます。実行中のタスクのソース依存関係のみが監視されます。例えば、`~compile`を実行した場合、テストソースファイルの変更は新しいビルドをトリガーしません。ファイルイベントの間には、シェルに戻る、前のコマンドを再実行する、sbtを終了するといったオプションも追加されました。これらの変更は、Ethan Atkins (@eatkins)によってio#178、#216、#226、#4512、#4627で実装されました。
sbt 1.3.0では、ビルド定義ソースが自動的に監視され、リロードせずにタスクを実行すると警告が表示されます。これは、次のように自動的にリロードするように設定できます。
Global / onChangedBuildSource := ReloadOnSourceChanges
この機能は、Ethan Atkins (@eatkins)によって#4664で寄稿されました。
sbt 1.3.0では、ファイルに基づいたカスタム増分タスクを実装するためのサポートが提供されています。`java.nio.file.Path`、`Seq[java.nio.file.Path]`、`File`、または`Seq[File]`を返すカスタムタスクの場合、いくつかのヘルパータスクを定義して、より増分的な処理を行うことができます。
import java.nio.file._
import scala.sys.process._
val gccCompile = taskKey[Seq[Path]]("compile C code using gcc")
val gccHeaders = taskKey[Seq[Path]]("header files")
val gccInclude = settingKey[Path]("include directory")
val gccLink = taskKey[Path]("link C code using gcc")
gccCompile / sourceDirectory := sourceDirectory.value
gccCompile / fileInputs += (gccCompile / sourceDirectory).value.toGlob / ** / "*.c"
gccInclude := (gccCompile / sourceDirectory).value.toPath / "include"
gccHeaders / fileInputs += gccInclude.value.toGlob / "*.h"
gccCompile / target := baseDirectory.value / "out"
gccCompile := {
val objectDir = Files.createDirectories((gccCompile / target).value.toPath / "objects")
def objectFile(path: Path): Path =
target.value.toPath / path.getFileName.toString.replaceAll(".c$", ".o")
Files.createDirectories(target.value.toPath)
val headerChanges = gccHeaders.inputFileChanges.hasChanges
val changes = gccCompile.inputFileChanges
changes.deleted.foreach(sf => Files.deleteIfExists(objectFile(sf)))
val sourceFileChanges = changes.created ++ changes.modified
val needRecompile = (sourceFileChanges ++ (if (headerChanges) changes.unmodified else Nil)).toSet
val logger = streams.value.log
gccCompile.inputFiles.map { sf =>
val of = objectFile(sf)
if (!Files.exists(of) || needRecompile(sf)) {
logger.info(s"Compiling $sf")
s"gcc -I${gccInclude.value} -c $sf -o $of".!!
}
of
}
}
この設定では、`gccCompile.inputFiles`はすべての入力`c`ソースファイルのシーケンスを返し、`gccCompile.inputFileChanges`は`gccCompile`の前回の実行以降に作成、削除、変更、および変更されていないファイルを含むデータ構造を返し、`gccHeaders.changedInputFiles`は`gccCompile`の前回の実行以降に変更されたヘッダーを返します。これらを組み合わせることで、`gccCompile`が最後に完了してからのファイルシステムの変更を考慮して、再構築が必要なソースファイルのみを増分的に再構築できます。
`gccLink`などの別のタスクでは、`gccCompile.outputFileChanges`を使用して`gccCompile`の結果も追跡できます。
gccLink := {
val library = (gccCompile / target).value.toPath / "libmylib.dylib"
val objectFiles = gccCompile.outputFiles
val logger = streams.value.log
if (!Files.exists(library) || gccCompile.outputFileChanges.hasChanges) {
logger.info(s"Rebuilding $library")
s"gcc -dynamiclib -o $library ${objectFiles mkString " "}".!!
}
library
}
タスクの入力は、コンテキストを認識する新しいパーサーを持つ`~`コマンドによって自動的に監視されます。ファイル出力を生成するタスクには、カスタムクリーンタスクも実装されています。クリーンタスクは、プロジェクトとコンフィグのスコープ全体で集約されます。例えば、`Test / clean`は、Testコンフィグで宣言されたTestコンフィグ内のタスクによって生成されたすべてのファイルをクリーンしますが、Compileコンフィグで生成されたファイルはクリーンしません。
この機能は、Ethan Atkins (@eatkins)によって#4627で寄稿されました。
ANSI対応ターミナルで実行する場合、sbt 1.3.0は現在実行中のタスクを表示します。これにより、開発者は並列処理されているタスクと、ビルドが時間をかけている場所を把握できます。Gradleの「リッチコンソール」とBuckの「スーパーコンソール」に敬意を表して、これを「スーパーシェル」と呼んでいます。
無効にするには、ビルドに次の内容を追加します。
ThisBuild / useSuperShell := false
または、`--supershell=false`(または`-Dsbt.supershell=false`)を付けてsbtを実行します。この機能は、Eugene Yokota (@eed3si9n)によって#4396/util#196として追加されました。
タスクの分解を視覚的に表示するには、`--traces`(または`-Dsbt.traces=true`)を付けてsbtを実行します。これにより、`build.traces`ファイルが生成され、Chrome Tracing `chrome://tracing/`を使用して表示できます。この機能は、Jason Zaugg (@retronym)によって寄稿されました。
タスクのタイミングを画面に出力するには、`--timings`(または`-Dsbt.task.timings=true -Dsbt.task.timings.on.shutdown=true`)を付けてsbtを実行します。
sbt 1.3.0では、SemanticDBの生成が容易になりました。ビルド全体でSemanticDBの生成を有効にするには
ThisBuild / semanticdbEnabled := true
ThisBuild / semanticdbVersion := "4.1.9"
ThisBuild / semanticdbIncludeInJar := false
これは@eed3si9nによって#4410として追加されました。
sbt 1.3.0には、`show`に似ていますが標準出力に直接出力する新しい`print`コマンドが追加されました。
# sbt -no-colors --error "print akka-cluster/scalaVersion"
2.12.8
これは、David Knapp (@Falmarri)によって#4341で寄稿されました。
`Function1`は`+=`を使用して追加できます。
Global / onLoad += { s =>
doSomething()
s
}
これは、Dale Wijnand (@dwijnand)によって#4521で寄稿されました。
sbt 1.3.0は、JDK 11で広範囲にテストされた最初のsbtリリースです。Travis CIのすべての統合テストはAdoptOpenJDKのJDK 11で行われており、@eed3si9nによって#4389/zinc#639/[zinc640]で更新されました。
まず、sbtプロジェクトのコアコミュニティメンバーであり、macOSで監視サービスを提供するネイティブコードを使用するClose Watchの作者であるEthan Atkinsを紹介します。通常、コミット数は公表しませんが、sbt 1.3.0のトップ10を紹介します。
541 Ethan Atkins
369 Eugene Yokota (eed3si9n)
42 Jorge Vicente Cantero (jvican)
35 Łukasz Wawrzyk
34 Dale Wijnand
24 Andrea Peruffo
16 Kenji Yoshida (xuwei-k)
13 Guillaume Martres
7 Arnout Engelen
7 Jason Zaugg
コミュニティメンバーとして、Ethanは自分の時間を使って、sbtの応答性を向上させるための様々なIO関連の改善に貢献してきました。sbt 1.3.0には、彼のアイデアが多く反映されています。
sbt 1の最後の機能リリースは、2018年7月のsbt 1.2.0でした。それ以降、バグ修正のためにsbt 1.2.xの下で8つのパッチリリースを行いました。しかし、機能強化のほとんどは`develop`ブランチにマージされました。これらの数ヶ月間に、45人の貢献者がsbt 1.3.0とZincに参加しました:Ethan Atkins、Eugene Yokota (eed3si9n)、Jorge Vicente Cantero (jvican)、Łukasz Wawrzyk、Dale Wijnand、Andrea Peruffo、Kenji Yoshida (xuwei-k)、Guillaume Martres、Arnout Engelen、Jason Zaugg、Krzysztof Romanowski、Antonio Cunei、Mirco Dotta、OlegYch、Alex Dupre、Nepomuk Seiler、0lejk4、Alexandre Archambault、Eric Peters、Kazuhiro Sera、Philippus、Som Snytt、Syed Akber Jafri、Thomas Droxler、Veera Venky、bigwheel、Akhtyam Sakaev、Alexey Vakhrenev、Eugene Platonov、Helena Edelson、Ignasi Marimon-Clos、Julien Sirocchi、Justin Kaeser、Kajetan Maliszewski、Leonard Ehrenfried、Mikołaj Jakubowski、Nafer Sanabria、Stefan Wachter、Yasuhiro Tatsuno、Yusuke Izawa、falmarri、ilya、kai-chi、tanishiking、Ólafur Páll Geirsson。ありがとうございました!