我想编译一个包含java源代码生成器的项目,然后在单个项目中编译生成的代码。 即:编译Generator.scala,运行Generator.generate(outputDir),将outputDir,package编译成jar。 我正在尝试这个:
sourceGenerators in Compile <+= sourceManaged in Compile map { out =>
Generator.generate(out / "generated")
}
但是sbt抱怨
[error] Build.scala:1: object example is not a member of package org
[error] import org.example.Generator
基本上,sbt没有看到它编译的项目中定义的Generator。 是不是可以用sbt做到这一点?
答案 0 :(得分:13)
所以,在深入研究一下之后,我想出了一个解决方案。首先,您需要将项目分成两个子项目。 gen
拥有包含您的生成器代码的所有源代码。 use
取决于gen
并使用生成器。
import sbt._
import Keys._
import java.io.{ File ⇒ JFile, FileOutputStream }
object OverallBuild extends Build {
lazy val root = Project(id = "overall", base = file(".")).aggregate(gen, use)
lazy val gen = Project(id = "generate", base = file("gen"))
val myCodeGenerator = TaskKey[Seq[File]]("mycode-generate", "Generate My Awesome Code")
lazy val use = Project(id = "use", base = file("use"),
settings = Defaults.defaultSettings ++ Seq(
sourceGenerators in Compile <+= (myCodeGenerator in Compile),
myCodeGenerator in Compile <<=
(javaSource in Compile, dependencyClasspath in Runtime in gen) map {
(javaSource, cp) ⇒ runMyCodeGenerator(javaSource, cp.files)
})).dependsOn(gen)
def runMyCodeGenerator(javaSource: File, cp: Seq[File]): Seq[File] = {
val mainClass = "com.yourcompany.myCodeGenerator"
val tmp = JFile.createTempFile("sources", ".txt")
val os = new FileOutputStream(tmp)
try {
val i = new Fork.ForkScala(mainClass).fork(None, Nil, cp,
Seq(javaSource.toString),
None,
false,
CustomOutput(os)).exitValue()
if (i != 0) {
error("Trouble with code generator")
}
} finally {
os.close()
}
scala.io.Source.fromFile(tmp).getLines.map(f ⇒ file(f)).toList
}
}
在这种情况下,我生成.java文件,所以我将javaSource
传递给生成器。
重要的是,当我们在这里使用sourceGenerators时,执行的任务必须返回生成的所有文件的Seq[File]
,以便sbt可以管理它们。在此实现中,我们的生成器将完整路径文件名输出到标准输出,然后将它们保存到临时文件中。
就像Scala和SBT一样,你可以做任何事情,只需要深入研究它。
答案 1 :(得分:1)
项目描述在加载时编译。无法直接调用在运行时生成的新代码。除非我猜测使用某种反射,确保没有分支JVM,并以某种方式将这些类加载到类加载器中。
我能想到的唯一方法是在项目定义中创建一个项目。
root
- src
- project/
- Build.scala // normal project definition
- project/
- Build.scala // inner most
在最内层项目定义中,您可以将外部src定义为src文件夹。这将为您提供可用于实际项目的Generator的编译版本。然后在正常的项目定义中添加导入到Generator并按原样使用它。
我很确定最里面的项目只会加载和编译一次。如果对Generator进行更改,则需要重新加载项目定义。退出和重新开放是最简单/最愚蠢的方式,但它可能有助于测试。如果有效,请查找更智能的重新加载方式。