在我的SBT项目中,我有一个输入文件src/main/greeting/Greeting.txt
,其中包含以下内容:
Hello, world!
这是我build.sbt
从Greeting.txt
文件生成Scala源代码:
sourceGenerators in Compile += Def.task{
println("GENERATING FILES")
val inputFile = file("src/main/greeting/Greeting.txt")
val generatedFile =
(sourceManaged in Compile).value / "scala" / "Main.scala"
val greeting = IO.read(inputFile).trim
IO.write(
generatedFile,
s"""object Main extends App { println("${greeting}") }"""
)
Seq(generatedFile)
}.taskValue
这个build.sbt
工作正常,除了它运行我的任务以生成Scala源每次我编译/运行我的项目。我希望它只在Greeting.txt
- 文件发生变化时才运行这些任务。我怎样才能做到这一点?
生成项目的Bash脚本:
#!/bin/bash
mkdir sourceGeneratorsExample
cd sourceGeneratorsExample
mkdir -p src/main/scala
mkdir -p src/main/greeting
echo "Hello, world!" >> src/main/greeting/Greeting.txt
cat <<HEREDOC > build.sbt
sourceGenerators in Compile += Def.task{
println("GENERATING FILES")
val inputFile = file("src/main/greeting/Greeting.txt")
val generatedFile =
(sourceManaged in Compile).value / "scala" / "Main.scala"
val greeting = IO.read(inputFile).trim
IO.write(
generatedFile,
"object Main extends App { println(\"" + greeting + "\") }"
)
Seq(generatedFile)
}.taskValue
HEREDOC
sbt.Tracked.{ inputChanged, outputChanged }
etc“,但没有对此进行扩展,并且手册中的任何其他地方都没有提到Tracked
对象。答案 0 :(得分:4)
您可以使用FileFunction.cached
,它是:
通用变化检测助手,用于帮助构建/工件生成等步骤来检测是否需要运行。
它使用缓存文件夹,SBT在其中自动保存文件更改的记录。使用FileFunction.cached
,您的build.sbt
可能看起来像这样:
sourceGenerators in Compile += Def.task{
// * Create a cached function which generates the output files
// only if the input files have changed.
// * The first parameter is a file instance of the path to
// the cache folder
// * The second parameter is the function to process the input
// files and return the output files
val cachedFun = FileFunction.cached(
streams.value.cacheDirectory / "greeting"
) { (in: Set[File]) =>
println("GENERATING FILES")
val generatedFile =
(sourceManaged in Compile).value / "scala" / "Main.scala"
val greeting = IO.read(in.head).trim
IO.write(
generatedFile,
"object Main extends App { println(\"" + greeting + "\") }"
)
Set(generatedFile)
}
// get the input file
val inputFile = file("src/main/greeting/Greeting.txt")
// put the input file into a `Set` (as required by `cachedFun`),
// pass it to the `cachedFun`,
// convert the result to `Seq` (as required by `Def.task`)
cachedFun(Set(inputFile)).toSeq
}.taskValue
FileFunction.cached
的第一个参数是一个目录,将用于存储缓存信息(例如,输入文件的哈希值)。在这里,我们传递了streams.value.cacheDirectory / "greeting"
,它将在target
目录内部的某个地方创建一个缓存子目录。优点在于,运行任务clean
时,将自动清除其目录。
cached
方法的第一个参数列表采用两个附加的可选inStyle
和outStyle
参数,它们确定如何检测更改(例如,通过修改日期或通过比较哈希值)。请注意,在较旧的SBT版本中,这两个参数是强制性的,因此您的cachedFun
看起来会像这样:
val cachedFun = FileFunction.cached(
cacheBaseDirectory = streams.value.cacheDirectory / "greeting",
inStyle = FilesInfo.lastModified,
outStyle = FilesInfo.exists
)(cachedFunBodyImpl)
FileFunction.cached
方法的第二个参数列表采用一个函数,该函数将输入文件的Set
映射到输出文件的Set
。仅当输入文件已更改时才调用它。
您可以找到有关旧版本SBT here(SBT 0.13.5)的更多信息,该版本在cached
和文件跟踪样式上进行了扩展。报价:
第一个参数列表有两个附加参数,可用于明确指定文件跟踪样式。默认情况下,基于文件的上次修改时间,输入跟踪样式为FilesInfo.lastModified,而仅根据文件是否存在,输出跟踪样式为FilesInfo.exists。另一种可用的样式是FilesInfo.hash,它基于文件内容的哈希来跟踪文件。
第一个代码段已使用SBT 1.2.8进行了测试。第二个代码段也应与早期的0.13.x版本一起使用。