1. sbt 1.3.x リリース

sbt 1.3.x リリース 

sbt 1.3.0 

これは sbt 1.x の 3 番目の機能リリースであり、バイナリ互換性のあるリリースで、新しい機能に焦点を当てています。sbt 1.x は**セマンティックバージョニング**の下でリリースされており、プラグインは 1.x シリーズ全体で動作することが期待されています。

sbt 1.3 の主要な機能は、すぐに使えるCoursier ライブラリ管理、ClassLoader レイヤリング、IO の改善、およびスーパーシェルです。これらを組み合わせることで、ビルドの実行におけるユーザーエクスペリエンスが向上することを期待しています。

互換性に影響を与える変更点 

  • Coursier を使用したライブラリ管理。詳細は下記を参照してください。
  • スーパーシェル。詳細は下記を参照してください。
  • 複数のコマンドに先頭のセミコロンが不要になりました。clean;Test/compile; が動作します。#4456 by @eatkins
  • HTTP レゾルバーは非推奨となりましたが、localhost または `withAllowInsecureProtocol(true)` とマークされたレゾルバーは許可されます。#4997
  • `CrossVersion.Disabled` は非推奨となりました。代わりに `CrossVersion.disabled` を使用してください。sbt/librarymanagement#316
  • ClassLoader 管理:リソースリークを防ぐため、sbt 1.3.0 は、これらのタスクが完了した後、`run` と `test` タスクで使用される一時的な ClassLoader を閉じます。タスクが ShutdownHook を使用する場合、またはタスクによって作成されたスレッドがタスクの完了後も実行を続ける場合、これによりダウンストリームクラッシュが発生する可能性があります。この動作を無効にするには、`Compile / run / fork := true` を設定するか、`-Dsbt.classloader.close=false` を付けて sbt を実行します。

Coursier を使用したライブラリ管理 

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 エンジンを交換できるようにしました。

