2013-09-11 12:52:52 +02:00
|
|
|
/**
|
2017-01-04 17:37:10 +01:00
|
|
|
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
2013-09-11 12:52:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package akka
|
|
|
|
|
|
2017-10-30 03:13:14 +02:00
|
|
|
import java.io.File
|
|
|
|
|
import java.io.PrintWriter
|
|
|
|
|
|
|
|
|
|
import scala.sys.process._
|
|
|
|
|
|
2013-09-11 12:52:52 +02:00
|
|
|
import sbt._
|
2017-10-30 03:13:14 +02:00
|
|
|
import sbt.util.CacheStoreFactory
|
2013-09-11 12:52:52 +02:00
|
|
|
import Keys._
|
|
|
|
|
|
|
|
|
|
object Protobuf {
|
|
|
|
|
val paths = SettingKey[Seq[File]]("protobuf-paths", "The paths that contain *.proto files.")
|
|
|
|
|
val outputPaths = SettingKey[Seq[File]]("protobuf-output-paths", "The paths where to save the generated *.java files.")
|
|
|
|
|
val protoc = SettingKey[String]("protobuf-protoc", "The path and name of the protoc executable.")
|
|
|
|
|
val protocVersion = SettingKey[String]("protobuf-protoc-version", "The version of the protoc executable.")
|
|
|
|
|
val generate = TaskKey[Unit]("protobuf-generate", "Compile the protobuf sources and do all processing.")
|
|
|
|
|
|
|
|
|
|
lazy val settings: Seq[Setting[_]] = Seq(
|
2015-02-18 00:15:50 +01:00
|
|
|
paths := Seq((sourceDirectory in Compile).value, (sourceDirectory in Test).value).map(_ / "protobuf"),
|
|
|
|
|
outputPaths := Seq((sourceDirectory in Compile).value, (sourceDirectory in Test).value).map(_ / "java"),
|
2013-09-11 12:52:52 +02:00
|
|
|
protoc := "protoc",
|
|
|
|
|
protocVersion := "2.5.0",
|
2015-02-18 00:15:50 +01:00
|
|
|
generate := {
|
|
|
|
|
val sourceDirs = paths.value
|
|
|
|
|
val targetDirs = outputPaths.value
|
2017-10-30 03:13:14 +02:00
|
|
|
val log = streams.value.log
|
2015-02-18 00:15:50 +01:00
|
|
|
|
|
|
|
|
if (sourceDirs.size != targetDirs.size)
|
|
|
|
|
sys.error(s"Unbalanced number of paths and destination paths!\nPaths: $sourceDirs\nDestination Paths: $targetDirs")
|
|
|
|
|
|
|
|
|
|
if (sourceDirs exists (_.exists)) {
|
|
|
|
|
val cmd = protoc.value
|
2017-10-30 03:13:14 +02:00
|
|
|
|
2015-02-18 00:15:50 +01:00
|
|
|
checkProtocVersion(cmd, protocVersion.value, log)
|
|
|
|
|
|
|
|
|
|
val base = baseDirectory.value
|
|
|
|
|
val sources = base / "src"
|
|
|
|
|
val targets = target.value
|
|
|
|
|
val cache = targets / "protoc" / "cache"
|
|
|
|
|
|
2017-10-06 10:30:28 +02:00
|
|
|
(sourceDirs zip targetDirs) map {
|
|
|
|
|
case (src, dst) ⇒
|
|
|
|
|
val relative = src.relativeTo(sources).getOrElse(throw new Exception(s"path $src is not a in source tree $sources")).toString
|
|
|
|
|
val tmp = targets / "protoc" / relative
|
|
|
|
|
IO.delete(tmp)
|
|
|
|
|
generate(cmd, src, tmp, log)
|
|
|
|
|
transformDirectory(tmp, dst, _ ⇒ true, transformFile(_.replace("com.google.protobuf", "akka.protobuf")), cache, log)
|
2015-02-18 00:15:50 +01:00
|
|
|
}
|
|
|
|
|
}
|
2017-10-06 10:30:28 +02:00
|
|
|
})
|
2013-09-11 12:52:52 +02:00
|
|
|
|
2017-10-06 10:30:28 +02:00
|
|
|
private def callProtoc[T](protoc: String, args: Seq[String], log: Logger, thunk: (ProcessBuilder, Logger) ⇒ T): T =
|
2013-09-11 12:52:52 +02:00
|
|
|
try {
|
|
|
|
|
val proc = Process(protoc, args)
|
|
|
|
|
thunk(proc, log)
|
2017-10-06 10:30:28 +02:00
|
|
|
} catch {
|
|
|
|
|
case e: Exception ⇒
|
|
|
|
|
throw new RuntimeException("error while executing '%s' with args: %s" format (protoc, args.mkString(" ")), e)
|
2013-09-11 12:52:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def checkProtocVersion(protoc: String, protocVersion: String, log: Logger): Unit = {
|
2017-10-06 10:30:28 +02:00
|
|
|
val res = callProtoc(protoc, Seq("--version"), log, { (p, l) ⇒ p !! l })
|
2013-09-11 12:52:52 +02:00
|
|
|
val version = res.split(" ").last.trim
|
|
|
|
|
if (version != protocVersion) {
|
|
|
|
|
sys.error("Wrong protoc version! Expected %s but got %s" format (protocVersion, version))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def generate(protoc: String, srcDir: File, targetDir: File, log: Logger): Unit = {
|
|
|
|
|
val protoFiles = (srcDir ** "*.proto").get
|
|
|
|
|
if (srcDir.exists)
|
|
|
|
|
if (protoFiles.isEmpty)
|
|
|
|
|
log.info("Skipping empty source directory %s" format srcDir)
|
|
|
|
|
else {
|
|
|
|
|
targetDir.mkdirs()
|
|
|
|
|
|
|
|
|
|
log.info("Generating %d protobuf files from %s to %s".format(protoFiles.size, srcDir, targetDir))
|
2017-10-06 10:30:28 +02:00
|
|
|
protoFiles.foreach { proto ⇒ log.info("Compiling %s" format proto) }
|
2013-09-11 12:52:52 +02:00
|
|
|
|
|
|
|
|
val exitCode = callProtoc(protoc, Seq("-I" + srcDir.absolutePath, "--java_out=%s" format targetDir.absolutePath) ++
|
2017-10-06 10:30:28 +02:00
|
|
|
protoFiles.map(_.absolutePath), log, { (p, l) ⇒ p ! l })
|
2013-09-11 12:52:52 +02:00
|
|
|
if (exitCode != 0)
|
|
|
|
|
sys.error("protoc returned exit code: %d" format exitCode)
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-30 03:13:14 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a transformed version of all files in a directory, given a predicate and a transform function for each file. From sbt-site
|
|
|
|
|
*/
|
|
|
|
|
private def transformDirectory(sourceDir: File, targetDir: File, transformable: File ⇒ Boolean, transform: (File, File) ⇒ Unit, cache: File, log: Logger): File = {
|
|
|
|
|
val runTransform = FileFunction.cached(CacheStoreFactory(cache), FilesInfo.hash, FilesInfo.exists) { (in: ChangeReport[File], out: ChangeReport[File]) ⇒
|
|
|
|
|
val map = Path.rebase(sourceDir, targetDir)
|
|
|
|
|
if (in.removed.nonEmpty || in.modified.nonEmpty) {
|
|
|
|
|
log.info("Preprocessing directory %s..." format sourceDir)
|
|
|
|
|
for (source ← in.removed; target ← map(source)) {
|
|
|
|
|
IO delete target
|
|
|
|
|
}
|
|
|
|
|
val updated = for (source ← in.modified; target ← map(source)) yield {
|
|
|
|
|
if (source.isFile) {
|
|
|
|
|
if (transformable(source)) transform(source, target)
|
|
|
|
|
else IO.copyFile(source, target)
|
|
|
|
|
}
|
|
|
|
|
target
|
|
|
|
|
}
|
|
|
|
|
log.info("Directory preprocessed: " + targetDir)
|
|
|
|
|
updated
|
|
|
|
|
} else Set.empty
|
|
|
|
|
}
|
|
|
|
|
val sources = (sourceDir.allPaths).get.toSet
|
|
|
|
|
runTransform(sources)
|
|
|
|
|
targetDir
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Transform a file, line by line.
|
|
|
|
|
*/
|
|
|
|
|
def transformFile(transform: String ⇒ String)(source: File, target: File): Unit = {
|
|
|
|
|
IO.reader(source) { reader ⇒
|
|
|
|
|
IO.writer(target, "", IO.defaultCharset) { writer ⇒
|
|
|
|
|
val pw = new PrintWriter(writer)
|
|
|
|
|
IO.foreachLine(reader) { line ⇒ pw.println(transform(line)) }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-11 12:52:52 +02:00
|
|
|
}
|