SBT sourceGenerators任务 - 仅在文件更改时执行

Ale*_*136 14 scala sbt

在我的SBT项目中,我有一个输入文件,src/main/greeting/Greeting.txt其中包含以下内容:

Hello, world!
Run Code Online (Sandbox Code Playgroud)

这是我build.sbtGreeting.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
Run Code Online (Sandbox Code Playgroud)

build.sbt很好,除了它每次编译/运行我的项目时运行我的任务来生成Scala源.我希望它只在Greeting.txt-file已更改时运行这些任务.我怎样才能做到这一点?


MCVE

生成项目的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
Run Code Online (Sandbox Code Playgroud)

重复/文档

  • 是2012年的答案,从那以后发生了很多变化.
  • 目前的参考手册建议使用" sbt.Tracked.{ inputChanged, outputChanged } 等",但在不扩大,并Tracked在手册中没有提到的目的其他地方.

Gui*_*rez 7

您可以使用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
Run Code Online (Sandbox Code Playgroud)

的第一个参数FileFunction.cached是将用于存储缓存信息(例如,输入文件的哈希值)的目录。在这里,我们传递了streams.value.cacheDirectory / "greeting",它将在target-directory 内部的某个地方创建一个缓存子目录。优点clean是运行任务时将自动清除其目录。

cached方法的第一个参数列表包含两个附加的可选参数inStyleoutStyle参数,它们确定如何检测更改(例如,通过修改日期或通过比较散列)。请注意,在较旧的SBT版本中,这两个参数是强制性的,因此您的cachedFun外观如下所示:

val cachedFun = FileFunction.cached(
  cacheBaseDirectory = streams.value.cacheDirectory / "greeting",
  inStyle = FilesInfo.lastModified,
  outStyle = FilesInfo.exists
)(cachedFunBodyImpl)
Run Code Online (Sandbox Code Playgroud)

FileFunction.cached-method 的第二个参数列表采用一个函数,将Set输入文件的a映射到Set输出文件的。仅当输入文件已更改时才调用它。

您可以在此处(SBT 0.13.5)找到有关旧版本SBT的更多信息,该cached文件进一步扩展了文件跟踪样式。报价:

第一个参数列表有两个附加参数,可用于明确指定文件跟踪样式。默认情况下,基于文件的上次修改时间,输入跟踪样式为FilesInfo.lastModified,而仅根据文件是否存在,输出跟踪样式为FilesInfo.exists。另一种可用的样式是FilesInfo.hash,它基于文件内容的哈希值跟踪文件。


第一个代码段已使用SBT 1.2.8进行了测试。第二个代码段也应与较早的0.13.x版本一起使用。