Generating boilerplate源代码可以正常工作:
sourceGenerators in Compile <+= sourceManaged in Compile map { srcDir =>
DslBoilerplate.generate(srcDir, Seq(
"path/to/a/definition/file"
))
}
当我运行sbt compile
时,这也会编译生成的源代码文件,从而产生一些类文件。我只是不希望每次在开发过程中重新编译项目时都重新编译生成的源代码。
所以,从类文件中我创建了一个jar文件并使用它来代替生成的源/类文件(我删除了那些文件)。这工作正常,现在可以通过jar文件访问生成的代码。有没有办法让sbt在初始项目构建中执行4个步骤(如果需要?)?:
(在this question中他们使用sbt.IO.jar方法创建一个jar但是他们已经有了现有的文件......)
还是有另一种比制作jar更好的方法来避免重新编译生成的源代码吗?
但这实际上不是我原来的问题。很抱歉不够清楚。如果我们认为这发生了2次转换,那可能会更清楚:
输入文件 --- 1 ---&gt; 源文件(* .scala) --- 2- - &gt; 目标文件(* .class)
转换的位置
当我使用sbt compile
编译项目时,这一切都正常。
但是如果我“重建项目”(在IntelliJ中),生成的源代码(来自sbt编译)将再次编译,这就是我想要避免的 - 但同时可以访问该代码。有没有其他方法可以避免编译,而不是将此代码放入jar中,然后删除源文件和目标文件?
所以我试着继续沿着与sbt搏斗的思路来使它创建一个源和目标jar - 仍然无法制作目标jar。这是我到目前为止提出的(在this的帮助下):
sourceGenerators in Compile += Def.task[Seq[File]] {
val srcDir = (sourceManaged in Compile).value
val targetDir = (classDirectory in Compile).value
// Picking up inputs for source generation
val inputDirs = Seq("examples/src/main/scala/molecule/examples/seattle")
// generate source files
val srcFiles = DslBoilerplate.generate(srcDir, inputDirs)
// prepare data to create jars
val srcFilesData = files2TupleRec("", srcDir)
val targetFilesData = files2TupleRec("", targetDir)
// debug
println("### srcDir: " + srcDir)
println("### srcFilesData: \n" + srcFilesData.mkString("\n"))
println("### targetDir: " + targetDir)
println("### targetFilesData: \n" + targetFilesData.mkString("\n"))
// Create jar from generated source files - works fine
val srcJar = new File("lib/srcFiles.jar/")
println("### sourceJar: " + srcJar)
sbt.IO.jar(srcFilesData, srcJar, new java.util.jar.Manifest)
// Create jar from target files compiled from generated source files
// Oops - those haven't been created yet, so this jar becomes empty... :-(
// Could we use dependsOn to have the source files compiled first?
val targetJar = new File("lib/targetFiles.jar/")
println("### targetJar: " + targetJar)
sbt.IO.jar(targetFilesData, targetJar, new java.util.jar.Manifest)
val cache = FileFunction.cached(
streams.value.cacheDirectory / "filesCache",
inStyle = FilesInfo.hash,
outStyle = FilesInfo.hash
) {
in: Set[File] => srcFiles.toSet
}
cache(srcFiles.toSet).toSeq
}.taskValue
def files2TupleRec(pathPrefix: String, dir: File): Seq[Tuple2[File, String]] = {
sbt.IO.listFiles(dir) flatMap {
f => {
if (f.isFile && f.name.endsWith(".scala")) Seq((f, s"${pathPrefix}${f.getName}"))
else files2TupleRec(s"${pathPrefix}${f.getName}/", f)
}
}
}
也许我还不需要创建罐子?也许它们不应该在源生成任务中创建?我需要帮助......
f.name.endsWith(".scala")
,dohh 过滤它们,我就无法制作带有类文件的jar
由于我最初的问题不是那么清楚,而塞思的答案正在解决一个明显的解释,我会接受他的回答(经过更多调查后,我看到我应该提出另一个问题)。
答案 0 :(得分:2)
您希望使用FileFunction.cached
,以便不会每次都重新生成源文件。
这是我自己构建的一个例子:
sourceGenerators in Compile += Def.task[Seq[File]] {
val src = (sourceManaged in Compile).value
val base = baseDirectory.value
val s = streams.value
val cache =
FileFunction.cached(s.cacheDirectory / "lexers", inStyle = FilesInfo.hash, outStyle = FilesInfo.hash) {
in: Set[File] =>
Set(flex(s.log.info(_), base, src, "ImportLexer"),
flex(s.log.info(_), base, src, "TokenLexer"))
}
cache(Set(base / "project" / "flex" / "warning.txt",
base / "project" / "flex" / "ImportLexer.flex",
base / "project" / "flex" / "TokenLexer.flex")).toSeq
}.taskValue
此处.txt
和.flex
文件是生成器的输入文件。生成源文件的实际工作是通过我的flex
方法进行的,该方法返回java.io.File
:
def flex(log: String => Unit, base: File, dir: File, kind: String): File =
...
您应该能够将此技术应用于您的构建。
FileFunction.cached
在API文档和sbt常见问题解答中描述了“如果输入文件未更改,任务如何避免重做工作?” (http://www.scala-sbt.org/0.13/docs/Faq.html)。 (如果缓存上的材料也来自http://www.scala-sbt.org/0.13/docs/Howto-Generating-Files.html,那将会很好;目前它不是。)