IDL や O/R Mapper などソース自動生成を行うツールはコンパイル前に自動実行させておくと便利です。
例えば Java/Scala で作成された foo.CodeGenerator
という main()
を持つツールを sbt コンパイル前に実行させるには Build.scala
に上記のように記述します:
import sbt._
import Keys._
import Tests._
object Build extends sbt.Build {
lazy val mainProject = Project(
id="myproject",
base=file("."),
settings = Project.defaultSettings ++ Seq(
libraryDependencies ++= List(
"foo.codegen" % "codegen" % "1.0.0",
"org.slf4j" % "slf4j-nop" % "1.6.4"
),
// sbt codeGen でマニュアル実行できるようにコマンド登録
codeGen <<= myCodeGenTask,
// コンパイルごとにソース自動生成タスクを実行するよう登録
sourceGenerators in Compile <+= myCodeGenTask
)
)
lazy val codeGen = TaskKey[Seq[File]]("generate my code.")
lazy val myCodeGenTask = (sourceManaged, dependencyClasspath in Compile, runner in Compile, streams) map { (dir, cp, r, s) =>
val outdir = (dir / "autogen").getPath
toError(r.run("foo.CodeGenerator", cp.files, Array(outdir /* コマンドライン引数 */), s.log))
Seq("Foo.scala", "Bar.scala").map{ fname => file(outdir + "/" + fname) }
}
}
ここで Maven リポジトリ foo.codegen:codegen:1.0.0
には foo.CodeGenerator
が含まれているものとします。
タスクは自動生成されたファイルの一覧を返すことでそれらがコンパイル対象に含まれます。上記の例では src_managed/autogen
の下に以下の 2 ファイルが生成されるものと仮定し:
case class Foo(x:Int)
case class Bar(y:String)
自動生成されたコードを使用するプログラムを作成して実行します。
object Hoge {
def main(args:Array[String]):Unit = {
println(Foo(99))
println(Bar("hoge"))
}
}
$ sbt compile run
[info] Loading project definition from <dir>/project
[info] Set current project to myproject (in build file:<dir>/)
[info] Updating {file:<dir>/}myproject...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Running foo.CodeGenerator <dir>/target/scala-2.10/src_managed/autogen
[info] Compiling 3 Scala sources to <dir>/target/scala-2.10/classes...
[success] Total time: 5 s, completed 2014/12/01 9:25:18
[info] Running foo.CodeGenerator <dir>/target/scala-2.10/src_managed/autogen
[info] Running Hoge
Foo(99)
Bar(hoge)
[success] Total time: 0 s, completed 2014/12/01 9:25:18
compile
と run
とで 2 度実行されているようですがコンパイルは 1 度だけですかね。自動生成とコンパイルが成功しました。