Add shutdown command

Shutdown was being handled as a special case in CommandExchange. This
promotes it to a full fledged command. Also replace instance of
hard-coded strings with constants.
This commit is contained in:
Ethan Atkins 2020-06-27 11:27:57 -07:00
parent 261084bbb2
commit 77b1e38e41
11 changed files with 45 additions and 33 deletions

View File

@ -14,8 +14,10 @@ object BasicCommandStrings {
val HelpCommand: String = "help"
val CompletionsCommand: String = "completions"
val Exit: String = "exit"
val Shutdown: String = "shutdown"
val Quit: String = "quit"
val TemplateCommand: String = "new"
val Cancel: String = "cancel"
/** The command name to terminate the program.*/
val TerminateAction: String = Exit
@ -57,7 +59,8 @@ $HelpCommand <regular expression>
def historyHelp =
Help(Nil, (HistoryHelpBrief +: HistoryCommands.descriptions).toMap, Set(HistoryCommands.Start))
def exitBrief: String = "Terminates the build."
def exitBrief: String = "Terminates the remote client or the build when called from the console."
def shutdownBrief: String = "Terminates the build."
def logLevelHelp: Help = {
val levels = Level.values.toSeq

View File

@ -56,6 +56,7 @@ object BasicCommands {
call,
early,
exit,
shutdown,
history,
oldshell,
client,
@ -356,6 +357,13 @@ object BasicCommands {
case _ => s exit true
}
}
def shutdown: Command = Command.command(Shutdown, shutdownBrief, shutdownBrief) { s =>
s.source match {
case Some(c) if c.channelName.startsWith("network") =>
s"${DisconnectNetworkChannel} ${c.channelName}" :: (Exec(Shutdown, None) +: s)
case _ => s exit true
}
}
@deprecated("Replaced by BuiltInCommands.continuous", "1.3.0")
def continuous: Command =
@ -412,7 +420,7 @@ object BasicCommands {
case xs => xs map (_.commandLine)
})
NetworkClient.run(s0.configuration, arguments)
"exit" :: s0.copy(remainingCommands = Nil)
TerminateAction :: s0.copy(remainingCommands = Nil)
}
def read: Command =

View File

