mirror of https://github.com/sbt/sbt.git
Store compile file stamps for each scala version
https://github.com/sbt/sbt/issues/4986 reported that +compile would
always recompile everything in the project even when the sources hadn't
changed. This was because the dependency classpath was changing between
calls to compile, which caused the external hooks cache introduced in
32a6d0d5d7 to invalidate the scala
library. To fix this, I cache the file stamps on a per scala version
basis. I added a scripted test that checks that there is no
recompilation in two consecutive calls to `+compile` in a multi scala
version build. It failed prior to these changes.
This commit is contained in:
parent
49afe01287
commit
bd4d04d131
|
|
@ -47,6 +47,7 @@ import sbt.internal.server.{
|
|||
LanguageServerReporter,
|
||||
ServerHandler
|
||||
}
|
||||
import sbt.nio.FileStamp.Formats.seqPathFileStampJsonFormatter
|
||||
import sbt.internal.testing.TestLogger
|
||||
import sbt.internal.util.Attributed.data
|
||||
import sbt.internal.util.Types._
|
||||
|
|
@ -617,31 +618,42 @@ object Defaults extends BuildCommon {
|
|||
s"inc_compile$extra.zip"
|
||||
},
|
||||
externalHooks := {
|
||||
import sbt.nio.FileStamp.Formats.seqPathFileStampJsonFormatter
|
||||
import sjsonnew.BasicJsonProtocol.mapFormat
|
||||
val currentInputs =
|
||||
(unmanagedSources / inputFileStamps).value ++ (managedSourcePaths / outputFileStamps).value
|
||||
val previousInputs = (externalHooks / inputFileStamps).previous
|
||||
val sv = scalaVersion.value
|
||||
val previousInputs = compileSourceFileInputs.previous.flatMap(_.get(sv))
|
||||
val inputChanges = previousInputs
|
||||
.map(sbt.nio.Settings.changedFiles(_, currentInputs))
|
||||
.getOrElse(FileChanges.noPrevious(currentInputs.map(_._1)))
|
||||
val currentOutputs = (dependencyClasspathFiles / outputFileStamps).value
|
||||
val previousOutputs = (externalHooks / outputFileStamps).previous
|
||||
val previousOutputs = compileBinaryFileInputs.previous.flatMap(_.get(sv))
|
||||
val outputChanges = previousOutputs
|
||||
.map(sbt.nio.Settings.changedFiles(_, currentOutputs))
|
||||
.getOrElse(FileChanges.noPrevious(currentOutputs.map(_._1)))
|
||||
ExternalHooks.default.value(inputChanges, outputChanges, fileTreeView.value)
|
||||
},
|
||||
externalHooks / inputFileStamps := {
|
||||
compileSourceFileInputs := {
|
||||
import sjsonnew.BasicJsonProtocol.mapFormat
|
||||
compile.value // ensures the inputFileStamps previous value is only set if compile succeeds.
|
||||
(unmanagedSources / inputFileStamps).value ++ (managedSourcePaths / outputFileStamps).value
|
||||
val version = scalaVersion.value
|
||||
val versions = crossScalaVersions.value.toSet + version
|
||||
val prev: Map[String, Seq[(java.nio.file.Path, sbt.nio.FileStamp)]] =
|
||||
compileSourceFileInputs.previous.map(_.filterKeys(versions)).getOrElse(Map.empty)
|
||||
prev + (version ->
|
||||
((unmanagedSources / inputFileStamps).value ++ (managedSourcePaths / outputFileStamps).value))
|
||||
},
|
||||
externalHooks / inputFileStamps := (externalHooks / inputFileStamps).triggeredBy(compile).value,
|
||||
externalHooks / outputFileStamps := {
|
||||
compileSourceFileInputs := compileSourceFileInputs.triggeredBy(compile).value,
|
||||
compileBinaryFileInputs := {
|
||||
import sjsonnew.BasicJsonProtocol.mapFormat
|
||||
compile.value // ensures the inputFileStamps previous value is only set if compile succeeds.
|
||||
(dependencyClasspathFiles / outputFileStamps).value
|
||||
val version = scalaVersion.value
|
||||
val versions = crossScalaVersions.value.toSet + version
|
||||
val prev: Map[String, Seq[(java.nio.file.Path, sbt.nio.FileStamp)]] =
|
||||
compileBinaryFileInputs.previous.map(_.filterKeys(versions)).getOrElse(Map.empty)
|
||||
prev + (version -> (dependencyClasspathFiles / outputFileStamps).value)
|
||||
},
|
||||
externalHooks / outputFileStamps :=
|
||||
(externalHooks / outputFileStamps).triggeredBy(compile).value,
|
||||
compileBinaryFileInputs := compileBinaryFileInputs.triggeredBy(compile).value,
|
||||
incOptions := { incOptions.value.withExternalHooks(externalHooks.value) },
|
||||
compileIncSetup := compileIncSetupTask.value,
|
||||
console := consoleTask.value,
|
||||
|
|
|
|||
|
|
@ -168,6 +168,12 @@ object Keys {
|
|||
private[sbt] val classpathFiles =
|
||||
taskKey[Seq[Path]]("The classpath for a task.").withRank(Invisible)
|
||||
private[sbt] val compileOutputs = taskKey[Seq[Path]]("Compilation outputs").withRank(Invisible)
|
||||
private[sbt] val compileSourceFileInputs =
|
||||
taskKey[Map[String, Seq[(Path, FileStamp)]]]("Source file stamps stored by scala version")
|
||||
.withRank(Invisible)
|
||||
private[sbt] val compileBinaryFileInputs =
|
||||
taskKey[Map[String, Seq[(Path, FileStamp)]]]("Source file stamps stored by scala version")
|
||||
.withRank(Invisible)
|
||||
|
||||
private[this] val hasCheckedMetaBuildMsg =
|
||||
"Indicates whether or not we have called the checkBuildSources task. This is to avoid warning " +
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
scalaVersion := "2.12.8"
|
||||
crossScalaVersions := List("2.12.8", "2.13.0")
|
||||
|
||||
val setLastModified = taskKey[Unit]("Sets the last modified time for classfiles")
|
||||
setLastModified := {
|
||||
val versions = crossScalaVersions.value
|
||||
versions.map(_.split('.').take(2).mkString("scala-", ".", "")).foreach { v =>
|
||||
val f = target.value / v / "classes" / "A.class"
|
||||
Stamps.value.put(f, IO.getModifiedTimeOrZero(f))
|
||||
}
|
||||
}
|
||||
|
||||
val checkLastModified = taskKey[Unit]("Checks the last modified time for classfiles")
|
||||
checkLastModified := {
|
||||
val versions = crossScalaVersions.value
|
||||
versions.map(_.split('.').take(2).mkString("scala-", ".", "")).foreach { v =>
|
||||
val classFile = target.value / v / "classes" / "A.class"
|
||||
val actual = IO.getModifiedTimeOrZero(classFile)
|
||||
val previous = Stamps.value.get(classFile)
|
||||
assert(actual == previous, s"$actual did not equal $previous for $classFile")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
object Stamps {
|
||||
val value = new java.util.HashMap[java.io.File, Long]
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
object A {
|
||||
def a = 1
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
> +compile
|
||||
|
||||
> setLastModified
|
||||
|
||||
> +compile
|
||||
|
||||
> checkLastModified
|
||||
|
|
@ -184,6 +184,7 @@ final class ScriptedTests(
|
|||
val (group, name) = testName
|
||||
s"$group/$name" match {
|
||||
case "actions/add-alias" => LauncherBased // sbt/Package$
|
||||
case "actions/cross-incremental" => LauncherBased // tbd
|
||||
case "actions/cross-multiproject" => LauncherBased // tbd
|
||||
case "actions/cross-multi-parser" =>
|
||||
LauncherBased // java.lang.ClassNotFoundException: javax.tools.DiagnosticListener when run with java 11 and an old sbt launcher
|
||||
|
|
|
|||
Loading…
Reference in New Issue