diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index e8784039a..dc78c4984 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -153,6 +153,7 @@ object Defaults extends BuildCommon { inputFileStamper :== sbt.nio.FileStamper.Hash, outputFileStamper :== sbt.nio.FileStamper.LastModified, watchForceTriggerOnAnyChange :== true, + watchPersistFileStamps :== true, watchTriggers :== Nil, clean := { () }, sbt.nio.Keys.fileStampCache := { diff --git a/main/src/main/scala/sbt/internal/Continuous.scala b/main/src/main/scala/sbt/internal/Continuous.scala index 5aa62184a..c2b5657c4 100644 --- a/main/src/main/scala/sbt/internal/Continuous.scala +++ b/main/src/main/scala/sbt/internal/Continuous.scala @@ -291,20 +291,22 @@ private[sbt] object Continuous extends DeprecatedContinuous { } else { FileTreeRepository.default } - val attributeMap = new FileStamp.Cache - repo.addObserver(t => attributeMap.invalidate(t.path)) + val fileStampCache = new FileStamp.Cache + repo.addObserver(t => fileStampCache.invalidate(t.path)) try { - val stateWithRepo = state - .put(globalFileTreeRepository, repo) - .put(persistentFileStampCache, attributeMap) - setup(stateWithRepo, command) { (commands, s, valid, invalid) => + val stateWithRepo = state.put(globalFileTreeRepository, repo) + val fullState = + if (extracted.get(watchPersistFileStamps)) + stateWithRepo.put(persistentFileStampCache, fileStampCache) + else stateWithRepo + setup(fullState, command) { (commands, s, valid, invalid) => EvaluateTask.withStreams(extracted.structure, s)(_.use(streams in Global) { streams => implicit val logger: Logger = streams.log if (invalid.isEmpty) { val currentCount = new AtomicInteger(count) val configs = getAllConfigs(valid.map(v => v._1 -> v._2)) val callbacks = - aggregate(configs, logger, in, s, currentCount, isCommand, commands, attributeMap) + aggregate(configs, logger, in, s, currentCount, isCommand, commands, fileStampCache) val task = () => { currentCount.getAndIncrement() // abort as soon as one of the tasks fails @@ -401,7 +403,7 @@ private[sbt] object Continuous extends DeprecatedContinuous { count: AtomicInteger, isCommand: Boolean, commands: Seq[String], - attributeMap: FileStamp.Cache + fileStampCache: FileStamp.Cache )( implicit extracted: Extracted ): Callbacks = { @@ -411,7 +413,7 @@ private[sbt] object Continuous extends DeprecatedContinuous { val onStart: () => Watch.Action = getOnStart(project, commands, configs, rawLogger, count) val nextInputEvent: () => Watch.Action = parseInputEvents(configs, state, inputStream, logger) val (nextFileEvent, cleanupFileMonitor): (() => Option[(Watch.Event, Watch.Action)], () => Unit) = - getFileEvents(configs, rawLogger, state, count, commands, attributeMap) + getFileEvents(configs, rawLogger, state, count, commands, fileStampCache) val nextEvent: () => Watch.Action = combineInputAndFileEvents(nextInputEvent, nextFileEvent, logger) val onExit = () => { @@ -476,7 +478,7 @@ private[sbt] object Continuous extends DeprecatedContinuous { state: State, count: AtomicInteger, commands: Seq[String], - attributeMap: FileStamp.Cache + fileStampCache: FileStamp.Cache )(implicit extracted: Extracted): (() => Option[(Watch.Event, Watch.Action)], () => Unit) = { val trackMetaBuild = configs.forall(_.watchSettings.trackMetaBuild) val buildGlobs = @@ -491,7 +493,7 @@ private[sbt] object Continuous extends DeprecatedContinuous { def watchEvent(stamper: FileStamper, forceTrigger: Boolean): Option[Watch.Event] = { if (!event.exists) { Some(Deletion(event)) - attributeMap.remove(event.path) match { + fileStampCache.remove(event.path) match { case null => None case _ => Some(Deletion(event)) } diff --git a/main/src/main/scala/sbt/nio/Keys.scala b/main/src/main/scala/sbt/nio/Keys.scala index d7577d53c..abc993d3c 100644 --- a/main/src/main/scala/sbt/nio/Keys.scala +++ b/main/src/main/scala/sbt/nio/Keys.scala @@ -85,6 +85,9 @@ object Keys { val watchOnTermination = settingKey[(Watch.Action, String, Int, State) => State]( "Transforms the state upon completion of a watch. The String argument is the command that was run during the watch. The Int parameter specifies how many times the command was run during the watch." ).withRank(DSetting) + val watchPersistFileStamps = settingKey[Boolean]( + "Toggles whether or not the continuous build will reuse the file stamps computed in previous runs. Setting this to true decrease watch startup latency but could cause inconsistent results if many source files are concurrently modified." + ).withRank(DSetting) val watchStartMessage = settingKey[(Int, String, Seq[String]) => Option[String]]( "The message to show when triggered execution waits for sources to change. The parameters are the current watch iteration count, the current project name and the tasks that are being run with each build." ).withRank(DSetting)