sbtのpreviousマクロ

最新のsbtに面白い機能が入っていたのを見つけたので紹介。このコミット

https://github.com/sbt/sbt/commit/c669606999193ab06dec825733fbd68a3afe4d3b#diff-6c1b7251b13f9eae3a2fd7bd6d1481fcR146


これ書いている時点から一ヶ月前(つまり2013年の12月)に入ったばかりの機能です。なので0.13.1にも入っておらず、0.13.2-M1以降です。


どんな機能かというと
TaskKeyに対して、previousというマクロ呼出しをすると、Taskの前回実行時の結果が簡単に取得できる」
というものです。*1とりあえずサンプルを作ってみましょう。


まずは、previousマクロ使わずに、jarのサイズ(byte数)を取得するだけの簡単なタスクを定義してみましょう。

val jarSize = TaskKey[Long]("jarSize")
 
jarSize := {
  val s = streams.value.log
  val size = (packageBin in Compile).value.length
  s.info(size.toString)
  size
}

packageBin in Compileが、普通にpackageをした際に生成されるjarを表すもので、java.io.Fileオブジェクトが取得できます。そしてそれに対してlengthを呼び出してprintするだけの単純なTaskです。


ちなみに、本当に単にprintしたいだけなら、上記のようにTaskKey[Long]ではなくTaskKey[Unit]にしてもいいです。
が、他のTaskからこのTask値を使う場合などを考慮して、値を返すようにしておくほうが便利です。
また、今回も「Taskの前回実行時の結果」を取得するには、値を返しておかないといけないので、TaskKey[Long]というようにしておくことは必要不可欠です。



さて、ここからやっと本題です。以下が、previousマクロを使ったversionです。

val jarSize = TaskKey[Long]("jarSize")
 
jarSize := {
  import sbinary.DefaultProtocol._
  val s = streams.value.log
  val current = (packageBin in Compile).value.length
  s.info("current  " + current)
  jarSize.previous.foreach{ previous =>
    s.info("previous " + previous)
    s.info((current - previous).toString)
  }
  current
}

previousは、TaskKeyに対して呼び出します(今回はjarSizeのkeyに対して)。「初回実行で前回の値が存在しない場合」もありえるので、previousの結果値はOptionです。
上記の例では

  • まず現在のjarSizeを表示し
  • もし前回のjarSizeが取得出来た場合は、前回値をprint
  • さらに、今回と前回の差分(何byte大きく or 小さくなったか?)

を表示します。

これを使って~jarSizeで継続実行すれば
「それぞれのコードの変更によって、jarのサイズがどのように変化するか?」
が簡単に取得でき
「jarファイルサイズのコードゴルフ
をするのに役に立ちますね!(そんなことする人はほとんどいないでしょうが・・・)


ちなみに、コミットのコメントに

TODO: user documentation
TODO: stable selection of the Format when there are multiple .previous calls on the same task
TODO: make it usable in InputTasks, specifically Parsers

などとあったので、まだ改善の余地というかやること残っているようです。(もしかしたら、使い方変わったりするのだろうか?)



previousマクロ使って、なにか面白い便利なTaskできたら教えて下さい。

*1:このマクロ使わなくても、頑張れば同じことできるはず?だけど、その場合どうやるんだろう・・・