scalapluginssbtincremental-compiler

SBT plugin: How to list files output by incremental recompilation


I am writing a plugin for SBT that requires a list of the class files generated by the last run of the Scala compiler.

This list of class files is then passed into a program that performs some bytecode transformations. Since this transformation process can be slow, I only want the class files written by the last run of the Scala compiler (i.e. those that there modified), not all class files in the output directory.

How can I obtain a list of the files last generated by the compile task?


Solution

  • I think you cannot get this information directly from the Analysis object returned by the compile task.

    However, what you could do is to check analysis.relations.allProducts for changes. If any of the files is modified you can execute yours task, which performs bytecode transformations.

    You could use a modified version of a FileFunction.cached, to check for changes.

    def cached(cacheBaseDirectory: File, inStyle: FilesInfo.Style)(action: Set[File] => Unit): Set[File] => Unit = {
      import Path._
      lazy val inCache = Difference.inputs(cacheBaseDirectory / "in-cache", inStyle)
      inputs => {
        inCache(inputs) { inReport =>
          if(!inReport.modified.isEmpty) action(inReport.modified)
        }   
      }     
    }
    

    The function takes following parameters:

    The function returns another function which is run only if the set of files passed to it as an argument is modified.

    Example

    val transformBytecode = taskKey[Unit]("Transforms bytecode of modified files")
    
    def cached(cacheBaseDirectory: File, inStyle: FilesInfo.Style)(action: Set[File] => Unit): Set[File] => Unit = {
      import Path._
      lazy val inCache = Difference.inputs(cacheBaseDirectory / "in-cache", inStyle)
      inputs => {
        inCache(inputs) { inReport =>
          if(!inReport.modified.isEmpty) action(inReport.modified)
        }   
      }     
    }
    
    transformBytecode <<= Def.task {
      val analysis = (compile in Compile).value
      val cachedFunction = cached(streams.value.cacheDirectory / "transform-bytecode", FilesInfo.lastModified) { modified =>
        // here you want to run the bytecode transformations on `modified` files
        println(s"Modified files $modified")
      }
      cachedFunction(analysis.relations.allProducts.toSet)
    }.triggeredBy(compile in Compile)