Skip to content

Commit

Permalink
Merge pull request #3991 from dwijnand/ExecStatusEvent-exitCode
Browse files Browse the repository at this point in the history
Add an optional exitCode to ExecStatusEvent so clients can use it
  • Loading branch information
eed3si9n committed Mar 15, 2018
2 parents 3559de0 + 98332c0 commit 4efce7e
Show file tree
Hide file tree
Showing 51 changed files with 226 additions and 74 deletions.
60 changes: 60 additions & 0 deletions build.sbt
Expand Up @@ -203,6 +203,24 @@ lazy val testingProj = (project in file("testing"))
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats,
mimaSettings,
mimaBinaryIssueFilters ++= Seq(
// copy method was never meant to be public
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.EndTestGroupErrorEvent.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.EndTestGroupErrorEvent.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.EndTestGroupEvent.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.EndTestGroupEvent.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.StartTestGroupEvent.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.StartTestGroupEvent.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestCompleteEvent.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestCompleteEvent.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestInitEvent.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestItemDetail.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestItemDetail.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestItemEvent.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestItemEvent.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestStringEvent.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.testing.TestStringEvent.copy$default$1"),
)
)
.configure(addSbtIO, addSbtCompilerClasspath, addSbtUtilLogging)

Expand Down Expand Up @@ -251,6 +269,17 @@ lazy val runProj = (project in file("run"))
baseDirectory.value / "src" / "main" / "contraband-scala",
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
mimaSettings,
mimaBinaryIssueFilters ++= Seq(
// copy method was never meant to be public
exclude[DirectMissingMethodProblem]("sbt.ForkOptions.copy"),
exclude[DirectMissingMethodProblem]("sbt.ForkOptions.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.OutputStrategy#BufferedOutput.copy"),
exclude[DirectMissingMethodProblem]("sbt.OutputStrategy#BufferedOutput.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.OutputStrategy#CustomOutput.copy"),
exclude[DirectMissingMethodProblem]("sbt.OutputStrategy#CustomOutput.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.OutputStrategy#LoggedOutput.copy"),
exclude[DirectMissingMethodProblem]("sbt.OutputStrategy#LoggedOutput.copy$default$*"),
)
)
.configure(addSbtIO, addSbtUtilLogging, addSbtCompilerClasspath)

Expand Down Expand Up @@ -307,6 +336,31 @@ lazy val protocolProj = (project in file("protocol"))
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats,
mimaSettings,
mimaBinaryIssueFilters ++= Seq(
// copy method was never meant to be public
exclude[DirectMissingMethodProblem]("sbt.protocol.ChannelAcceptedEvent.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.ChannelAcceptedEvent.copy$default$1"),
exclude[DirectMissingMethodProblem]("sbt.protocol.ExecCommand.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.ExecCommand.copy$default$1"),
exclude[DirectMissingMethodProblem]("sbt.protocol.ExecCommand.copy$default$2"),
exclude[DirectMissingMethodProblem]("sbt.protocol.ExecStatusEvent.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.ExecStatusEvent.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.protocol.ExecutionEvent.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.ExecutionEvent.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.protocol.InitCommand.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.InitCommand.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.protocol.LogEvent.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.LogEvent.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.protocol.SettingQuery.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.SettingQuery.copy$default$1"),
exclude[DirectMissingMethodProblem]("sbt.protocol.SettingQueryFailure.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.SettingQueryFailure.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.protocol.SettingQuerySuccess.copy"),
exclude[DirectMissingMethodProblem]("sbt.protocol.SettingQuerySuccess.copy$default$*"),

// ignore missing methods in sbt.internal
exclude[DirectMissingMethodProblem]("sbt.internal.*"),
)
)
.configure(addSbtUtilLogging)

Expand Down Expand Up @@ -336,6 +390,12 @@ lazy val commandProj = (project in file("main-command"))
// Replace nailgun socket stuff
exclude[MissingClassProblem]("sbt.internal.NG*"),
exclude[MissingClassProblem]("sbt.internal.ReferenceCountedFileDescriptor"),

