sbt 1.2.0-RC1

in

@eed3si9n

皆さんこんにちは。Lightbend Tooling team にかわって sbt 1.2.0-RC1 をアナウンスします。これは sbt 1 のフィーチャーリリース第二弾の RC-1 で、バイナリ互換性は維持しつつ新機能にフォーカスを当てたリリースとなっている。sbt 1 は Semantic Versioning にもとづいてリリースされるので、プラグインは sbt 1.x シリーズ中機能することが期待されている。

  • 2018年7月6日までに大きな問題が見つからなければ、1.2.0-RC1 は 1.2.0 final 版となる予定だ。

sbt 1.2 の主な新機能はクロスJDK forking、composite project、そして実験的な thin client だ。sbt 1.1 から 6ヶ月の間他にも色々なバグ修正や改善点がたまっていた。

プラグイン開発のための SbtPlugin

SbtPlugin は、プロジェクトが sbt plugin であることを宣言するためのプラグインだ。これは自動的に scripted test を導入して、sbtPlugin := true を設定する。

lazy val root = (project in file("."))
  .enablePlugins(SbtPlugin)

互換性に関する注意: ScritpedPlugin は triggered plugin ではなくなった。

#3875 by @eed3si9n

クロス JDK forking

runtest を fork した場合、java++ を使って Java Home を切り替えれるようになった。

sbt:helloworld> run
[info] Running (fork) Hello
[info] 1.8.0_171
sbt:helloworld> java++ 10!
[info] Reapplying settings...
sbt:helloworld> run
[info] Running (fork) Hello
[info] 10.0.1

sbt は、インストール済みの Java home を検知して discoveredJavaHomes セッティングに代入し、shyiko/jabba もサポートする。それで不十分な場合は Global / javaHomes を用いて補足する:

Global / javaHomes += "6" -> file("/something/java-6")

この機能は、ライブラリを古い JDK でテストして互換性の確認を取ることを目的としている。

#4139 by @2m, @cunei, and @eed3si9n

Composite project

sbt 1.2.0 は CompositeProject という trait を導入して、プラグイン作者が、クロスビルドなどのためにサブプロジェクトを生成することを可能とする。

trait CompositeProject {
  def componentProjects: Seq[Project]
}

これは @BennyHill さんによって #4056 にてコントリビュートされた。

Project matrix

実験段階CompositeProject のレファレンス実装として、projectMatrix という DSL を導入する sbt-projectmatrix というプラグインを実装した。

lazy val core = (projectMatrix in file("core"))
  .scalaVersions("2.12.6", "2.11.12")
  .settings(
    name := "core"
  )
  .jvmPlatform()
 
lazy val app = (projectMatrix in file("app"))
  .dependsOn(core)
  .scalaVersions("2.12.6")
  .settings(
    name := "app"
  )
  .jvmPlatform()

このプラグインはよりジェネリックなクロスビルド (Scala バージョン、プラットフォーム、その他) をサポートして、サブプロジェクトとして表現することを目的としている。上の例の projectMatrixcoreJVM2_12coreJVM2_11、そして appJVM2_12 という 3つのサブプロジェクトを作る。

Semantic Version selector API

sbt 1.2.0 は、VersionNumber() データ型に Semantic Version selector を導入し、基本的なマッチ、比較 (<=, <, >=, >)、論理演算 (>1.0.0 <2.0.0, ||)、範囲 (A.B.C - D.E.F)、ワイルドカード (2.12.x) をサポートする。

scala> import sbt.librarymanagement.{ VersionNumber, SemanticSelector }
import sbt.librarymanagement.{VersionNumber, SemanticSelector}
 
scala> VersionNumber("2.12.5").matchesSemVer(SemanticSelector(">=2.12"))
res1: Boolean = true
 
scala> VersionNumber("2.12.5").matchesSemVer(SemanticSelector("<2.12"))
res2: Boolean = false
 
scala> VersionNumber("2.13.0-M4").matchesSemVer(SemanticSelector("2.13"))
res3: Boolean = false
 
scala> VersionNumber("2.12.5").matchesSemVer(SemanticSelector("2.12.1 - 2.12.6"))
res4: Boolean = true
 
