diff --git a/main/src/main/scala/sbt/internal/Continuous.scala b/main/src/main/scala/sbt/internal/Continuous.scala index 4032b061c..89e5fe00e 100644 --- a/main/src/main/scala/sbt/internal/Continuous.scala +++ b/main/src/main/scala/sbt/internal/Continuous.scala @@ -8,7 +8,8 @@ package sbt package internal -import java.io.{ ByteArrayInputStream, InputStream, File => _ } +import java.io.{ ByteArrayInputStream, IOException, InputStream, File => _ } +import java.nio.file.Path import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger } import sbt.BasicCommandStrings.{ @@ -28,7 +29,7 @@ import sbt.internal.util.complete.{ Parser, Parsers } import sbt.internal.util.{ AttributeKey, JLine, Util } import sbt.nio.Keys.{ fileInputs, _ } import sbt.nio.Watch.{ Creation, Deletion, ShowOptions, Update } -import sbt.nio.file.FileAttributes +import sbt.nio.file.{ FileAttributes, Glob } import sbt.nio.{ FileStamp, FileStamper, Watch } import sbt.util.{ Level, _ } @@ -328,12 +329,14 @@ private[sbt] object Continuous extends DeprecatedContinuous { } val fileStampCache = new FileStamp.Cache repo.addObserver(t => fileStampCache.invalidate(t.path)) + val persistFileStamps = extracted.get(watchPersistFileStamps) + val cachingRepo: FileTreeRepository[FileAttributes] = + if (persistFileStamps) repo else new FileStampRepository(fileStampCache, repo) try { - val stateWithRepo = state.put(globalFileTreeRepository, repo) + val stateWithRepo = state.put(globalFileTreeRepository, cachingRepo) val fullState = addLegacyWatchSetting( - if (extracted.get(watchPersistFileStamps)) - stateWithRepo.put(persistentFileStampCache, fileStampCache) + if (persistFileStamps) stateWithRepo.put(persistentFileStampCache, fileStampCache) else stateWithRepo ) setup(fullState, commands) { (s, valid, invalid) => @@ -1091,4 +1094,17 @@ private[sbt] object Continuous extends DeprecatedContinuous { } } + private[sbt] class FileStampRepository( + fileStampCache: FileStamp.Cache, + underlying: FileTreeRepository[FileAttributes] + ) extends FileTreeRepository[FileAttributes] { + def putIfAbsent(path: Path, stamper: FileStamper): (Option[FileStamp], Option[FileStamp]) = + fileStampCache.putIfAbsent(path, stamper) + override def list(path: Path): Seq[(Path, FileAttributes)] = underlying.list(path) + override def addObserver(observer: Observer[FileEvent[FileAttributes]]): AutoCloseable = + underlying.addObserver(observer) + override def register(glob: Glob): Either[IOException, Observable[FileEvent[FileAttributes]]] = + underlying.register(glob) + override def close(): Unit = underlying.close() + } } diff --git a/main/src/main/scala/sbt/nio/FileStamp.scala b/main/src/main/scala/sbt/nio/FileStamp.scala index 0cd081686..fb6a3fcfb 100644 --- a/main/src/main/scala/sbt/nio/FileStamp.scala +++ b/main/src/main/scala/sbt/nio/FileStamp.scala @@ -253,12 +253,12 @@ private[sbt] object FileStamp { case e => e.value } - def putIfAbsent(key: Path, stamper: FileStamper): Unit = { + def putIfAbsent(key: Path, stamper: FileStamper): (Option[FileStamp], Option[FileStamp]) = { underlying.get(key) match { - case null => updateImpl(key, stamper) - case _ => + case null => (None, updateImpl(key, stamper)) + case Right(s) => (Some(s), None) + case Left(_) => (None, None) } - () } def update(key: Path, stamper: FileStamper): (Option[FileStamp], Option[FileStamp]) = { underlying.get(key) match { diff --git a/main/src/main/scala/sbt/nio/Settings.scala b/main/src/main/scala/sbt/nio/Settings.scala index 532ed90ae..8600a50e8 100644 --- a/main/src/main/scala/sbt/nio/Settings.scala +++ b/main/src/main/scala/sbt/nio/Settings.scala @@ -13,6 +13,7 @@ import java.nio.file.{ Files, Path } import sbt.Project._ import sbt.internal.Clean.ToSeqPath +import sbt.internal.Continuous.FileStampRepository import sbt.internal.util.{ AttributeKey, SourcePosition } import sbt.internal.{ Clean, Continuous, DynamicInput, SettingsGraph } import sbt.nio.FileStamp.{ fileStampJsonFormatter, pathJsonFormatter, _ } @@ -319,10 +320,22 @@ private[sbt] object Settings { addTaskDefinition(Keys.inputFileStamps in scopedKey.scope := { val cache = (unmanagedFileStampCache in scopedKey.scope).value val stamper = (Keys.inputFileStamper in scopedKey.scope).value + val stampFile: Path => Option[(Path, FileStamp)] = + sbt.Keys.state.value.get(globalFileTreeRepository) match { + case Some(repo: FileStampRepository) => + (path: Path) => + repo.putIfAbsent(path, stamper) match { + case (None, Some(s)) => + cache.put(path, s) + Some(path -> s) + case _ => cache.getOrElseUpdate(path, stamper).map(path -> _) + } + case _ => + (path: Path) => cache.getOrElseUpdate(path, stamper).map(path -> _) + } (Keys.allInputPathsAndAttributes in scopedKey.scope).value.flatMap { - case (p, a) if a.isRegularFile && !Files.isHidden(p) => - cache.getOrElseUpdate(p, stamper).map(p -> _) - case _ => None + case (path, a) if a.isRegularFile && !Files.isHidden(path) => stampFile(path) + case _ => None } }) private[this] def outputsAndStamps[T: JsonFormat: ToSeqPath]( diff --git a/main/src/main/scala/sbt/nio/Watch.scala b/main/src/main/scala/sbt/nio/Watch.scala index efd7d552a..fcb497b65 100644 --- a/main/src/main/scala/sbt/nio/Watch.scala +++ b/main/src/main/scala/sbt/nio/Watch.scala @@ -576,7 +576,7 @@ object Watch { sbt.Keys.aggregate in watchTasks :== false, watchTriggeredMessage :== Watch.defaultOnTriggerMessage, watchForceTriggerOnAnyChange :== false, - watchPersistFileStamps :== true, + watchPersistFileStamps := (sbt.Keys.turbo in ThisBuild).value, watchTriggers :== Nil, ) } diff --git a/sbt/src/sbt-test/watch/on-change/build.sbt b/sbt/src/sbt-test/watch/on-change/build.sbt index 260e5d1c2..542ca3643 100644 --- a/sbt/src/sbt-test/watch/on-change/build.sbt +++ b/sbt/src/sbt-test/watch/on-change/build.sbt @@ -33,4 +33,4 @@ watchOnFileInputEvent := { (_, event: Watch.Event) => else Watch.Trigger } -watchAntiEntropy := 0.millis \ No newline at end of file +watchAntiEntropy := 0.millis