// copy method was never meant to be public
exclude[DirectMissingMethodProblem]("sbt.CommandSource.copy"),
exclude[DirectMissingMethodProblem]("sbt.CommandSource.copy$default$*"),
exclude[DirectMissingMethodProblem]("sbt.Exec.copy"),
exclude[DirectMissingMethodProblem]("sbt.Exec.copy$default$*"),
),
unmanagedSources in (Compile, headerCreate) := {
val old = (unmanagedSources in (Compile, headerCreate)).value
Expand Down
Expand Up @@ -19,7 +19,7 @@ final class CommandSource private (
override def toString: String = {
"CommandSource(" + channelName + ")"
}
protected[this] def copy(channelName: String = channelName): CommandSource = {
private[this] def copy(channelName: String = channelName): CommandSource = {
new CommandSource(channelName)
}
def withChannelName(channelName: String): CommandSource = {
Expand Down
6 changes: 3 additions & 3 deletions main-command/src/main/contraband-scala/sbt/Exec.scala
Expand Up @@ -21,7 +21,7 @@ final class Exec private (
override def toString: String = {
"Exec(" + commandLine + ", " + execId + ", " + source + ")"
}
protected[this] def copy(commandLine: String = commandLine, execId: Option[String] = execId, source: Option[sbt.CommandSource] = source): Exec = {
private[this] def copy(commandLine: String = commandLine, execId: Option[String] = execId, source: Option[sbt.CommandSource] = source): Exec = {
new Exec(commandLine, execId, source)
}
def withCommandLine(commandLine: String): Exec = {
Expand All @@ -42,8 +42,8 @@ final class Exec private (
}
object Exec {
def newExecId: String = java.util.UUID.randomUUID.toString
def apply(commandLine: String, source: Option[sbt.CommandSource]): Exec = new Exec(commandLine, None, source)
def apply(commandLine: String, source: sbt.CommandSource): Exec = new Exec(commandLine, None, Option(source))
def apply(commandLine: String, source: Option[sbt.CommandSource]): Exec = new Exec(commandLine, source)
def apply(commandLine: String, source: sbt.CommandSource): Exec = new Exec(commandLine, Option(source))
def apply(commandLine: String, execId: Option[String], source: Option[sbt.CommandSource]): Exec = new Exec(commandLine, execId, source)
def apply(commandLine: String, execId: String, source: sbt.CommandSource): Exec = new Exec(commandLine, Option(execId), Option(source))
}
3 changes: 2 additions & 1 deletion main-command/src/main/scala/sbt/internal/server/Server.scala
Expand Up @@ -63,7 +63,8 @@ private[sbt] object Server {
val maxSocketLength = new UnixDomainSocketLibrary.SockaddrUn().sunPath.length - 1
val path = socketfile.getAbsolutePath
if (path.length > maxSocketLength)
sys.error("socket file absolute path too long; " +
sys.error(
"socket file absolute path too long; " +
"either switch to another connection type " +
"or define a short \"SBT_GLOBAL_SERVER_DIR\" value. " +
s"Current path: ${path}")
Expand Down
55 changes: 53 additions & 2 deletions main/src/main/scala/sbt/MainLoop.scala
Expand Up @@ -15,6 +15,7 @@ import jline.TerminalFactory
import sbt.io.{ IO, Using }
import sbt.internal.util.{ ErrorHandling, GlobalLogBacking }
import sbt.internal.util.complete.DefaultParsers
import sbt.internal.langserver.ErrorCodes
import sbt.util.Logger
import sbt.protocol._

Expand Down Expand Up @@ -155,13 +156,63 @@ object MainLoop {
state.log error errMsg
state.fail
}
StandardMain.exchange publishEventMessage ExecStatusEvent(
val doneEvent = ExecStatusEvent(
"Done",
channelName,
exec.execId,
newState.remainingCommands.toVector map (_.commandLine))
newState.remainingCommands.toVector map (_.commandLine),
exitCode(newState, state),
)
if (doneEvent.execId.isDefined) { // send back a response or error
import sbt.protocol.codec.JsonProtocol._
StandardMain.exchange publishEvent doneEvent
} else { // send back a notification
StandardMain.exchange publishEventMessage doneEvent
}
newState
}

def logFullException(e: Throwable, log: Logger): Unit = State.logFullException(e, log)

private[this] type ExitCode = Option[Long]
private[this] object ExitCode {
def apply(n: Long): ExitCode = Option(n)
val Success: ExitCode = ExitCode(0)
val Unknown: ExitCode = None
}

private[this] def exitCode(state: State, prevState: State): ExitCode = {
exitCodeFromStateNext(state) match {
case ExitCode.Success => exitCodeFromStateOnFailure(state, prevState)
case x => x
}
}

// State's "next" field indicates the next action for the command processor to take
// we'll use that to determine if the command failed
private[this] def exitCodeFromStateNext(state: State): ExitCode = {
state.next match {
case State.Continue => ExitCode.Success
case State.ClearGlobalLog => ExitCode.Success
case State.KeepLastLog => ExitCode.Success
case ret: State.Return =>
ret.result match {
case exit: xsbti.Exit => ExitCode(exit.code().toLong)
case _: xsbti.Continue => ExitCode.Success
case _: xsbti.Reboot => ExitCode.Success
case x =>
val clazz = if (x eq null) "" else " (class: " + x.getClass + ")"
state.log debug s"Unknown main result: $x$clazz"
ExitCode.Unknown
}
}
}

// the shell command specifies an onFailure so that if an exception is thrown
// it's handled by executing the shell again, instead of the state failing
// so we also use that to indicate that the execution failed
private[this] def exitCodeFromStateOnFailure(state: State, prevState: State): ExitCode =
if (prevState.onFailure.isDefined && state.onFailure.isEmpty) ExitCode(ErrorCodes.UnknownError)
else ExitCode.Success

}
8 changes: 7 additions & 1 deletion main/src/main/scala/sbt/internal/server/NetworkChannel.scala
Expand Up @@ -240,7 +240,13 @@ final class NetworkChannel(val name: String,
if (isLanguageServerProtocol) {
event match {
case entry: StringEvent => logMessage(entry.level, entry.message)
case _ => langRespond(event, execId)
case entry: ExecStatusEvent =>
entry.exitCode match {
case None => langRespond(event, entry.execId)
case Some(0) => langRespond(event, entry.execId)
case Some(exitCode) => langError(entry.execId, exitCode, "")
}
case _ => langRespond(event, execId)
}
} else {
contentType match {
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Expand Up @@ -2,6 +2,6 @@ scalaVersion := "2.12.4"
scalacOptions ++= Seq("-feature", "-language:postfixOps")

addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.5")
addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.3.2")
addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.4.0")
addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2")
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.8.0")
Expand Up @@ -18,7 +18,7 @@ override def hashCode: Int = {
override def toString: String = {
"ClientCapabilities()"
}
protected[this] def copy(): ClientCapabilities = {
private[this] def copy(): ClientCapabilities = {
new ClientCapabilities()
}

Expand Down
Expand Up @@ -38,7 +38,7 @@ final class Diagnostic private (
override def toString: String = {
"Diagnostic(" + range + ", " + severity + ", " + code + ", " + source + ", " + message + ")"
}
protected[this] def copy(range: sbt.internal.langserver.Range = range, severity: Option[Long] = severity, code: Option[String] = code, source: Option[String] = source, message: String = message): Diagnostic = {
private[this] def copy(range: sbt.internal.langserver.Range = range, severity: Option[Long] = severity, code: Option[String] = code, source: Option[String] = source, message: String = message): Diagnostic = {
new Diagnostic(range, severity, code, source, message)
}
def withRange(range: sbt.internal.langserver.Range): Diagnostic = {
Expand Down
Expand Up @@ -25,7 +25,7 @@ final class InitializeParams private (
override def toString: String = {
"InitializeParams(" + processId + ", " + rootPath + ", " + rootUri + ", " + initializationOptions + ", " + capabilities + ", " + trace + ")"
}
protected[this] def copy(processId: Option[Long] = processId, rootPath: Option[String] = rootPath, rootUri: Option[String] = rootUri, initializationOptions: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue] = initializationOptions, capabilities: Option[sbt.internal.langserver.ClientCapabilities] = capabilities, trace: Option[String] = trace): InitializeParams = {
private[this] def copy(processId: Option[Long] = processId, rootPath: Option[String] = rootPath, rootUri: Option[String] = rootUri, initializationOptions: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue] = initializationOptions, capabilities: Option[sbt.internal.langserver.ClientCapabilities] = capabilities, trace: Option[String] = trace): InitializeParams = {
new InitializeParams(processId, rootPath, rootUri, initializationOptions, capabilities, trace)
}
def withProcessId(processId: Option[Long]): InitializeParams = {
Expand Down
Expand Up @@ -20,7 +20,7 @@ final class InitializeResult private (
override def toString: String = {
"InitializeResult(" + capabilities + ")"
}
protected[this] def copy(capabilities: sbt.internal.langserver.ServerCapabilities = capabilities): InitializeResult = {
private[this] def copy(capabilities: sbt.internal.langserver.ServerCapabilities = capabilities): InitializeResult = {
new InitializeResult(capabilities)
}
def withCapabilities(capabilities: sbt.internal.langserver.ServerCapabilities): InitializeResult = {
Expand Down
Expand Up @@ -21,7 +21,7 @@ final class Location private (
override def toString: String = {
"Location(" + uri + ", " + range + ")"
}
protected[this] def copy(uri: String = uri, range: sbt.internal.langserver.Range = range): Location = {
private[this] def copy(uri: String = uri, range: sbt.internal.langserver.Range = range): Location = {
new Location(uri, range)
}
def withUri(uri: String): Location = {
Expand Down
Expand Up @@ -22,7 +22,7 @@ final class LogMessageParams private (
override def toString: String = {
"LogMessageParams(" + `type` + ", " + message + ")"
}
protected[this] def copy(`type`: Long = `type`, message: String = message): LogMessageParams = {
private[this] def copy(`type`: Long = `type`, message: String = message): LogMessageParams = {
new LogMessageParams(`type`, message)
}
def withType(`type`: Long): LogMessageParams = {
Expand Down
Expand Up @@ -26,7 +26,7 @@ final class Position private (
override def toString: String = {
"Position(" + line + ", " + character + ")"
}
protected[this] def copy(line: Long = line, character: Long = character): Position = {
private[this] def copy(line: Long = line, character: Long = character): Position = {
new Position(line, character)
}
def withLine(line: Long): Position = {
Expand Down
Expand Up @@ -23,7 +23,7 @@ final class PublishDiagnosticsParams private (
override def toString: String = {
"PublishDiagnosticsParams(" + uri + ", " + diagnostics + ")"
}
protected[this] def copy(uri: String = uri, diagnostics: Vector[sbt.internal.langserver.Diagnostic] = diagnostics): PublishDiagnosticsParams = {
private[this] def copy(uri: String = uri, diagnostics: Vector[sbt.internal.langserver.Diagnostic] = diagnostics): PublishDiagnosticsParams = {
new PublishDiagnosticsParams(uri, diagnostics)
}
def withUri(uri: String): PublishDiagnosticsParams = {
Expand Down
Expand Up @@ -26,7 +26,7 @@ final class Range private (
override def toString: String = {
"Range(" + start + ", " + end + ")"
}
protected[this] def copy(start: sbt.internal.langserver.Position = start, end: sbt.internal.langserver.Position = end): Range = {
private[this] def copy(start: sbt.internal.langserver.Position = start, end: sbt.internal.langserver.Position = end): Range = {
new Range(start, end)
}
def withStart(start: sbt.internal.langserver.Position): Range = {
Expand Down
Expand Up @@ -20,7 +20,7 @@ final class SaveOptions private (
override def toString: String = {
"SaveOptions(" + includeText + ")"
}
protected[this] def copy(includeText: Option[Boolean] = includeText): SaveOptions = {
private[this] def copy(includeText: Option[Boolean] = includeText): SaveOptions = {
new SaveOptions(includeText)
}
def withIncludeText(includeText: Option[Boolean]): SaveOptions = {
Expand Down
Expand Up @@ -20,7 +20,7 @@ final class SbtExecParams private (
override def toString: String = {
"SbtExecParams(" + commandLine + ")"
}
protected[this] def copy(commandLine: String = commandLine): SbtExecParams = {
private[this] def copy(commandLine: String = commandLine): SbtExecParams = {
new SbtExecParams(commandLine)
}
def withCommandLine(commandLine: String): SbtExecParams = {
Expand Down
Expand Up @@ -23,7 +23,7 @@ final class ServerCapabilities private (
override def toString: String = {
"ServerCapabilities(" + textDocumentSync + ", " + hoverProvider + ", " + definitionProvider + ")"
}
protected[this] def copy(textDocumentSync: Option[sbt.internal.langserver.TextDocumentSyncOptions] = textDocumentSync, hoverProvider: Option[Boolean] = hoverProvider, definitionProvider: Option[Boolean] = definitionProvider): ServerCapabilities = {
private[this] def copy(textDocumentSync: Option[sbt.internal.langserver.TextDocumentSyncOptions] = textDocumentSync, hoverProvider: Option[Boolean] = hoverProvider, definitionProvider: Option[Boolean] = definitionProvider): ServerCapabilities = {
new ServerCapabilities(textDocumentSync, hoverProvider, definitionProvider)
}
def withTextDocumentSync(textDocumentSync: Option[sbt.internal.langserver.TextDocumentSyncOptions]): ServerCapabilities = {
Expand Down
Expand Up @@ -21,7 +21,7 @@ final class TextDocumentIdentifier private (
override def toString: String = {
"TextDocumentIdentifier(" + uri + ")"
}
protected[this] def copy(uri: String = uri): TextDocumentIdentifier = {
private[this] def copy(uri: String = uri): TextDocumentIdentifier = {
new TextDocumentIdentifier(uri)
}
def withUri(uri: String): TextDocumentIdentifier = {
Expand Down
Expand Up @@ -23,7 +23,7 @@ final class TextDocumentPositionParams private (
override def toString: String = {
"TextDocumentPositionParams(" + textDocument + ", " + position + ")"
}
protected[this] def copy(textDocument: sbt.internal.langserver.TextDocumentIdentifier = textDocument, position: sbt.internal.langserver.Position = position): TextDocumentPositionParams = {
private[this] def copy(textDocument: sbt.internal.langserver.TextDocumentIdentifier = textDocument, position: sbt.internal.langserver.Position = position): TextDocumentPositionParams = {
new TextDocumentPositionParams(textDocument, position)
}
def withTextDocument(textDocument: sbt.internal.langserver.TextDocumentIdentifier): TextDocumentPositionParams = {
Expand Down

0 comments on commit 4efce7e

Please sign in to comment.