正確にはそれをするだけのつもりで作りはじめたら、微妙に思い出して追加のtask入れたのでそれだけではないのですが、とりあえず scalafixAll --check
するだけの scalafixCheckAll
taskの話をします。
https://github.com/xuwei-k/scalafix-check/commit/6b4127d96e0626b7493d71eb8cfa0c47f272136a
scalafixをsbtから使うなら、公式のsbt-scalafix pluginをほぼ必ず使うわけです。
sbt-scalafixは、いくつかのkeyが定義されていますが、割とInputKeyが多いです。InputKeyというのは引数を取るInputTask定義用のKeyです。引数を取らない普通のTask用のKey定義はTaskKeyです。
https://www.scala-sbt.org/1.x/docs/Input-Tasks.html
InputKeyにして柔軟に色々やることによって、定義する側は無闇に大量にkeyを定義しなくて済むし、使う側も(定義する側がparser使ってると)タブ補完が効いたりもするし、かなり便利に柔軟に指定出来て便利です。
sbtの正しい良い使い方の例だと思います。
じゃあなぜわざわざそれをそのまま使わずに、あえて定義してsbt pluginにまでしたか?というと
「InputTaskのままでは、他のTaskと同時に指定して並列実行出来ない」
というのが1番の理由です。
同時に実行したい理由は、たいした差ではないけど、その方が原理上は速く終わる可能性があるからです。*1
つまり、lint系のtaskをまとめて実行したい場合にCIのyamlなどに
sbt scalafmtCheckAll "scalafixAll --check"
と書けば、直列実行になりますが、sbt標準の all
というものを使って同時実行したくなりますが、そのままでは不可能ですね? --check
なしで
sbt "all scalafmtCheckAll scalafixAll"
としても、以下のようなエラーになります
[error] Cannot mix input tasks with plain tasks/settings. Input task(s): [error] ScopedKey(Scope(Select(ProjectRef(ここは省略), Zero, Zero, Zero),scalafixAll) [error] Task(s)/setting(s): [error] ScopedKey(Scope(Select(ProjectRef(ここは省略)), Zero, Zero, Zero),scalafmtCheckAll) [error] [error] all scalafmtCheckAll scalafixAll [error] ^
これはこれで別にbuild.sbtなどに数行追加すればある意味Taskにするのは可能なわけですが
(実際にGitHubを検索した例)
とはいえ、最短で数行とはいえ、毎回書くのも面倒なので、あえてsbt pluginにしました。
また、厳密にはこれcommandのaliasとして定義するならともかく、複数のsub projectがある場合にTaskとして定義するとscalafixが有効になってるsub projectの全てに定義、的なことをしないといけないはずで、地味に面倒です。
また、ついでに、よく入れる細かい設定として
- SemanticRuleは実行せずにSyntacticRuleだけ実行したい( "--syntactic" の引数渡す)
.scalafix.conf
のruleはアルファベット順でsortして書く決まりにすることが多いが、それのチェックしたい
みたいなことがあったので、そういう地味なtaskを入れました。
sbt-scalafixに直接依存せずに無理やりリフレクションで動的にsbt-scalafixの依存があるかどうか?的な地味な工夫もしました (この工夫が必要だったのか?は若干謎)
*1:失敗するパターンにおいては、余計なtask実行されるデメリットもありますが