问题描述
在我的 SBT 项目中,我有一个输入文件 src/main/greeting/Greeting.txt
,其内容如下:
In my SBT project, I have an input file src/main/greeting/Greeting.txt
with the following content:
Hello, world!
这是我的 build.sbt
,它从 Greeting.txt
文件生成 Scala 源代码:
This is my build.sbt
that generates Scala source from the Greeting.txt
file:
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
文件已更改时运行这些任务.我怎样才能做到这一点?
This build.sbt
works fine, except that it runs my tasks to generate the Scala sources every time I compile/run my project. I would like it to only run these tasks when the Greeting.txt
-file has changed. How can I achieve this?
生成项目的 Bash 脚本:
Bash-script that generates the project:
#!/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
重复文件/文档
- 这是 2012 年的答案,自那以后发生了很多变化.
- 当前参考手册建议使用 "
sbt.Tracked.{ inputChanged, outputChanged }
等",但没有对此进行扩展,并且Tracked
对象在手册中的其他任何地方都没有提及. - This is an answer from 2012, a lot has changed since then.
- The current reference manual advises to use "
sbt.Tracked.{ inputChanged, outputChanged }
etc", but does not expand on that, and theTracked
object is not mentioned anywhere else in the manual.
Duplicates / Documentation
推荐答案
您可以使用 FileFunction.cached
,这是一个:
You can use FileFunction.cached
, which is a:
用于帮助构建/工件生成/等步骤的通用更改检测助手检测它们是否需要运行.
它使用缓存文件夹,SBT 会在其中自动保存文件更改的记录.使用 FileFunction.cached
,您的 build.sbt
可能如下所示:
It uses a cache folder, where SBT automatically keeps a record of the file changes. With FileFunction.cached
, your build.sbt
might look like this:
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
运行时自动清理.
The first parameter for FileFunction.cached
is a directory that will be used to store cache information (e.g. hashes of the input files). Here, we passed streams.value.cacheDirectory / "greeting"
, which will create a cache subdirectory somewhere inside of the target
-directory. The advantage is that his directory will be automatically cleaned when the task clean
is run.
cached
方法的第一个参数列表采用两个额外的可选 inStyle
和 outStyle
参数,它们决定了如何检测更改(例如通过修改日期,或通过比较哈希值).请注意,在旧版本的 SBT 中,这两个参数是必需的,因此您的 cachedFun
看起来有点像这样:
The first argument list of the cached
method takes two additional optional inStyle
and outStyle
arguments, which determine how changes are detected (e.g. by modification date, or by comparing hashes). Note that in older versions of SBT, these two arguments are mandatory, so that your cachedFun
would look somewhat like this:
val cachedFun = FileFunction.cached(
cacheBaseDirectory = streams.value.cacheDirectory / "greeting",
inStyle = FilesInfo.lastModified,
outStyle = FilesInfo.exists
)(cachedFunBodyImpl)
FileFunction.cached
方法的第二个参数列表采用将输入文件的 Set
映射到输出的 Set
的函数文件.仅当输入文件发生更改时才会调用它.
The second argument list of the FileFunction.cached
-method takes a function that maps a Set
of input files to the Set
of output files. It is invoked only if the input files have changed.
您可以找到有关旧版 SBT 的更多信息 here (SBT 0.13.5),它扩展了 cached
和文件跟踪样式.引用:
You can find more info for an older version of SBT here (SBT 0.13.5), which expands on cached
and file tracking styles. Quoting:
第一个参数列表有两个额外的参数,允许明确指定文件跟踪样式.默认情况下,输入跟踪样式为 FilesInfo.lastModified,基于文件的最后修改时间,输出跟踪样式为 FilesInfo.exists,仅基于文件是否存在.另一种可用的样式是 FilesInfo.hash,它根据文件内容的哈希值跟踪文件.
第一个代码片段已经使用 SBT 1.2.8 进行了测试.第二个代码段也应该适用于较早的 0.13.x 版本.
The first code snippet has been tested using SBT 1.2.8. The second code snippet should also work with earlier 0.13.x versions.
这篇关于SBT sourceGenerators 任务 - 仅在文件更改时执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!