SBT sourceGenerators task - execute only if a file changes

后端 未结 1 566
星月不相逢
星月不相逢 2021-02-07 14:09

In my SBT project, I have an input file src/main/greeting/Greeting.txt with the following content:

Hello, world!

This is my

相关标签:
1条回答
  • 2021-02-07 14:16

    You can use FileFunction.cached, which is a:

    Generic change-detection helper used to help build / artifact generation / etc. steps detect whether or not they need to run.

    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
    

    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.

    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)
    

    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.

    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:

    There are two additional arguments for the first parameter list that allow the file tracking style to be explicitly specified. By default, the input tracking style is FilesInfo.lastModified, based on a file's last modified time, and the output tracking style is FilesInfo.exists, based only on whether the file exists. The other available style is FilesInfo.hash, which tracks a file based on a hash of its contents.


    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.

    0 讨论(0)
提交回复
热议问题