Use sbt source file stamps in zinc

This fixes the nio/external-hooks test and also restores the performance
of the benchmarks for the latest sbt version in
https://github.com/eatkins/scala-build-watch-performance which had
regressed when the custom ExternalHooks were disabled in
7c4b01d9f7.

The main change is that it changes the ReadStamps object that is passed
into the compiler options to one that uses the unmanagedFileStampCache
and managedFileStampCache for source files and falls back to the default
stamper otherwise. This improves the performance quite significantly
since we only hash the files once. It also makes it so that the analysis
file will contain the source file stamps of the files when compilation
began, rather than when compilation ended. That is what
nio/external-hooks was testing. In the real world what could happen was
that one modified a source file during compilation but then no
incremental re-compilation would occur because after the initial
compilation completed, zinc wrote the stamp of the modified source file
in the analysis file even though it may have actually compiled a
different version of the source file.
This commit is contained in:
Ethan Atkins 2020-04-30 18:22:15 -07:00
parent b678d2115f
commit 329d989e73
3 changed files with 37 additions and 4 deletions

View File

@ -214,8 +214,41 @@ object Defaults extends BuildCommon {
VirtualTerminal.handler,
) ++ serverHandlers.value :+ ServerHandler.fallback
},
uncachedStamper := Stamps.uncachedStamps(fileConverter.value),
reusableStamper := Stamps.timeWrapLibraryStamps(uncachedStamper.value, fileConverter.value),
timeWrappedStamper := Stamps
.timeWrapLibraryStamps(Stamps.uncachedStamps(fileConverter.value), fileConverter.value),
reusableStamper := {
val converter = fileConverter.value
val unmanagedCache = unmanagedFileStampCache.value
val managedCache = managedFileStampCache.value
val backing = timeWrappedStamper.value
new xsbti.compile.analysis.ReadStamps {
def getAllLibraryStamps()
: java.util.Map[xsbti.VirtualFileRef, xsbti.compile.analysis.Stamp] =
backing.getAllLibraryStamps()
def getAllProductStamps()
: java.util.Map[xsbti.VirtualFileRef, xsbti.compile.analysis.Stamp] =
backing.getAllProductStamps()
def getAllSourceStamps()
: java.util.Map[xsbti.VirtualFileRef, xsbti.compile.analysis.Stamp] =
new java.util.HashMap[xsbti.VirtualFileRef, xsbti.compile.analysis.Stamp]
def library(fr: xsbti.VirtualFileRef): xsbti.compile.analysis.Stamp = {
val path = converter.toPath(fr)
managedCache
.getOrElseUpdate(path, sbt.nio.FileStamper.Hash)
.map(_.stamp)
.getOrElse(backing.library(fr))
}
def product(fr: xsbti.VirtualFileRef): xsbti.compile.analysis.Stamp = backing.product(fr)
def source(fr: xsbti.VirtualFile): xsbti.compile.analysis.Stamp = {
val path = converter.toPath(fr)
unmanagedCache
.get(path)
.orElse(managedCache.getOrElseUpdate(path, sbt.nio.FileStamper.Hash))
.map(_.stamp)
.getOrElse(backing.source(fr))
}
}
},
traceLevel in run :== 0,
traceLevel in runMain :== 0,
traceLevel in bgRun :== 0,

View File

@ -227,8 +227,8 @@ object Keys {
val fileConverter = settingKey[FileConverter]("The file converter used to convert between Path and VirtualFile")
val allowMachinePath = settingKey[Boolean]("Allow machine-specific paths during conversion.")
val rootPaths = settingKey[Map[String, NioPath]]("The root paths used to abstract machine-specific paths.")
private[sbt] val uncachedStamper = settingKey[ReadStamps]("The stamper to create timestamp or hash.")
private[sbt] val reusableStamper = settingKey[ReadStamps]("The stamper can be reused across subprojects and sessions.")
private[sbt] val timeWrappedStamper = settingKey[ReadStamps]("The stamper to create timestamp or hash.")
private[sbt] val reusableStamper = taskKey[ReadStamps]("The stamper can be reused across subprojects and sessions.")
// package keys
val packageBin = taskKey[File]("Produces a main artifact, such as a binary jar.").withRank(ATask)