Skip to content

Latest commit

 

History

History
74 lines (64 loc) · 2.48 KB

2.1 SBT Cache invalidation.md

File metadata and controls

74 lines (64 loc) · 2.48 KB

SBT Cache Invalidation

Issue

In the macro example #2 about the PackageSearch, we saw that cached files calling a macro will not be recompiled if another file only referenced in a macro splice changes.

DISCLAIMER: I'm not good at SBT, there are probably way better ways of doing this, if you know any, please help with a PR to this file

Let's define our keys:

  lazy val clearUncached = taskKey[Unit]("Clear target classes in `uncachedClasses`")
  lazy val uncachedClasses = settingKey[Seq[String]]("Classes that should not be cached during compilation")

Let's define a utility task to help us delete certain compiled files before each compilation:

// These are the extensions that compiler output can have for a source file (jvm and scalajs)
lazy val Appendages = Seq(
  ".class",
  ".sjsir",
  ".tasty",
  "$package.class",
  "$package.sjsir",
  "$package.tasty",
  "$package$.class",
  "$package$.sjsir"
)

// In case we don't want any uncached classes
lazy val defaultUncachedClasses =
  uncachedClasses := Seq()

lazy val ensureUncaching = clearUncached := {
  println("Forcefully invalidating caches...")
  val targetFile = target.value
  val classesFile = targetFile / ("scala-" + scalaVersion.value) / "classes"
  for(className <- uncachedClasses.value) {
    val path = (classesFile / className).getAbsolutePath()
    for(appendage <- Appendages) {
      val file = new File(path + appendage)
      val deleted = file.delete()      
    }
  }
}

// This value is actually the act of setting the compile operation to something new
lazy val uncachingCompile = 
  (compile) := {
    val res = (Compile / compile).value
    clearUncached.value
    res
  }

// Only required for scalajs, you might want to do the same with fullOptJS
lazy val uncachingFastOptJS =
  (fastOptJS) := {
    val res = (Compile / fastOptJS).value
    clearUncached.value
    res
  }

Then in our project we can just use it like:

lazy val myProj = project.in(file(...))
  .settings(
    ensureUncaching, // Set our uncaching method
    uncachingCompile, // Make our compile task do uncaching
    uncachingFastOptJS, // Only for scalajs
    uncachedClasses := Seq("myname/mydomain/myproject/MainClass") // Define all uncached classes (use / instead of . for packages)
    
    // ... your other settings
  )

You should be able to test by running compile repeatedly in the sbt terminal and you will see that it always compiles at least 1 file (or however many you defined in your project's uncachedClasses)