ClassLoader レイヤリングを使用したターボモード 

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.Flat` には、Java ランタイムを除くすべてのクラスと JAR が含まれています。この戦略を使用するタスクの動作は、新しい JVM を起動するオーバーヘッドがないフォーキングに似ています。
  • `ClassLoaderLayeringStrategy.ScalaLibrary` は、Scala 標準ライブラリがウォームアップされた 2 レイヤーの ClassLoader を作成します。これは sbt 1.2.x と同様です。
  • `ClassLoaderLayeringStrategy.AllLibraryJars` は、Scala 標準ライブラリに加えてライブラリ依存関係もウォームアップされた 3 レイヤーの ClassLoader を作成します。

`ClassLoaderLayeringStrategy.AllLibraryJars` は、`run` タスクと `test` タスクの応答時間を向上させるはずです。ライブラリ jar の classloader をキャッシュすることで、同じセッション内でタスクが複数回実行される場合、`run` タスクと `test` タスクの起動レイテンシを大幅に削減できます。GC の圧力も軽減されます。ライブラリ jar は、タスクが評価されるたびに再ロードされません。

**注記**:`ClassLoaderLayeringStrategy.AllLibraryJars` はテスト間でシングルトンオブジェクトを再利用するため、ライブラリは自身でクリーンアップする必要があります。

`ClassLoaderLayeringStrategy.Flat` は、レイヤード ClassLoader でうまく動作しない特定のアプリケーションに役立ちます。そのような例として、Scala コレクションで使用される Java シリアル化とシリアル化プロキシパターンがあります。

ClassLoader レイヤリングは、Ethan Atkins (@eatkins) によって #4476 として貢献されました。

IO の改善 

ClassLoader レイヤリングに加えて、sbt 1.3.0 には、次のような多くのパフォーマンス強化が組み込まれています。

  • 高速な再帰的なディレクトリ一覧表示 — sbt は内部的にネイティブライブラリ swoval を使用しており、Java 標準ライブラリのインプリメンテーションよりも高速な再帰的なディレクトリ一覧表示を可能にするネイティブ OS API への JNI インターフェースを提供します。
  • 継続的ビルドでのファイル変更検出のレイテンシの削減。ほとんどの場合、ファイルイベントは 10 ミリ秒以内にタスク評価をトリガーします。

執筆時点では、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#178io#216io#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を実行します。

SemanticDBサポート 

sbt 1.3.0では、SemanticDBの生成が容易になりました。ビルド全体でSemanticDBの生成を有効にするには

ThisBuild / semanticdbEnabled := true
ThisBuild / semanticdbVersion := "4.1.9"
ThisBuild / semanticdbIncludeInJar := false

これは@eed3si9nによって#4410として追加されました。

printコマンド 

sbt 1.3.0には、`show`に似ていますが標準出力に直接出力する新しい`print`コマンドが追加されました。

# sbt -no-colors --error  "print akka-cluster/scalaVersion"
2.12.8

これは、David Knapp (@Falmarri)によって#4341で寄稿されました。

Function1の追加 

`Function1`は`+=`を使用して追加できます。

Global / onLoad += { s =>
  doSomething()
  s
}

これは、Dale Wijnand (@dwijnand)によって#4521で寄稿されました。

JDK 11サポート 

sbt 1.3.0は、JDK 11で広範囲にテストされた最初のsbtリリースです。Travis CIのすべての統合テストはAdoptOpenJDKのJDK 11で行われており、@eed3si9nによって#4389/zinc#639/[zinc640]で更新されました。

  • protobuf 3.7.0にアップグレードすることで、JDK 9以降での警告を修正しました。zinc#644 @smarter
  • JDK 11での`rt.jar`の無効化によって発生していた不必要な再構築を修正しました。#4679 @eatkins

その他のバグ修正と改善 

  • 1文字のエイリアスを使用したクロスビルドの修正 #4355 / #1074 @eed3si9n
  • グローバルディレクトリに関する古い警告の削除 #4356 / #1054 @eed3si9n
  • クロスJDKフォーキングのためのJDK検出の改善 #4313 / #4462 @raboof
  • `-Dsbt.global.base`プロパティ内の`~`をユーザーホームに展開します。 #4367 @kai-chi
  • `def sequential[A](tasks: Seq[Initialize[Task[A]]]): Initialize[Task[A]]`を追加します。 #4369 @3tty0n
  • コマンド失敗時にエラーイベントを送信するようにsbtサーバーを実装しました。 #4378 @andreaTP
  • LSPクライアントによるリクエストのキャンセルを実装しました。 #4384 @andreaTP
  • sbtコマンドを補完するためにsbtサーバーで`"sbt/completion"`コマンドを実装しました。 #4397 @andreaTP
  • sbtサーバーによって報告されるエラーの順序を修正しました。 #4497 @tdroxler
  • キャッシュされた解決を修正しました。 #4424 @eed3si9n
  • sbtタスク定義リンターは、デフォルトでエラーではなく警告を表示するようになりました。`import sbt.dsl.LinterLevel.Ignore`をスコープ内に入れることで、リンターを完全に無効にできます。 #4485 @eatkins
  • Full GCは、sbtが少なくとも1分間アイドル状態であった場合にのみ自動的にトリガーされ、シェルコマンド間で最大1回のみ実行されます。これにより、シェルの応答性が向上します。 #4544 @eatkins
  • JDK 12でのNPEを回避します。 #4549 @retronym
  • 追い出し警告のサマリーを修正しました lm#288 @bigwheel
  • ZincのAPI情報の永続化をスキップするフラグを修正しました。 zinc#399 @romanowski
  • Zincが合成トップレベルメンバーの変更を検出できない問題を修正しました。 #4316/zinc#572 @jvican
  • コンパイラのミドルエンドとバックエンドフェーズの前に、生成された非ローカルクラスのコールバックをZincが通知するようにしました。 zinc#582 @jvican
  • パフォーマンス向上のため、Zincでの正規表現の使用を削除しました。 zinc#583 @retronym
  • デフォルト引数を含む増分コンパイルを修正しました。 zinc#591 @jvican
  • ZincのスレッドセーフなAnalysisコールバックを追加しました。 zinc#626 @dotta
  • Javadocの非ゼロ終了コードがタスクに失敗しない問題を修正しました。 zinc#625 @raboof

参加 

まず、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。ありがとうございました!