sbtのコンパイル時間やテスト時間を短くする方法について調べてみた

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

モバイルアプリサービス部の五十嵐です。

最近scalaのプロジェクトにアサインされたのですが、scalaのコンパイルにマシンパワーを使うため周囲からMBPのファンの音がうるさいとクレームをもらうようになりました。また、コンパイルやテストの度に数十秒〜数分かかると集中力が切れて、ついついポケモンGoをさわってしまうのでよろしくありません。

そこで、sbtのコンパイル時間やテスト時間を短くする方法について調べてみました。

なお、この記事で対象としているプロジェクトの構成は以下のとおりです。

  • sbt 1.0.3
  • scala 2.11.7

コンパイル時に不要なArtifactを作成しない

build.sbt ファイルにArtifactの生成に関する設定をすることができます。

// disable publishing the main jar produced by `package`
publishArtifact in (Compile, packageBin) := false

// disable publishing the main API jar
publishArtifact in (Compile, packageDoc) := false

// disable publishing the main sources jar
publishArtifact in (Compile, packageSrc) := false

これらの設定を実際に今のプロジェクトで試してみたんですが、コンパイル時間への影響は誤差の範囲でした。

サブプロジェクトだけを対象にする

プロジェクトが大きくなる全体をコンパイルするのには時間がかかりますから、プロジェクトをいくつかのサブプロジェクトに分割するのはよく使われる手法だと思います。実際に今のプロジェクトでも以下の3つのサブプロジェクトに分割されています。

.
├── external
├── internal
└── library

サブプロジェクトだけを対象にするには project コマンドを使います。

バッチモードなら sbt "project external" <任意のコマンド> です。 インタラクティブモードなら以下のように sbt コマンドでインタラクティブモードに入ってから project コマンドを実行することでサブプロジェクトにスイッチします。

$ sbt # インタラクティブモードに入る
[xxxx-api] $ project external # サブプロジェクトに入る
[xxxx-external-api] $ <任意のコマンド> # サブプロジェクトに対して任意のコマンドを実行する

テストする対象を限定する

テストについても便利なコマンドがあるので紹介します。通常の test コマンドはプロジェクトもしくはサブプロジェクト全体のテストが実行されますが、 testOnly コマンドを使用することで、テストする対象のSpecを限定することができます。

$ sbt # インタラクティブモードに入る
[xxxx-api] $ project external # サブプロジェクトに入る
[xxxx-external-api] $ testOnly <テスト名>

<テスト名> の部分はタブ補完ができます。また、ワイルドカード指定やスペース区切りで複数の <テスト名> を指定することができます。

前回失敗したテストや差分だけをテスト対象とする

testOnly コマンドと似たコマンドに testQuick コマンドがあります。これは testOnly コマンド同様に <テスト名> を指定しますが、実際に実行されるテストの内容は次の通りに限定されます。

  • 前回のテスト実行で失敗したテスト
  • 前回のテスト実行から新たに作成されたテスト
  • 1つ以上の推移的依存関係を持っているテスト(よくわからない)
$ sbt # インタラクティブモードに入る
[xxxx-api] $ project external # サブプロジェクトに入る
[xxxx-external-api] $ testQuick <テスト名>

変更が加えられたら自動的にコマンドを実行する

最後にMBPのファン音対策とは逆効果になりますが、便利なコマンドがあるのでご紹介。コマンドの前に ~ を付けることで、ソースコードになにか変更されたらコマンドを再実行するという事ができます。

# helpの内容
  ~ <command>                             Executes the specified command whenever source files change.

実際にやってみると、以下のようになります。コマンドが終了すると Waiting ... となり、ここで Enter を押さないかぎりソースコードの変化を待ち続けます。ソースコードに変化があると、コマンドが自動的に再実行されます。

$ sbt # インタラクティブモードに入る
[xxxx-api] $ project external # サブプロジェクトに入る
[xxxx-external-api] $ ~ compile
### 途中省略 ###
1. Waiting for source changes... (press enter to interrupt)

まとめ

テストに関しては実行時間を短縮できそうなコマンドがいくつかありました。コンパイルに関してはとても多くの設定があり自分もよくわかっていない部分が多いため今後も調べてみたいと思います。

参考