@ -18,6 +18,7 @@ import java.util.UUID
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
import java.util.concurrent.{ ConcurrentHashMap, LinkedBlockingQueue, TimeUnit }
import sbt.BasicCommandStrings.{ Shutdown, TerminateAction }
import sbt.internal.client.NetworkClient.Arguments
import sbt.internal.langserver.{ LogMessageParams, MessageType, PublishDiagnosticsParams }
import sbt.internal.protocol._
@ -155,13 +156,13 @@ class NetworkClient(
val conn = new ServerConnection(sk) {
override def onNotification(msg: JsonRpcNotificationMessage): Unit = {
msg.method match {
case "shutdown" =>
case `Shutdown` =>
val log = msg.params match {
case Some(jvalue) => Converter.fromJson[Boolean](jvalue).getOrElse(true)
case _ => false
}
if (running.compareAndSet(true, false) && log) {
if (!arguments.commandArguments.contains("shutdown")) {
if (!arguments.commandArguments.contains(Shutdown)) {
if (Terminal.console.getLastLine.fold(true)(_.nonEmpty)) errorStream.println()
console.appendLog(Level.Error, "sbt server disconnected")
exitClean.set(false)
@ -451,7 +452,7 @@ class NetworkClient(
case Success(params) => splitDiagnostics(params); Vector()
case Failure(_) => Vector()
}
case ("shutdown", Some(_)) => Vector.empty
case (`Shutdown`, Some(_)) => Vector.empty
case (msg, _) if msg.startsWith("build/") => Vector.empty
case _ =>
Vector(
@ -557,7 +558,7 @@ class NetworkClient(
withSignalHandler(contHandler, Signals.CONT) {
interactiveThread.set(Thread.currentThread)
val cleaned = arguments.commandArguments
val userCommands = cleaned.takeWhile(_ != "exit")
val userCommands = cleaned.takeWhile(_ != TerminateAction)
val interactive = cleaned.isEmpty
val exit = cleaned.nonEmpty && userCommands.isEmpty
attachUUID.set(sendJson(attach, s"""{"interactive": $interactive}"""))
@ -668,8 +669,8 @@ class NetworkClient(
case _ => queue.take
}
} catch {
case _: InterruptedException if cmd == "shutdown" => result = 0
case _: InterruptedException => result = if (exitClean.get) 0 else 1
case _: InterruptedException if cmd == Shutdown => result = 0
case _: InterruptedException => result = if (exitClean.get) 0 else 1
}
}
if (result == null) 1 else result

View File

@ -12,6 +12,7 @@ import java.nio.channels.ClosedChannelException
import java.util.concurrent.atomic.AtomicBoolean
import jline.console.history.PersistentHistory
import sbt.BasicCommandStrings.{ Cancel, TerminateAction, Shutdown }
import sbt.BasicKeys.{ historyPath, terminalShellPrompt }
import sbt.State
import sbt.internal.CommandChannel
@ -54,7 +55,7 @@ private[sbt] object UITask {
try {
@tailrec def impl(): Either[String, String] = {
lineReader.readLine(clear + terminal.prompt.mkPrompt()) match {
case null => Left("exit")
case null => Left(TerminateAction)
case s: String =>
lineReader.getHistory match {
case p: PersistentHistory =>
@ -63,8 +64,8 @@ private[sbt] object UITask {
case _ =>
}
s match {
case "" => impl()
case cmd @ ("shutdown" | "exit" | "cancel") => Left(cmd)
case "" => impl()
case cmd @ (`Shutdown` | `TerminateAction` | `Cancel`) => Left(cmd)
case cmd =>
if (terminal.prompt != Prompt.Batch) terminal.setPrompt(Prompt.Running)
terminal.printStream.write(Int.MinValue)

View File

@ -15,7 +15,7 @@ import java.util.Properties
import java.util.concurrent.ForkJoinPool
import java.util.concurrent.atomic.AtomicBoolean
import sbt.BasicCommandStrings.{ SetTerminal, Shell, TemplateCommand, networkExecPrefix }
import sbt.BasicCommandStrings.{ SetTerminal, Shell, Shutdown, TemplateCommand, networkExecPrefix }
import sbt.Project.LoadAction
import sbt.compiler.EvalImports
import sbt.internal.Aggregation.AnyKeys
@ -1025,7 +1025,7 @@ object BuiltinCommands {
* happen primarily on windows.
*/
if (Terminal.startedByRemoteClient && !exchange.hasServer) {
Exec("shutdown", None) +: s1
Exec(Shutdown, None) +: s1
} else {
exchange prompt ConsolePromptEvent(s0)
val minGCInterval = Project

View File

@ -11,6 +11,7 @@ import java.lang.reflect.InvocationTargetException
import java.nio.file.Path
import java.io.File
import sbt.BasicCommandStrings.TerminateAction
import sbt.io._, syntax._
import sbt.util._
import sbt.internal.util.complete.{ DefaultParsers, Parser }, DefaultParsers._
@ -54,7 +55,7 @@ private[sbt] object TemplateCommandUtil {
case xs => xs map (_.commandLine)
})
run(infos, arguments, state.configuration, ivyConf, globalBase, scalaModuleInfo, log)
"exit" :: s2.copy(remainingCommands = Nil)
TerminateAction :: s2.copy(remainingCommands = Nil)
}
private def run(

View File

@ -13,7 +13,7 @@ import java.net.Socket
import java.util.concurrent.atomic._
import java.util.concurrent.{ LinkedBlockingQueue, TimeUnit }
import sbt.BasicCommandStrings.networkExecPrefix
import sbt.BasicCommandStrings.{ Cancel, Shutdown, TerminateAction, networkExecPrefix }
import sbt.BasicKeys._
import sbt.internal.protocol.JsonRpcResponseError
import sbt.internal.server._
@ -96,7 +96,7 @@ private[sbt] final class CommandExchange {
case _ =>
}
}
Exec("exit", Some(CommandSource(ConsoleChannel.defaultName)))
Exec(TerminateAction, Some(CommandSource(ConsoleChannel.defaultName)))
case x => x
}
case _ => commandQueue.take
@ -105,11 +105,8 @@ private[sbt] final class CommandExchange {
poll match {
case Some(exec) if exec.source.fold(true)(s => channels.exists(_.name == s.channelName)) =>
exec.commandLine match {
case "shutdown" =>
exec
.withCommandLine("exit")
.withSource(Some(CommandSource(ConsoleChannel.defaultName)))
case "exit" if exec.source.fold(false)(_.channelName.startsWith("network")) =>
case `TerminateAction`
if exec.source.fold(false)(_.channelName.startsWith("network")) =>
channels.collectFirst {
case c: NetworkChannel if exec.source.fold(false)(_.channelName == c.name) => c
} match {
@ -383,8 +380,7 @@ private[sbt] final class CommandExchange {
private[sbt] def shutdown(name: String): Unit = {
Option(currentExecRef.get).foreach(cancel)
commandQueue.clear()
val exit =
Exec("shutdown", Some(Exec.newExecId), Some(CommandSource(name)))
val exit = Exec(Shutdown, Some(Exec.newExecId), Some(CommandSource(name)))
commandQueue.add(exit)
()
}
@ -415,7 +411,7 @@ private[sbt] final class CommandExchange {
case mt: FastTrackTask =>
mt.task match {
case `attach` => mt.channel.prompt(ConsolePromptEvent(lastState.get))
case "cancel" => Option(currentExecRef.get).foreach(cancel)
case `Cancel` => Option(currentExecRef.get).foreach(cancel)
case t if t.startsWith(ContinuousCommands.stopWatch) =>
ContinuousCommands.stopWatchImpl(mt.channel.name)
mt.channel match {
@ -423,8 +419,8 @@ private[sbt] final class CommandExchange {
case _ => mt.channel.prompt(ConsolePromptEvent(lastState.get))
}
commandQueue.add(Exec(t, None, None))
case "exit" => exit(mt)
case "shutdown" =>
case `TerminateAction` => exit(mt)
case `Shutdown` =>
channels.find(_.name == mt.channel.name) match {
case Some(c: NetworkChannel) => c.shutdown(false)
case _ =>

View File

@ -10,7 +10,7 @@ package internal.nio
import java.nio.file.Path
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
import sbt.BasicCommandStrings.{ RebootCommand, TerminateAction }
import sbt.BasicCommandStrings.{ RebootCommand, Shutdown, TerminateAction }
import sbt.Keys.{ baseDirectory, pollInterval, state }
import sbt.Scope.Global
import sbt.SlashSyntax0._
@ -90,7 +90,7 @@ private[sbt] class CheckBuildSources extends AutoCloseable {
val commands =
allCmds.flatMap(_.split(";").flatMap(_.trim.split(" ").headOption).filterNot(_.isEmpty))
val filter = (c: String) =>
c == LoadProject || c == RebootCommand || c == TerminateAction || c == "shutdown"
c == LoadProject || c == RebootCommand || c == TerminateAction || c == Shutdown
val res = !commands.exists(filter)
if (!res) {
previousStamps.set(getStamps(force = true))

View File

@ -11,6 +11,7 @@ package server
import java.net.URI
import sbt.BasicCommandStrings.Shutdown
import sbt.BuildSyntax._
import sbt.Def._
import sbt.Keys._
@ -153,7 +154,7 @@ object BuildServerProtocol {
()
case r: JsonRpcRequestMessage if r.method == "build/exit" =>
val _ = callback.appendExec("shutdown", Some(r.id))
val _ = callback.appendExec(Shutdown, Some(r.id))
case r: JsonRpcRequestMessage if r.method == "buildTarget/sources" =>
val param = Converter.fromJson[SourcesParams](json(r)).get

View File

@ -15,6 +15,7 @@ import java.nio.channels.ClosedChannelException
import java.util.concurrent.{ ConcurrentHashMap, LinkedBlockingQueue }
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
import sbt.BasicCommandStrings.{ Shutdown, TerminateAction }
import sbt.internal.langserver.{ CancelRequestParams, ErrorCodes, LogMessageParams, MessageType }
import sbt.internal.langserver.{ CancelRequestParams, ErrorCodes }
import sbt.internal.protocol.{
@ -150,7 +151,7 @@ final class NetworkChannel(
override def reader: UITask.Reader = () => {
try {
this.synchronized(this.wait)
Left("exit")
Left(TerminateAction)
} catch {
case _: InterruptedException => Right("")
}
@ -564,7 +565,7 @@ final class NetworkChannel(
super.shutdown(logShutdown)
if (logShutdown) Terminal.consoleLog(s"shutting down client connection $name")
VirtualTerminal.cancelRequests(name)
try jsonRpcNotify("shutdown", logShutdown)
try jsonRpcNotify(Shutdown, logShutdown)
catch { case _: IOException => }
running.set(false)
out.close()

View File

@ -13,7 +13,7 @@ import java.time.{ Instant, ZoneId, ZonedDateTime }
import java.util.Locale
import java.util.concurrent.TimeUnit
import sbt.BasicCommandStrings.ContinuousExecutePrefix
import sbt.BasicCommandStrings.{ ContinuousExecutePrefix, TerminateAction }
import sbt._
import sbt.internal.LabeledFunctions._
import sbt.internal.nio.FileEvent
@ -487,7 +487,7 @@ object Watch {
Watch.InputOption(4.toChar, "<ctrl-d>", "interrupt (exits sbt in batch mode)", CancelWatch),
Watch.InputOption('r', "re-run the command", Trigger),
Watch.InputOption('s', "return to shell", Prompt),
Watch.InputOption('q', "quit sbt", Run("exit")),
Watch.InputOption('q', "quit sbt", Run(TerminateAction)),
Watch.InputOption('?', "print options", ShowOptions)
)