mirror of https://github.com/sbt/sbt.git
Restore watchOnTermination
At some point the watchOnTermination callback stopped working. I'm not exactly sure how or why that happened but it is fairly straightforward to restore. The one tricky thing was that the callback has the signature (Watch.Action, _, _, _) => State, which requires propagating the action to the failWatch command. The easiest way to do this was to add a mutable field to the ContinuousState. This is rather ugly and reflects some poor design choices but a more comprehensive refactor is out of the scope of this fix. This commit adds a scripted test that ensures that the callback is invoked both in the successful and unsuccessful watch cases. In each case the callback deletes a file and we ensure that the file is indeed absent after the watch exits.
This commit is contained in:
parent
64c7071ff2
commit
ca7c872e27
|
|
@ -1128,7 +1128,28 @@ private[sbt] object Continuous extends DeprecatedContinuous {
|
|||
val callbacks: Callbacks,
|
||||
val dynamicInputs: mutable.Set[DynamicInput],
|
||||
val pending: Boolean,
|
||||
var failAction: Option[Watch.Action],
|
||||
) {
|
||||
def this(
|
||||
count: Int,
|
||||
commands: Seq[String],
|
||||
beforeCommandImpl: (State, mutable.Set[DynamicInput]) => State,
|
||||
afterCommand: State => State,
|
||||
afterWatch: State => State,
|
||||
callbacks: Callbacks,
|
||||
dynamicInputs: mutable.Set[DynamicInput],
|
||||
pending: Boolean,
|
||||
) = this(
|
||||
count,
|
||||
commands,
|
||||
beforeCommandImpl,
|
||||
afterCommand,
|
||||
afterWatch,
|
||||
callbacks,
|
||||
dynamicInputs,
|
||||
pending,
|
||||
None
|
||||
)
|
||||
def beforeCommand(state: State): State = beforeCommandImpl(state, dynamicInputs)
|
||||
def incremented: ContinuousState = withCount(count + 1)
|
||||
def withPending(p: Boolean) =
|
||||
|
|
@ -1323,7 +1344,8 @@ private[sbt] object ContinuousCommands {
|
|||
case Watch.Prompt => stop.map(_ :: s"$PromptChannel ${channel.name}" :: Nil mkString ";")
|
||||
case Watch.Run(commands) =>
|
||||
stop.map(_ +: commands.map(_.commandLine).filter(_.nonEmpty) mkString "; ")
|
||||
case Watch.HandleError(_) =>
|
||||
case a @ Watch.HandleError(_) =>
|
||||
cs.failAction = Some(a)
|
||||
stop.map(_ :: s"$failWatch ${channel.name}" :: Nil mkString "; ")
|
||||
case _ => stop
|
||||
}
|
||||
|
|
@ -1353,27 +1375,31 @@ private[sbt] object ContinuousCommands {
|
|||
}
|
||||
cs.afterCommand(postState)
|
||||
}
|
||||
private[sbt] val stopWatchCommand = watchCommand(stopWatch) { (channel, state) =>
|
||||
state.get(watchStates).flatMap(_.get(channel)) match {
|
||||
case Some(cs) =>
|
||||
val afterWatchState = cs.afterWatch(state)
|
||||
cs.callbacks.onExit()
|
||||
StandardMain.exchange
|
||||
.channelForName(channel)
|
||||
.foreach { c =>
|
||||
c.terminal.setPrompt(Prompt.Pending)
|
||||
c.unprompt(ConsoleUnpromptEvent(Some(CommandSource(channel))))
|
||||
private[this] val exitWatchShared = (error: Boolean) =>
|
||||
(channel: String, state: State) =>
|
||||
state.get(watchStates).flatMap(_.get(channel)) match {
|
||||
case Some(cs) =>
|
||||
val afterWatchState = cs.afterWatch(state)
|
||||
cs.callbacks.onExit()
|
||||
StandardMain.exchange
|
||||
.channelForName(channel)
|
||||
.foreach { c =>
|
||||
c.terminal.setPrompt(Prompt.Pending)
|
||||
c.unprompt(ConsoleUnpromptEvent(Some(CommandSource(channel))))
|
||||
}
|
||||
val newState = afterWatchState.get(watchStates) match {
|
||||
case None => afterWatchState
|
||||
case Some(w) => afterWatchState.put(watchStates, w - channel)
|
||||
}
|
||||
afterWatchState.get(watchStates) match {
|
||||
case None => afterWatchState
|
||||
case Some(w) => afterWatchState.put(watchStates, w - channel)
|
||||
}
|
||||
case _ => state
|
||||
}
|
||||
}
|
||||
private[sbt] val failWatchCommand = watchCommand(failWatch) { (channel, state) =>
|
||||
state.fail
|
||||
}
|
||||
val commands = cs.commands.mkString("; ")
|
||||
val count = cs.count
|
||||
val action = cs.failAction.getOrElse(Watch.CancelWatch)
|
||||
val st = cs.callbacks.onTermination(action, commands, count, newState)
|
||||
if (error) st.fail else st
|
||||
case _ => if (error) state.fail else state
|
||||
}
|
||||
private[sbt] val stopWatchCommand = watchCommand(stopWatch)(exitWatchShared(false))
|
||||
private[sbt] val failWatchCommand = watchCommand(failWatch)(exitWatchShared(true))
|
||||
/*
|
||||
* Creates a FileTreeRepository where it is safe to call close without inadvertently cancelling
|
||||
* still active watches.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
watchOnIteration := { (count, project, commands) =>
|
||||
Watch.CancelWatch
|
||||
}
|
||||
watchOnTermination := { (action, count, command, state) =>
|
||||
action match {
|
||||
case Watch.CancelWatch =>
|
||||
java.nio.file.Files.delete(java.nio.file.Paths.get("foo.txt"))
|
||||
case Watch.HandleError(e) =>
|
||||
if (e.getMessage == "fail")
|
||||
java.nio.file.Files.delete(java.nio.file.Paths.get("bar.txt"))
|
||||
else
|
||||
throw new IllegalStateException("unexpected error")
|
||||
}
|
||||
state
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
$ exists foo.txt
|
||||
> ~compile
|
||||
$ absent foo.txt
|
||||
> set watchOnIteration := { (_, _, _) => new Watch.HandleError(new IllegalStateException("fail")) }
|
||||
$ exists bar.txt
|
||||
-> ~compile
|
||||
$ absent bar.txt
|
||||
|
||||
Loading…
Reference in New Issue