scala> VersionNumber("2.12.5").matchesSemVer(SemanticSelector("2.12.x"))
res5: Boolean = true
 
scala> VersionNumber("2.12.5").matchesSemVer(SemanticSelector("2.11.x || 2.12.x"))
res6: Boolean = true

これは Rikito Taniguchi (@tanishiking) さんにより lm#239 にてコントリビュートされた。

addPluginSbtFile コマンド

IntelliJ チームからビルドに安全にプラグインを注入したいというリクエストが以前よりあった。sbt 1.2.0 は -addPluginSbtFile というコマンドを追加してそれを可能とする。

$ cat /tmp/extra.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.7")
 
$ sbt -addPluginSbtFile=/tmp/extra.sbt
...
sbt:helloworld> plugins
In file:/xxxx/hellotest/
  ...
  sbtassembly.AssemblyPlugin: enabled in root

これは @eed3si9n により #4211 にて実装された。

拡張可能な sbt server

実験段階。プラグインを用いて sbt server を拡張できるようにした。

    Global / serverHandlers += ServerHandler({ callback =>
      import callback._
      import sjsonnew.BasicJsonProtocol._
      import sbt.internal.protocol.JsonRpcRequestMessage
      ServerIntent(
        {
          case r: JsonRpcRequestMessage if r.method == "lunar/helo" =>
            jsonRpcNotify("lunar/oleh", "")
            ()
        },
        PartialFunction.empty
      )

この機能はまだ実験的なので今後 API などが変更される可能性がある。

#3975 by @eed3si9n

Thin client(s)

実験段階。sbt 1.2.0 は -client という新しいモードを追加する。sbt が -client コマンドとともに実行されると、ビルドを読み込まず、JSON-RPC を用いて sbt server のインスタンスに接続しようとする。サーバが走っていなければ (portfile が見つからなければ) 新たな sbt インスタンスを全く別の新しい JVM 上に fork する。

これによって sbt をターミナル上、もしくはエディタなどから呼び出すことができる。

$ time sbt -client clean
[info] entering *experimental* thin client - BEEP WHIRR
[info] server was not detected. starting an instance
[info] waiting for the server...
[info] waiting for the server...
[info] waiting for the server...
[info] waiting for the server...
[info] server found
> clean
[success] completed
sbt -client clean  9.23s user 2.33s system 22% cpu 50.558 total
 
# server stays
$ ps | rg java
21860 ttys015    1:22.43 java -Xms2048M -Xmx2048M -Xss2M -jar /usr/local/Cellar/sbt/1.1.6/libexec/bin/sbt-launch.jar
22014 ttys015    0:00.00 rg java
 
$ time sbt -client clean
[info] entering *experimental* thin client - BEEP WHIRR
> clean
[info] Updating ...
[info] Done updating.
[success] completed
sbt -client clean  3.39s user 1.75s system 104% cpu 4.898 total

server を終了させるには sbt -client shutdown を呼び出す。 #4227 by @eed3si9n

さらに、thin client の代替実装も既に出ていて、Rust を用いてクリスさん cb372/sbt-client と Dale dwijnand/sbtl が作っている。

互換性に影響のある変更点

  • 廃止勧告が出ていたコマンド ------ を削除した。onFailure, sbtClearOnFailure, resumeFromFailure に移行してほしい。 #4124

その他のバグ修正や改善点

  • Command.process(String, State): State が間違って消されていたので再導入した。 #4023 by @dwijnand
  • シャットダウン時 active.json が削除されない問題を修正した。 #4194 by @veera83372
  • Windows でタイムスタンプを読むときのファイルパーミッションのエラー ("CreateFile() failed") を修正した。 io#134 by @cunei
  • .value が無いことを検知する linter を修正した。 #4090 by @eed3si9n
  • removeEscapeSequences における StringIndexOutOfBoundsException を修正した。 util#139 by @dwijnand
  • OkHttp の JavaNetAuthenticator で null pointer error が出る問題を修正した。 lm#177 by @eed3si9n
  • Sonatype がタイムアウトする問題をデフォルトを 1h まで延ばして修正した。 lm#246 by @peterneyens
  • 並列ダウンロード時にスレッドスラッシングしてしてまう問題を修正した。 lm249 by @OlegYch
  • JavaDoc の警告がエラーとしてログに表示される問題を修正した。 zinc#506 by @kaygorodov
  • クラス依存性が classOf[A] を拾わない問題を修正した。 zinc#510 by @natansil
  • クラス依存性が存在しない object を含む問題を修正した。 zinc422 by @romanowski
  • 廃止された 0.10/0.12 DSL の移行に関するドキュメンテーションへのリンクを修正した。 #3901 by [@colindean]
  • skip キーのドキュメンテーションを修正した。 #3926 by @dkim
  • fork しない並列テストにおける競合問題を修正した。 #3985 by @retronym
  • Global / cancelabletrue に設定された場合の fork されたテストにおける Ctrl-C の取り扱いを修正した。 #4226 by @driquelme
  • run のスタックトレースを修正した。 #4232 by @eed3si9n

  • ++ <scala-version> <command> が互換性のあるサブプロくジェクトのみで <command> を実行するようにした。 #3698/#3995 by @ruippeixotog

  • eviction warning がデフォルトでまとめだけを表示するようにして、ThisBuild / evictionWarningOptions で設定を変えれるようにした。 lm211 and #3947 by @exoego
  • inThisBuild(...), inConfig(C)(...), inTask(t)(...), inScope(scope)(...) が可変長変数を受け取るようにした。 #4106 by @dwijnand
  • fgRunfgRunMain タスクを追加して sbt 0.13 の run と同様に振る舞うようにした。 #4216 by @agaro1121
  • scripted のファイル名として test.scriptpending.script もサポートするようにした。 #4220 by @regadas
  • inspect コマンドがエイリアスも処理できるようにした。 #4221 by @gpoirier
  • ~ のメッセージに現行プロジェクトの id を含むようにした。 #2038 / #3813 by @dwijnand
  • PathFinder#getget() に変更した。 io#104 by @dwijnand
  • アクセスが拒否された場合のエラーメッセージを改善した。 lm#203 by @stephennancekivell
  • "Choosing local" という警告を改善した。 lm#248 by @khvatov
  • Scalac オプションの変更を無視できるオプションを追加した。 zinc#548 by @lukaszwawrzyk
  • プラグインによって導入される scripted の並列実行を可能とした。 #3891 by @jvican
  • コンフィギュレーション軸のための scope filter のファクトリーメソッド inConfigurationsByKeysinConfigurationsByRefs を追加した。 #3994
  • lastGreploadFailed、などのコマンドを追加して kebab-case のコマンドを置き換えた。 #4080 by @naferx, #4159 by @Asamsig, and #4169 by @tiqwab
  • JUnitXML レポートに timestamp フィールドを追加した。 4154 by @timcharper
  • "Loading settings" ログメッセージにサブプロジェクト名を含めた。 #4164 by @alodavi
  • about コマンドがプラグインのリストをインデントして表示するようにした。 #4187 by @mcanlas
  • -Dsbt.offlineoffline セッティングを設定するようにした。 #4198 by @eed3si9n

内部実装に関する変更

  • コンパイラ警告の削除。 #3087 by @dwijnand
  • @dwijnand によるその他のリファクタリング
  • Zinc におけるコンパイラ警告の削除。 zinc#493 by @exoego
  • 最適化: 不必要な URI のコピーを IO.directoryURI で作らないようにした。 io#132 by @jrudolph
  • 最適化: initStringCodecs で reflect universe の初期化を回避した。 util#153 by @jrudolph
  • 最適化: Parsers.validID の高速化。 #3952 by @jrudolph
  • 最適化: Scope の委譲を最適化するために for 内包表記を自前で展開した。 #4003 by @jrudolph and @eed3si9n

Scala Spree NYC

2018年6月19日に Scala Days の周辺イベントとして Scala Spree も New York City に来た。主催は Tapad社と Scala Center で、Lightbend社は朝食とランチを提供した。Scala Spree は OSS な Scala プロジェクトの代表者 (通常はメンテナ) と一緒に作業して pull request を送って、Scala コントリビュータになろうというハッカソンの一種だ。

Lightbend Tooling Team は sbt を代表して参加した。前準備として、Dale と僕は GitHub issue に "help wanted""good first issue" といったラベル付けを行って、コントリビュータ希望者が作業できるものを見つけれるようにした。

一日を通してディスカッションやバグ調査が行われ、当日や後日も含め複数の pull request があった:

  • fgRunfgRunMain タスクを追加して sbt 0.13 の run と同様に振る舞うようにした。 #4216 by @agaro1121
  • scripted のファイル名として test.scriptpending.script もサポートするようにした。 #4220 by @regadas
  • inspect コマンドがエイリアスも処理できるようにした。 #4221 by @gpoirier
  • Global / cancelabletrue に設定された場合の fork されたテストにおける Ctrl-C の取り扱いを修正した。 #4226 by @driquelme
  • "Choosing local" という警告を改善した。 lm#248 by @khvatov
  • CI プロセスに doc を追加した。 #4218 by @regadas

イベントに皆が参加しやすいようにサポートしていたフレンドリーな Tapad の皆さんにお礼を言いたい。

参加

前回の sbt 1 フィーチャーリリースは 2028年1月の sbt 1.1.0 だった。それ以降バグ修正などのために 6本のパッチリリースをリリースしたが、機能追加などは 1.x ブランチに merge されてきた。

2月の段階で sbt 1.2.0 roadmap を公開して、その中で以下の点に注力することを提案した:

  • sbt 1.x へのビルド移行
  • sbt をコントリビュートしやすくする
  • sbt server (LSP) 関連の機能拡張

2018年を通して Lightbend Tooling team はオープンソースにおけるコントリビューションにフォーカスしてきた。これは、僕たちがコミュニティーにどう貢献できるかということと、sbt や Zinc といったツール群が Scala コミュニティー全般から参加しやすくするにはどうすればいいのかという両方を含んでいる。毎週水曜日にミーティングを行ったり (是非参加したい人は声をかけてほしい)、ロードマップを公開したり、コントリビュータガイドを改善したり、再現性の低い CI テストを直すなどといったことを具体的に行ってきた。さらに、一言でコントリビューションといっても様々な方法があることを強調してきた:

  1. 採用するのを手伝う
  2. 職場や Stackoverflow などで他のユーザをアシストする
  3. ドキュメンテーションにコントリビュートする
  4. issue トラッカーのガーデニング
  5. pull request のレビューを手伝う
  6. issue を報告する
  7. エコシステムを拡張する
  8. コアをパッチする

sbt と Zinc 1 の改善に手伝ってくれた皆さんにこの場をお借りして感謝します。

sbt 1.2.0-RC1 は 47名のコントリビュータのお陰でできました (敬称略): Dale Wijnand, Eugene Yokota, Yasuhiro Tatsuno (exoego), Łukasz Wawrzyk, Kenji Yoshida (xuwei-k), Jorge Vicente Cantero (jvican), Alistair Johnson, Antonio Cunei, Rikito Taniguchi (tanishiking), Tim Harper, Aloisia Davì (alodavi), Ethan Atkins, Jason Zaugg, Johannes Rudolph, Krzysztof Romanowski, Allan Renucci, Filipe Regadas, Hiroshi Ito, OlegYch, natans, Alex Khvatov, Alexander Samsig, Andreas Jim-Hartmann, Andrei Pozolotin, Andrey Kaygorodov, Anthony Garo, Colin Dean, Daniel Riquelme, Deokhwan Kim, Guillaume Poirier, Heikki Vesalainen, Jason Pickens, Jonas Fonseca, Maksym Fedorov, Mark Canlas, Martynas Mickevičius, Michael Pollmeier, Mike Skells, Nafer Sanabria, Naohisa Murakami (tiqwab), PanAeon, Peter Neyens, Rui Gonçalves, Sean Sullivan, Seth Tisue, Stephen Nancekivell, Veera Venky. Thank you!