Fix windows reload loop

On windows* it was possible to get into a loop where the build would
continually restart because for some reason the build.sbt file would get
touched during test (I did not see this behavior on osx). Thankfully,
the repository keeps track of the file hash and when we detect that the
build file has been updated, we check the file hash to see if it
actually changed.

Note that had this bug shipped, it would have been fixable by overriding
the watchOnEvent task in user builds.

The loop would occur if I ran ~filesJVM/test in
https://github.com/swoval/swoval. It would not occur if I ran
test:compile, so the fact that the build file is being touched seems
to be related to the test run itself.
This commit is contained in:
Ethan Atkins 2018-10-10 18:48:42 -07:00
parent c9a0698a18
commit f579b89577
2 changed files with 15 additions and 9 deletions

View File

@ -23,7 +23,7 @@ import sbt.internal.io.{ EventMonitor, Source, WatchState }
import sbt.internal.util.Types.const
import sbt.internal.util.complete.DefaultParsers
import sbt.internal.util.{ AttributeKey, JLine }
import sbt.io.FileEventMonitor.Event
import sbt.io.FileEventMonitor.{ Creation, Deletion, Event, Update }
import sbt.io._
import sbt.util.{ Level, Logger }
import xsbti.compile.analysis.Stamp
@ -144,6 +144,18 @@ object Watched {
}
scanInput()
}
private[sbt] def onEvent(
sources: Seq[WatchSource],
projectSources: Seq[WatchSource]
): Event[StampedFile] => Watched.Action =
event =>
if (sources.exists(_.accept(event.entry.typedPath.getPath))) Watched.Trigger
else if (projectSources.exists(_.accept(event.entry.typedPath.getPath))) event match {
case Update(prev, cur, _) if prev.value.map(_.stamp) != cur.value.map(_.stamp) => Reload
case _: Creation[_] | _: Deletion[_] => Reload
case _ => Ignore
} else Ignore
private[this] val reRun = if (isWin) "" else " or 'r' to re-run the command"
private def waitMessage(project: String): String =
s"Waiting for source changes$project... (press enter to interrupt$reRun)"

View File

@ -621,14 +621,8 @@ object Defaults extends BuildCommon {
consoleProject := consoleProjectTask.value,
watchTransitiveSources := watchTransitiveSourcesTask.value,
watchProjectTransitiveSources := watchTransitiveSourcesTaskImpl(watchProjectSources).value,
watchOnEvent := {
val sources = watchTransitiveSources.value
val projectSources = watchProjectTransitiveSources.value
e =>
if (sources.exists(_.accept(e.entry.typedPath.getPath))) Watched.Trigger
else if (projectSources.exists(_.accept(e.entry.typedPath.getPath))) Watched.Reload
else Watched.Ignore
},
watchOnEvent := Watched
.onEvent(watchTransitiveSources.value, watchProjectTransitiveSources.value),
watchHandleInput := Watched.handleInput,
watchPreWatch := { (_, _) =>
Watched.Ignore