mirror of https://github.com/sbt/sbt.git
Merge pull request #8040 from eed3si9n/wip/sandbox
[1.x] Implement client-side run
This commit is contained in:
commit
bdaf1d9d32
|
|
@ -758,7 +758,7 @@ lazy val protocolProj = (project in file("protocol"))
|
|||
// General command support and core commands not specific to a build system
|
||||
lazy val commandProj = (project in file("main-command"))
|
||||
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
|
||||
.dependsOn(protocolProj, completeProj, utilLogging)
|
||||
.dependsOn(protocolProj, completeProj, utilLogging, runProj)
|
||||
.settings(
|
||||
testedBaseSettings,
|
||||
name := "Command",
|
||||
|
|
@ -1072,6 +1072,7 @@ lazy val mainProj = (project in file("main"))
|
|||
exclude[IncompatibleTemplateDefProblem]("sbt.internal.server.BuildServerReporter"),
|
||||
exclude[MissingClassProblem]("sbt.internal.CustomHttp*"),
|
||||
exclude[ReversedMissingMethodProblem]("sbt.JobHandle.isAutoCancel"),
|
||||
exclude[ReversedMissingMethodProblem]("sbt.BackgroundJobService.createWorkingDirectory"),
|
||||
)
|
||||
)
|
||||
.configure(
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ abstract class CommandChannel {
|
|||
}
|
||||
}
|
||||
}
|
||||
protected def appendExec(commandLine: String, execId: Option[String]): Boolean =
|
||||
append(Exec(commandLine, execId.orElse(Some(Exec.newExecId)), Some(CommandSource(name))))
|
||||
def poll: Option[Exec] = Option(commandQueue.poll)
|
||||
|
||||
def prompt(e: ConsolePromptEvent): Unit = userThread.onConsolePromptEvent(e)
|
||||
|
|
@ -81,20 +83,21 @@ abstract class CommandChannel {
|
|||
private[sbt] final def logLevel: Level.Value = level.get
|
||||
private[this] def setLevel(value: Level.Value, cmd: String): Boolean = {
|
||||
level.set(value)
|
||||
append(Exec(cmd, Some(Exec.newExecId), Some(CommandSource(name))))
|
||||
appendExec(cmd, None)
|
||||
}
|
||||
private[sbt] def onCommand: String => Boolean = {
|
||||
case "error" => setLevel(Level.Error, "error")
|
||||
case "debug" => setLevel(Level.Debug, "debug")
|
||||
case "info" => setLevel(Level.Info, "info")
|
||||
case "warn" => setLevel(Level.Warn, "warn")
|
||||
case cmd =>
|
||||
if (cmd.nonEmpty) append(Exec(cmd, Some(Exec.newExecId), Some(CommandSource(name))))
|
||||
else false
|
||||
}
|
||||
private[sbt] def onFastTrackTask: String => Boolean = { s: String =>
|
||||
private[sbt] def onCommandLine(cmd: String): Boolean =
|
||||
cmd match {
|
||||
case "error" => setLevel(Level.Error, "error")
|
||||
case "debug" => setLevel(Level.Debug, "debug")
|
||||
case "info" => setLevel(Level.Info, "info")
|
||||
case "warn" => setLevel(Level.Warn, "warn")
|
||||
case cmd =>
|
||||
if (cmd.nonEmpty) appendExec(cmd, None)
|
||||
else false
|
||||
}
|
||||
private[sbt] def onFastTrackTask(cmd: String): Boolean = {
|
||||
fastTrack.synchronized(fastTrack.forEach { q =>
|
||||
q.add(new FastTrackTask(this, s))
|
||||
q.add(new FastTrackTask(this, cmd))
|
||||
()
|
||||
})
|
||||
true
|
||||
|
|
|
|||
|
|
@ -23,8 +23,16 @@ import java.text.DateFormat
|
|||
import sbt.BasicCommandStrings.{ DashDashDetachStdio, DashDashServer, Shutdown, TerminateAction }
|
||||
import sbt.internal.client.NetworkClient.Arguments
|
||||
import sbt.internal.langserver.{ LogMessageParams, MessageType, PublishDiagnosticsParams }
|
||||
import sbt.internal.worker.{ ClientJobParams, JvmRunInfo, NativeRunInfo, RunInfo }
|
||||
import sbt.internal.protocol._
|
||||
import sbt.internal.util.{ ConsoleAppender, ConsoleOut, Signals, Terminal, Util }
|
||||
import sbt.internal.util.{
|
||||
ConsoleAppender,
|
||||
ConsoleOut,
|
||||
MessageOnlyException,
|
||||
Signals,
|
||||
Terminal,
|
||||
Util
|
||||
}
|
||||
import sbt.io.IO
|
||||
import sbt.io.syntax._
|
||||
import sbt.protocol._
|
||||
|
|
@ -43,6 +51,7 @@ import Serialization.{
|
|||
attach,
|
||||
cancelReadSystemIn,
|
||||
cancelRequest,
|
||||
clientJob,
|
||||
promptChannel,
|
||||
readSystemIn,
|
||||
systemIn,
|
||||
|
|
@ -63,6 +72,7 @@ import Serialization.{
|
|||
}
|
||||
import NetworkClient.Arguments
|
||||
import java.util.concurrent.TimeoutException
|
||||
import sbt.util.Logger
|
||||
|
||||
trait ConsoleInterface {
|
||||
def appendLog(level: Level.Value, message: => String): Unit
|
||||
|
|
@ -166,6 +176,11 @@ class NetworkClient(
|
|||
case null => inputThread.set(new RawInputThread)
|
||||
case _ =>
|
||||
}
|
||||
private lazy val log: Logger = new Logger {
|
||||
def trace(t: => Throwable): Unit = ()
|
||||
def success(message: => String): Unit = ()
|
||||
def log(level: Level.Value, message: => String): Unit = console.appendLog(level, message)
|
||||
}
|
||||
|
||||
private[sbt] def connectOrStartServerAndConnect(
|
||||
promptCompleteUsers: Boolean,
|
||||
|
|
@ -295,7 +310,18 @@ class NetworkClient(
|
|||
}
|
||||
// initiate handshake
|
||||
val execId = UUID.randomUUID.toString
|
||||
val initCommand = InitCommand(tkn, Option(execId), Some(true))
|
||||
val skipAnalysis = true
|
||||
val opts = InitializeOption(
|
||||
token = tkn,
|
||||
skipAnalysis = Some(skipAnalysis),
|
||||
canWork = Some(true),
|
||||
)
|
||||
val initCommand = InitCommand(
|
||||
token = tkn, // duplicated with opts for compatibility
|
||||
execId = Option(execId),
|
||||
skipAnalysis = Some(skipAnalysis), // duplicated with opts for compatibility
|
||||
initializationOptions = Some(opts),
|
||||
)
|
||||
conn.sendString(Serialization.serializeCommandAsJsonMessage(initCommand))
|
||||
connectionHolder.set(conn)
|
||||
conn
|
||||
|
|
@ -641,6 +667,12 @@ class NetworkClient(
|
|||
case Success(params) => splitDiagnostics(params); Vector()
|
||||
case Failure(_) => Vector()
|
||||
}
|
||||
case (`clientJob`, Some(json)) =>
|
||||
import sbt.internal.worker.codec.JsonProtocol._
|
||||
Converter.fromJson[ClientJobParams](json) match {
|
||||
case Success(params) => clientSideRun(params).get; Vector.empty
|
||||
case Failure(_) => Vector.empty
|
||||
}
|
||||
case (`Shutdown`, Some(_)) => Vector.empty
|
||||
case (msg, _) if msg.startsWith("build/") => Vector.empty
|
||||
case _ =>
|
||||
|
|
@ -687,6 +719,58 @@ class NetworkClient(
|
|||
}
|
||||
}
|
||||
|
||||
private def clientSideRun(params: ClientJobParams): Try[Unit] =
|
||||
params.runInfo match {
|
||||
case Some(info) => clientSideRun(info)
|
||||
case _ => Failure(new MessageOnlyException(s"runInfo is not specified in $params"))
|
||||
}
|
||||
|
||||
private def clientSideRun(runInfo: RunInfo): Try[Unit] = {
|
||||
def jvmRun(info: JvmRunInfo): Try[Unit] = {
|
||||
val option = ForkOptions(
|
||||
javaHome = info.javaHome.map(new File(_)),
|
||||
outputStrategy = None, // TODO: Handle buffered output etc
|
||||
bootJars = Vector.empty,
|
||||
workingDirectory = info.workingDirectory.map(new File(_)),
|
||||
runJVMOptions = info.jvmOptions,
|
||||
connectInput = info.connectInput,
|
||||
envVars = info.environmentVariables,
|
||||
)
|
||||
// ForkRun handles exit code handling and cancellation
|
||||
val runner = new ForkRun(option)
|
||||
runner
|
||||
.run(
|
||||
mainClass = info.mainClass,
|
||||
classpath = info.classpath.map(_.path).map(new File(_)),
|
||||
options = info.args,
|
||||
log = log
|
||||
)
|
||||
}
|
||||
def nativeRun(info: NativeRunInfo): Try[Unit] = {
|
||||
import java.lang.{ ProcessBuilder => JProcessBuilder }
|
||||
val option = ForkOptions(
|
||||
javaHome = None,
|
||||
outputStrategy = None, // TODO: Handle buffered output etc
|
||||
bootJars = Vector.empty,
|
||||
workingDirectory = info.workingDirectory.map(new File(_)),
|
||||
runJVMOptions = Vector.empty,
|
||||
connectInput = info.connectInput,
|
||||
envVars = info.environmentVariables,
|
||||
)
|
||||
val command = info.cmd :: info.args.toList
|
||||
val jpb = new JProcessBuilder(command: _*)
|
||||
val exitCode = try Fork.blockForExitCode(Fork.forkInternal(option, Nil, jpb))
|
||||
catch {
|
||||
case _: InterruptedException =>
|
||||
log.warn("run canceled")
|
||||
1
|
||||
}
|
||||
Run.processExitCode(exitCode, "runner")
|
||||
}
|
||||
if (runInfo.jvm) jvmRun(runInfo.jvmRunInfo.getOrElse(sys.error("missing jvmRunInfo")))
|
||||
else nativeRun(runInfo.nativeRunInfo.getOrElse(sys.error("missing nativeRunInfo")))
|
||||
}
|
||||
|
||||
def onRequest(msg: JsonRpcRequestMessage): Unit = {
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
(msg.method, msg.params) match {
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ trait ServerCallback {
|
|||
private[sbt] def authOptions: Set[ServerAuthentication]
|
||||
private[sbt] def authenticate(token: String): Boolean
|
||||
private[sbt] def setInitialized(value: Boolean): Unit
|
||||
private[sbt] def setInitializeOption(opts: InitializeOption): Unit
|
||||
private[sbt] def onSettingQuery(execId: Option[String], req: Q): Unit
|
||||
private[sbt] def onCompletionRequest(execId: Option[String], cp: CP): Unit
|
||||
private[sbt] def onCancellationRequest(execId: Option[String], crp: CRP): Unit
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ private[sbt] trait UITask extends Runnable with AutoCloseable {
|
|||
private[sbt] val reader: UITask.Reader
|
||||
private[this] final def handleInput(s: Either[String, String]): Boolean = s match {
|
||||
case Left(m) => channel.onFastTrackTask(m)
|
||||
case Right(cmd) => channel.onCommand(cmd)
|
||||
case Right(cmd) => channel.onCommandLine(cmd)
|
||||
}
|
||||
private[this] val isStopped = new AtomicBoolean(false)
|
||||
override def run(): Unit = {
|
||||
|
|
@ -56,6 +56,20 @@ private[sbt] object UITask {
|
|||
object Reader {
|
||||
// Avoid filling the stack trace since it isn't helpful here
|
||||
object interrupted extends InterruptedException
|
||||
|
||||
/**
|
||||
* Return Left for fast track commands, otherwise return Right(...).
|
||||
*/
|
||||
def splitCommand(cmd: String): Either[String, String] =
|
||||
// We need to put the empty string on the fast track queue so that we can
|
||||
// reprompt the user if another command is running on the server.
|
||||
if (cmd.isEmpty()) Left("")
|
||||
else
|
||||
cmd match {
|
||||
case Shutdown | TerminateAction | Cancel => Left(cmd)
|
||||
case cmd => Right(cmd)
|
||||
}
|
||||
|
||||
def terminalReader(parser: Parser[_])(
|
||||
terminal: Terminal,
|
||||
state: State
|
||||
|
|
@ -78,15 +92,8 @@ private[sbt] object UITask {
|
|||
Right("") // should be unreachable
|
||||
// JLine returns null on ctrl+d when there is no other input. This interprets
|
||||
// ctrl+d with no imput as an exit
|
||||
case None => Left(TerminateAction)
|
||||
case Some(s: String) =>
|
||||
s.trim() match {
|
||||
// We need to put the empty string on the fast track queue so that we can
|
||||
// reprompt the user if another command is running on the server.
|
||||
case "" => Left("")
|
||||
case cmd @ (`Shutdown` | `TerminateAction` | `Cancel`) => Left(cmd)
|
||||
case cmd => Right(cmd)
|
||||
}
|
||||
case None => Left(TerminateAction)
|
||||
case Some(s: String) => splitCommand(s.trim())
|
||||
}
|
||||
}
|
||||
terminal.setPrompt(Prompt.Pending)
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ abstract class BackgroundJobService extends Closeable {
|
|||
|
||||
def waitFor(job: JobHandle): Unit
|
||||
|
||||
private[sbt] def createWorkingDirectory: File
|
||||
|
||||
/** Copies classpath to temporary directories. */
|
||||
def copyClasspath(products: Classpath, full: Classpath, workingDirectory: File): Classpath
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import sbt.internal.server.{
|
|||
BspCompileTask,
|
||||
BuildServerProtocol,
|
||||
BuildServerReporter,
|
||||
ClientJob,
|
||||
Definition,
|
||||
LanguageServerProtocol,
|
||||
ServerHandler,
|
||||
|
|
@ -222,7 +223,7 @@ object Defaults extends BuildCommon {
|
|||
closeClassLoaders :== SysProp.closeClassLoaders,
|
||||
allowZombieClassLoaders :== true,
|
||||
packageTimestamp :== Package.defaultTimestamp,
|
||||
) ++ BuildServerProtocol.globalSettings
|
||||
) ++ BuildServerProtocol.globalSettings ++ ClientJob.globalSettings
|
||||
|
||||
private[sbt] lazy val globalIvyCore: Seq[Setting[_]] =
|
||||
Seq(
|
||||
|
|
@ -2717,7 +2718,7 @@ object Defaults extends BuildCommon {
|
|||
lazy val configSettings: Seq[Setting[_]] =
|
||||
Classpaths.configSettings ++ configTasks ++ configPaths ++ packageConfig ++
|
||||
Classpaths.compilerPluginConfig ++ deprecationSettings ++
|
||||
BuildServerProtocol.configSettings
|
||||
BuildServerProtocol.configSettings ++ ClientJob.configSettings
|
||||
|
||||
lazy val compileSettings: Seq[Setting[_]] =
|
||||
configSettings ++ (mainBgRunMainTask +: mainBgRunTask) ++ Classpaths.addUnmanagedLibrary
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import sbt.internal.remotecache.RemoteCacheArtifact
|
|||
import sbt.internal.server.BuildServerProtocol.BspFullWorkspace
|
||||
import sbt.internal.server.{ BuildServerReporter, ServerHandler }
|
||||
import sbt.internal.util.{ AttributeKey, ProgressState, SourcePosition }
|
||||
import sbt.internal.worker.ClientJobParams
|
||||
import sbt.io._
|
||||
import sbt.librarymanagement.Configurations.CompilerPlugin
|
||||
import sbt.librarymanagement.LibraryManagementCodec._
|
||||
|
|
@ -437,6 +438,8 @@ object Keys {
|
|||
val bspScalaMainClasses = inputKey[Unit]("Implementation of buildTarget/scalaMainClasses").withRank(DTask)
|
||||
val bspScalaMainClassesItem = taskKey[ScalaMainClassesItem]("").withRank(DTask)
|
||||
val bspReporter = taskKey[BuildServerReporter]("").withRank(DTask)
|
||||
val clientJob = inputKey[ClientJobParams]("Translates a task into a job specification").withRank(Invisible)
|
||||
val clientJobRunInfo = inputKey[ClientJobParams]("Translates the run task into a job specification").withRank(Invisible)
|
||||
|
||||
val useCoursier = settingKey[Boolean]("Use Coursier for dependency resolution.").withRank(BSetting)
|
||||
val csrCacheDirectory = settingKey[File]("Coursier cache directory. Uses -Dsbt.coursier.home or Coursier's default.").withRank(CSetting)
|
||||
|
|
|
|||
|
|
@ -144,6 +144,16 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
|
|||
override val isAutoCancel = false
|
||||
}
|
||||
|
||||
private[sbt] def createWorkingDirectory: File = {
|
||||
val id = nextId.getAndIncrement()
|
||||
createWorkingDirectory(id)
|
||||
}
|
||||
private[sbt] def createWorkingDirectory(id: Long): File = {
|
||||
val workingDir = serviceTempDir / s"job-$id"
|
||||
IO.createDirectory(workingDir)
|
||||
workingDir
|
||||
}
|
||||
|
||||
def doRunInBackground(
|
||||
spawningTask: ScopedKey[_],
|
||||
state: State,
|
||||
|
|
@ -153,8 +163,7 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
|
|||
val extracted = Project.extract(state)
|
||||
val logger =
|
||||
LogManager.constructBackgroundLog(extracted.structure.data, state, context)(spawningTask)
|
||||
val workingDir = serviceTempDir / s"job-$id"
|
||||
IO.createDirectory(workingDir)
|
||||
val workingDir = createWorkingDirectory(id)
|
||||
val job = try {
|
||||
new ThreadJobHandle(id, spawningTask, logger, workingDir, start(logger, workingDir))
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ object BuildServerProtocol {
|
|||
state.respondEvent(result)
|
||||
}
|
||||
}.evaluated,
|
||||
bspScalaMainClasses / aggregate := false
|
||||
bspScalaMainClasses / aggregate := false,
|
||||
)
|
||||
|
||||
// This will be scoped to Compile, Test, IntegrationTest etc
|
||||
|
|
@ -345,7 +345,7 @@ object BuildServerProtocol {
|
|||
} else {
|
||||
new BuildServerForwarder(meta, logger, underlying)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
private[sbt] object Method {
|
||||
final val Initialize = "build/initialize"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2023, Scala center
|
||||
* Copyright 2011 - 2022, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt
|
||||
package internal
|
||||
package server
|
||||
|
||||
import java.io.File
|
||||
import sbt.BuildSyntax._
|
||||
import sbt.Def._
|
||||
import sbt.Keys._
|
||||
import sbt.SlashSyntax0._
|
||||
import sbt.internal.util.complete.Parser
|
||||
import sbt.internal.worker.{ ClientJobParams, FilePath, JvmRunInfo, RunInfo }
|
||||
import sbt.io.IO
|
||||
import sbt.protocol.Serialization
|
||||
|
||||
/**
|
||||
* A ClientJob represents a unit of work that sbt server process
|
||||
* can outsourse back to the client. Initially intended for sbtn client-side run.
|
||||
*/
|
||||
object ClientJob {
|
||||
lazy val globalSettings: Seq[Def.Setting[_]] = Seq(
|
||||
clientJob := clientJobTask.evaluated,
|
||||
clientJob / aggregate := false,
|
||||
)
|
||||
|
||||
private def clientJobTask: Def.Initialize[InputTask[ClientJobParams]] = Def.inputTaskDyn {
|
||||
val tokens = spaceDelimited().parsed
|
||||
val state = Keys.state.value
|
||||
val p = Act.aggregatedKeyParser(state)
|
||||
if (tokens.isEmpty) {
|
||||
sys.error("expected an argument, for example foo/run")
|
||||
}
|
||||
val scopedKey = Parser.parse(tokens.head, p) match {
|
||||
case Right(x :: Nil) => x
|
||||
case Right(xs) => sys.error("too many keys")
|
||||
case Left(err) => sys.error(err)
|
||||
}
|
||||
if (scopedKey.key == run.key)
|
||||
clientJobRunInfo.in(scopedKey.scope).toTask(" " + tokens.tail.mkString(" "))
|
||||
else sys.error(s"unsupported task for clientJob $scopedKey")
|
||||
}
|
||||
|
||||
// This will be scoped to Compile, Test, etc
|
||||
lazy val configSettings: Seq[Def.Setting[_]] = Seq(
|
||||
clientJobRunInfo := clientJobRunInfoTask.evaluated,
|
||||
)
|
||||
|
||||
private def clientJobRunInfoTask: Def.Initialize[InputTask[ClientJobParams]] = Def.inputTask {
|
||||
val state = Keys.state.value
|
||||
val args = spaceDelimited().parsed
|
||||
val mainClass = (Keys.run / Keys.mainClass).value
|
||||
val service = bgJobService.value
|
||||
val fo = (Keys.run / Keys.forkOptions).value
|
||||
val workingDir = service.createWorkingDirectory
|
||||
val cp = service.copyClasspath(
|
||||
exportedProductJars.value,
|
||||
fullClasspathAsJars.value,
|
||||
workingDir,
|
||||
hashContents = true,
|
||||
)
|
||||
val strategy = fo.outputStrategy.map(_.getClass().getSimpleName().filter(_ != '$'))
|
||||
// sbtn doesn't set java.home, so we need to do the fallback here
|
||||
val javaHome =
|
||||
fo.javaHome.map(IO.toURI).orElse(sys.props.get("java.home").map(x => IO.toURI(new File(x))))
|
||||
val jvmRunInfo = JvmRunInfo(
|
||||
args = args.toVector,
|
||||
classpath = cp.map(x => IO.toURI(x.data)).map(FilePath(_, "")).toVector,
|
||||
mainClass = mainClass.getOrElse(sys.error("no main class")),
|
||||
connectInput = fo.connectInput,
|
||||
javaHome = javaHome,
|
||||
outputStrategy = strategy,
|
||||
workingDirectory = fo.workingDirectory.map(IO.toURI),
|
||||
jvmOptions = fo.runJVMOptions,
|
||||
environmentVariables = fo.envVars.toMap,
|
||||
)
|
||||
val info = RunInfo(
|
||||
jvm = true,
|
||||
jvmRunInfo = jvmRunInfo,
|
||||
)
|
||||
val result = ClientJobParams(
|
||||
runInfo = info
|
||||
)
|
||||
import sbt.internal.worker.codec.JsonProtocol._
|
||||
state.notifyEvent(Serialization.clientJob, result)
|
||||
result
|
||||
}
|
||||
}
|
||||
|
|
@ -62,6 +62,7 @@ private[sbt] object LanguageServerProtocol {
|
|||
else throw LangServerError(ErrorCodes.InvalidRequest, "invalid token")
|
||||
} else ()
|
||||
setInitialized(true)
|
||||
setInitializeOption(opt)
|
||||
if (!opt.skipAnalysis.getOrElse(false)) appendExec("collectAnalyses", None)
|
||||
jsonRpcRespond(InitializeResult(serverCapabilities), Some(r.id))
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
|
|||
import sbt.BasicCommandStrings.{ Shutdown, TerminateAction }
|
||||
import sbt.internal.langserver.{ CancelRequestParams, ErrorCodes, LogMessageParams, MessageType }
|
||||
import sbt.internal.protocol.{
|
||||
InitializeOption,
|
||||
JsonRpcNotificationMessage,
|
||||
JsonRpcRequestMessage,
|
||||
JsonRpcResponseError,
|
||||
|
|
@ -83,6 +84,10 @@ final class NetworkChannel(
|
|||
private val delimiter: Byte = '\n'.toByte
|
||||
private val out = connection.getOutputStream
|
||||
private var initialized = false
|
||||
|
||||
/** Reference to the client-side custom options
|
||||
*/
|
||||
private val initializeOption = new AtomicReference[InitializeOption](null)
|
||||
private val pendingRequests: mutable.Map[String, JsonRpcRequestMessage] = mutable.Map()
|
||||
|
||||
private[this] val inputBuffer = new LinkedBlockingQueue[Int]()
|
||||
|
|
@ -124,7 +129,7 @@ final class NetworkChannel(
|
|||
self.jsonRpcNotify(method, params)
|
||||
|
||||
def appendExec(commandLine: String, execId: Option[String]): Boolean =
|
||||
self.append(Exec(commandLine, execId, Some(CommandSource(name))))
|
||||
self.appendExec(commandLine, execId)
|
||||
|
||||
def appendExec(exec: Exec): Boolean = self.append(exec)
|
||||
|
||||
|
|
@ -133,6 +138,8 @@ final class NetworkChannel(
|
|||
private[sbt] def authOptions: Set[ServerAuthentication] = self.authOptions
|
||||
private[sbt] def authenticate(token: String): Boolean = self.authenticate(token)
|
||||
private[sbt] def setInitialized(value: Boolean): Unit = self.setInitialized(value)
|
||||
private[sbt] def setInitializeOption(opts: InitializeOption): Unit =
|
||||
self.setInitializeOption(opts)
|
||||
private[sbt] def onSettingQuery(execId: Option[String], req: SettingQuery): Unit =
|
||||
self.onSettingQuery(execId, req)
|
||||
private[sbt] def onCompletionRequest(execId: Option[String], cp: CompletionParams): Unit =
|
||||
|
|
@ -141,6 +148,30 @@ final class NetworkChannel(
|
|||
self.onCancellationRequest(execId, crp)
|
||||
}
|
||||
|
||||
// Take over commandline for network channel
|
||||
private val networkCommand: PartialFunction[String, String] = {
|
||||
case cmd if cmd.split(" ").head.split("/").last == "run" =>
|
||||
s"clientJob $cmd"
|
||||
}
|
||||
override protected def appendExec(commandLine: String, execId: Option[String]): Boolean =
|
||||
if (clientCanWork && networkCommand.isDefinedAt(commandLine))
|
||||
super.appendExec(networkCommand(commandLine), execId)
|
||||
else super.appendExec(commandLine, execId)
|
||||
|
||||
override private[sbt] def onCommandLine(cmd: String): Boolean =
|
||||
if (clientCanWork && networkCommand.isDefinedAt(cmd))
|
||||
appendExec(networkCommand(cmd), None)
|
||||
else super.onCommandLine(cmd)
|
||||
|
||||
protected def setInitializeOption(opts: InitializeOption): Unit = initializeOption.set(opts)
|
||||
|
||||
// Returns true if sbtn has declared with canWork: true
|
||||
protected def clientCanWork: Boolean =
|
||||
Option(initializeOption.get) match {
|
||||
case Some(opts) => opts.canWork.getOrElse(false)
|
||||
case _ => false
|
||||
}
|
||||
|
||||
protected def authenticate(token: String): Boolean = instance.authenticate(token)
|
||||
|
||||
protected def setInitialized(value: Boolean): Unit = initialized = value
|
||||
|
|
@ -369,40 +400,6 @@ final class NetworkChannel(
|
|||
try pendingWrites.put(event -> delimit)
|
||||
catch { case _: InterruptedException => }
|
||||
|
||||
def onCommand(command: CommandMessage): Unit = command match {
|
||||
case x: InitCommand => onInitCommand(x)
|
||||
case x: ExecCommand => onExecCommand(x)
|
||||
case x: SettingQuery => onSettingQuery(None, x)
|
||||
}
|
||||
|
||||
private def onInitCommand(cmd: InitCommand): Unit = {
|
||||
if (auth(ServerAuthentication.Token)) {
|
||||
cmd.token match {
|
||||
case Some(x) =>
|
||||
authenticate(x) match {
|
||||
case true =>
|
||||
initialized = true
|
||||
notifyEvent(ChannelAcceptedEvent(name))
|
||||
case _ => sys.error("invalid token")
|
||||
}
|
||||
case None => sys.error("init command but without token.")
|
||||
}
|
||||
} else {
|
||||
initialized = true
|
||||
}
|
||||
}
|
||||
|
||||
private def onExecCommand(cmd: ExecCommand) = {
|
||||
if (initialized) {
|
||||
append(
|
||||
Exec(cmd.commandLine, cmd.execId orElse Some(Exec.newExecId), Some(CommandSource(name)))
|
||||
)
|
||||
()
|
||||
} else {
|
||||
log.warn(s"ignoring command $cmd before initialization")
|
||||
}
|
||||
}
|
||||
|
||||
protected def onSettingQuery(execId: Option[String], req: SettingQuery) = {
|
||||
if (initialized) {
|
||||
StandardMain.exchange.withState { s =>
|
||||
|
|
|
|||
|
|
@ -4,24 +4,30 @@
|
|||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.protocol
|
||||
/**
|
||||
* Passed into InitializeParams as part of "initialize" request as the user-defined option.
|
||||
* https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize
|
||||
*/
|
||||
final class InitializeOption private (
|
||||
val token: Option[String],
|
||||
val skipAnalysis: Option[Boolean]) extends Serializable {
|
||||
val skipAnalysis: Option[Boolean],
|
||||
val canWork: Option[Boolean]) extends Serializable {
|
||||
|
||||
private def this(token: Option[String]) = this(token, None)
|
||||
private def this(token: Option[String]) = this(token, None, None)
|
||||
private def this(token: Option[String], skipAnalysis: Option[Boolean]) = this(token, skipAnalysis, None)
|
||||
|
||||
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
|
||||
case x: InitializeOption => (this.token == x.token) && (this.skipAnalysis == x.skipAnalysis)
|
||||
case x: InitializeOption => (this.token == x.token) && (this.skipAnalysis == x.skipAnalysis) && (this.canWork == x.canWork)
|
||||
case _ => false
|
||||
})
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (17 + "sbt.internal.protocol.InitializeOption".##) + token.##) + skipAnalysis.##)
|
||||
37 * (37 * (37 * (37 * (17 + "sbt.internal.protocol.InitializeOption".##) + token.##) + skipAnalysis.##) + canWork.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"InitializeOption(" + token + ", " + skipAnalysis + ")"
|
||||
"InitializeOption(" + token + ", " + skipAnalysis + ", " + canWork + ")"
|
||||
}
|
||||
private[this] def copy(token: Option[String] = token, skipAnalysis: Option[Boolean] = skipAnalysis): InitializeOption = {
|
||||
new InitializeOption(token, skipAnalysis)
|
||||
private[this] def copy(token: Option[String] = token, skipAnalysis: Option[Boolean] = skipAnalysis, canWork: Option[Boolean] = canWork): InitializeOption = {
|
||||
new InitializeOption(token, skipAnalysis, canWork)
|
||||
}
|
||||
def withToken(token: Option[String]): InitializeOption = {
|
||||
copy(token = token)
|
||||
|
|
@ -35,6 +41,12 @@ final class InitializeOption private (
|
|||
def withSkipAnalysis(skipAnalysis: Boolean): InitializeOption = {
|
||||
copy(skipAnalysis = Option(skipAnalysis))
|
||||
}
|
||||
def withCanWork(canWork: Option[Boolean]): InitializeOption = {
|
||||
copy(canWork = canWork)
|
||||
}
|
||||
def withCanWork(canWork: Boolean): InitializeOption = {
|
||||
copy(canWork = Option(canWork))
|
||||
}
|
||||
}
|
||||
object InitializeOption {
|
||||
|
||||
|
|
@ -42,4 +54,6 @@ object InitializeOption {
|
|||
def apply(token: String): InitializeOption = new InitializeOption(Option(token))
|
||||
def apply(token: Option[String], skipAnalysis: Option[Boolean]): InitializeOption = new InitializeOption(token, skipAnalysis)
|
||||
def apply(token: String, skipAnalysis: Boolean): InitializeOption = new InitializeOption(Option(token), Option(skipAnalysis))
|
||||
def apply(token: Option[String], skipAnalysis: Option[Boolean], canWork: Option[Boolean]): InitializeOption = new InitializeOption(token, skipAnalysis, canWork)
|
||||
def apply(token: String, skipAnalysis: Boolean, canWork: Boolean): InitializeOption = new InitializeOption(Option(token), Option(skipAnalysis), Option(canWork))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ implicit lazy val InitializeOptionFormat: JsonFormat[sbt.internal.protocol.Initi
|
|||
unbuilder.beginObject(__js)
|
||||
val token = unbuilder.readField[Option[String]]("token")
|
||||
val skipAnalysis = unbuilder.readField[Option[Boolean]]("skipAnalysis")
|
||||
val canWork = unbuilder.readField[Option[Boolean]]("canWork")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.protocol.InitializeOption(token, skipAnalysis)
|
||||
sbt.internal.protocol.InitializeOption(token, skipAnalysis, canWork)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
|
|
@ -23,6 +24,7 @@ implicit lazy val InitializeOptionFormat: JsonFormat[sbt.internal.protocol.Initi
|
|||
builder.beginObject()
|
||||
builder.addField("token", obj.token)
|
||||
builder.addField("skipAnalysis", obj.skipAnalysis)
|
||||
builder.addField("canWork", obj.canWork)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
45
protocol/src/main/contraband-scala/sbt/internal/worker/ClientJobParams.scala
generated
Normal file
45
protocol/src/main/contraband-scala/sbt/internal/worker/ClientJobParams.scala
generated
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.worker
|
||||
/**
|
||||
* Client-side job support.
|
||||
*
|
||||
* Notification: sbt/clientJob
|
||||
*
|
||||
* Parameter for the sbt/clientJob notification.
|
||||
* A client-side job represents a unit of work that sbt server
|
||||
* can outsourse back to the client, for example for run task.
|
||||
*/
|
||||
final class ClientJobParams private (
|
||||
val runInfo: Option[sbt.internal.worker.RunInfo]) extends Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
|
||||
case x: ClientJobParams => (this.runInfo == x.runInfo)
|
||||
case _ => false
|
||||
})
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (17 + "sbt.internal.worker.ClientJobParams".##) + runInfo.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"ClientJobParams(" + runInfo + ")"
|
||||
}
|
||||
private[this] def copy(runInfo: Option[sbt.internal.worker.RunInfo] = runInfo): ClientJobParams = {
|
||||
new ClientJobParams(runInfo)
|
||||
}
|
||||
def withRunInfo(runInfo: Option[sbt.internal.worker.RunInfo]): ClientJobParams = {
|
||||
copy(runInfo = runInfo)
|
||||
}
|
||||
def withRunInfo(runInfo: sbt.internal.worker.RunInfo): ClientJobParams = {
|
||||
copy(runInfo = Option(runInfo))
|
||||
}
|
||||
}
|
||||
object ClientJobParams {
|
||||
|
||||
def apply(runInfo: Option[sbt.internal.worker.RunInfo]): ClientJobParams = new ClientJobParams(runInfo)
|
||||
def apply(runInfo: sbt.internal.worker.RunInfo): ClientJobParams = new ClientJobParams(Option(runInfo))
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.worker
|
||||
final class FilePath private (
|
||||
val path: java.net.URI,
|
||||
val digest: String) extends Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
|
||||
case x: FilePath => (this.path == x.path) && (this.digest == x.digest)
|
||||
case _ => false
|
||||
})
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (17 + "sbt.internal.worker.FilePath".##) + path.##) + digest.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"FilePath(" + path + ", " + digest + ")"
|
||||
}
|
||||
private[this] def copy(path: java.net.URI = path, digest: String = digest): FilePath = {
|
||||
new FilePath(path, digest)
|
||||
}
|
||||
def withPath(path: java.net.URI): FilePath = {
|
||||
copy(path = path)
|
||||
}
|
||||
def withDigest(digest: String): FilePath = {
|
||||
copy(digest = digest)
|
||||
}
|
||||
}
|
||||
object FilePath {
|
||||
|
||||
def apply(path: java.net.URI, digest: String): FilePath = new FilePath(path, digest)
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.worker
|
||||
final class JvmRunInfo private (
|
||||
val args: Vector[String],
|
||||
val classpath: Vector[sbt.internal.worker.FilePath],
|
||||
val mainClass: String,
|
||||
val connectInput: Boolean,
|
||||
val javaHome: Option[java.net.URI],
|
||||
val outputStrategy: Option[String],
|
||||
val workingDirectory: Option[java.net.URI],
|
||||
val jvmOptions: Vector[String],
|
||||
val environmentVariables: scala.collection.immutable.Map[String, String],
|
||||
val inputs: Vector[sbt.internal.worker.FilePath],
|
||||
val outputs: Vector[sbt.internal.worker.FilePath]) extends Serializable {
|
||||
|
||||
private def this(args: Vector[String], classpath: Vector[sbt.internal.worker.FilePath], mainClass: String, connectInput: Boolean, javaHome: Option[java.net.URI], outputStrategy: Option[String], workingDirectory: Option[java.net.URI], jvmOptions: Vector[String], environmentVariables: scala.collection.immutable.Map[String, String]) = this(args, classpath, mainClass, connectInput, javaHome, outputStrategy, workingDirectory, jvmOptions, environmentVariables, Vector(), Vector())
|
||||
|
||||
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
|
||||
case x: JvmRunInfo => (this.args == x.args) && (this.classpath == x.classpath) && (this.mainClass == x.mainClass) && (this.connectInput == x.connectInput) && (this.javaHome == x.javaHome) && (this.outputStrategy == x.outputStrategy) && (this.workingDirectory == x.workingDirectory) && (this.jvmOptions == x.jvmOptions) && (this.environmentVariables == x.environmentVariables) && (this.inputs == x.inputs) && (this.outputs == x.outputs)
|
||||
case _ => false
|
||||
})
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.worker.JvmRunInfo".##) + args.##) + classpath.##) + mainClass.##) + connectInput.##) + javaHome.##) + outputStrategy.##) + workingDirectory.##) + jvmOptions.##) + environmentVariables.##) + inputs.##) + outputs.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"JvmRunInfo(" + args + ", " + classpath + ", " + mainClass + ", " + connectInput + ", " + javaHome + ", " + outputStrategy + ", " + workingDirectory + ", " + jvmOptions + ", " + environmentVariables + ", " + inputs + ", " + outputs + ")"
|
||||
}
|
||||
private[this] def copy(args: Vector[String] = args, classpath: Vector[sbt.internal.worker.FilePath] = classpath, mainClass: String = mainClass, connectInput: Boolean = connectInput, javaHome: Option[java.net.URI] = javaHome, outputStrategy: Option[String] = outputStrategy, workingDirectory: Option[java.net.URI] = workingDirectory, jvmOptions: Vector[String] = jvmOptions, environmentVariables: scala.collection.immutable.Map[String, String] = environmentVariables, inputs: Vector[sbt.internal.worker.FilePath] = inputs, outputs: Vector[sbt.internal.worker.FilePath] = outputs): JvmRunInfo = {
|
||||
new JvmRunInfo(args, classpath, mainClass, connectInput, javaHome, outputStrategy, workingDirectory, jvmOptions, environmentVariables, inputs, outputs)
|
||||
}
|
||||
def withArgs(args: Vector[String]): JvmRunInfo = {
|
||||
copy(args = args)
|
||||
}
|
||||
def withClasspath(classpath: Vector[sbt.internal.worker.FilePath]): JvmRunInfo = {
|
||||
copy(classpath = classpath)
|
||||
}
|
||||
def withMainClass(mainClass: String): JvmRunInfo = {
|
||||
copy(mainClass = mainClass)
|
||||
}
|
||||
def withConnectInput(connectInput: Boolean): JvmRunInfo = {
|
||||
copy(connectInput = connectInput)
|
||||
}
|
||||
def withJavaHome(javaHome: Option[java.net.URI]): JvmRunInfo = {
|
||||
copy(javaHome = javaHome)
|
||||
}
|
||||
def withJavaHome(javaHome: java.net.URI): JvmRunInfo = {
|
||||
copy(javaHome = Option(javaHome))
|
||||
}
|
||||
def withOutputStrategy(outputStrategy: Option[String]): JvmRunInfo = {
|
||||
copy(outputStrategy = outputStrategy)
|
||||
}
|
||||
def withOutputStrategy(outputStrategy: String): JvmRunInfo = {
|
||||
copy(outputStrategy = Option(outputStrategy))
|
||||
}
|
||||
def withWorkingDirectory(workingDirectory: Option[java.net.URI]): JvmRunInfo = {
|
||||
copy(workingDirectory = workingDirectory)
|
||||
}
|
||||
def withWorkingDirectory(workingDirectory: java.net.URI): JvmRunInfo = {
|
||||
copy(workingDirectory = Option(workingDirectory))
|
||||
}
|
||||
def withJvmOptions(jvmOptions: Vector[String]): JvmRunInfo = {
|
||||
copy(jvmOptions = jvmOptions)
|
||||
}
|
||||
def withEnvironmentVariables(environmentVariables: scala.collection.immutable.Map[String, String]): JvmRunInfo = {
|
||||
copy(environmentVariables = environmentVariables)
|
||||
}
|
||||
def withInputs(inputs: Vector[sbt.internal.worker.FilePath]): JvmRunInfo = {
|
||||
copy(inputs = inputs)
|
||||
}
|
||||
def withOutputs(outputs: Vector[sbt.internal.worker.FilePath]): JvmRunInfo = {
|
||||
copy(outputs = outputs)
|
||||
}
|
||||
}
|
||||
object JvmRunInfo {
|
||||
|
||||
def apply(args: Vector[String], classpath: Vector[sbt.internal.worker.FilePath], mainClass: String, connectInput: Boolean, javaHome: Option[java.net.URI], outputStrategy: Option[String], workingDirectory: Option[java.net.URI], jvmOptions: Vector[String], environmentVariables: scala.collection.immutable.Map[String, String]): JvmRunInfo = new JvmRunInfo(args, classpath, mainClass, connectInput, javaHome, outputStrategy, workingDirectory, jvmOptions, environmentVariables)
|
||||
def apply(args: Vector[String], classpath: Vector[sbt.internal.worker.FilePath], mainClass: String, connectInput: Boolean, javaHome: java.net.URI, outputStrategy: String, workingDirectory: java.net.URI, jvmOptions: Vector[String], environmentVariables: scala.collection.immutable.Map[String, String]): JvmRunInfo = new JvmRunInfo(args, classpath, mainClass, connectInput, Option(javaHome), Option(outputStrategy), Option(workingDirectory), jvmOptions, environmentVariables)
|
||||
def apply(args: Vector[String], classpath: Vector[sbt.internal.worker.FilePath], mainClass: String, connectInput: Boolean, javaHome: Option[java.net.URI], outputStrategy: Option[String], workingDirectory: Option[java.net.URI], jvmOptions: Vector[String], environmentVariables: scala.collection.immutable.Map[String, String], inputs: Vector[sbt.internal.worker.FilePath], outputs: Vector[sbt.internal.worker.FilePath]): JvmRunInfo = new JvmRunInfo(args, classpath, mainClass, connectInput, javaHome, outputStrategy, workingDirectory, jvmOptions, environmentVariables, inputs, outputs)
|
||||
def apply(args: Vector[String], classpath: Vector[sbt.internal.worker.FilePath], mainClass: String, connectInput: Boolean, javaHome: java.net.URI, outputStrategy: String, workingDirectory: java.net.URI, jvmOptions: Vector[String], environmentVariables: scala.collection.immutable.Map[String, String], inputs: Vector[sbt.internal.worker.FilePath], outputs: Vector[sbt.internal.worker.FilePath]): JvmRunInfo = new JvmRunInfo(args, classpath, mainClass, connectInput, Option(javaHome), Option(outputStrategy), Option(workingDirectory), jvmOptions, environmentVariables, inputs, outputs)
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.worker
|
||||
final class NativeRunInfo private (
|
||||
val cmd: String,
|
||||
val args: Vector[String],
|
||||
val connectInput: Boolean,
|
||||
val outputStrategy: Option[String],
|
||||
val workingDirectory: Option[java.net.URI],
|
||||
val environmentVariables: scala.collection.immutable.Map[String, String],
|
||||
val inputs: Vector[sbt.internal.worker.FilePath],
|
||||
val outputs: Vector[sbt.internal.worker.FilePath]) extends Serializable {
|
||||
|
||||
private def this(cmd: String, args: Vector[String], connectInput: Boolean, outputStrategy: Option[String], workingDirectory: Option[java.net.URI], environmentVariables: scala.collection.immutable.Map[String, String]) = this(cmd, args, connectInput, outputStrategy, workingDirectory, environmentVariables, Vector(), Vector())
|
||||
|
||||
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
|
||||
case x: NativeRunInfo => (this.cmd == x.cmd) && (this.args == x.args) && (this.connectInput == x.connectInput) && (this.outputStrategy == x.outputStrategy) && (this.workingDirectory == x.workingDirectory) && (this.environmentVariables == x.environmentVariables) && (this.inputs == x.inputs) && (this.outputs == x.outputs)
|
||||
case _ => false
|
||||
})
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.worker.NativeRunInfo".##) + cmd.##) + args.##) + connectInput.##) + outputStrategy.##) + workingDirectory.##) + environmentVariables.##) + inputs.##) + outputs.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"NativeRunInfo(" + cmd + ", " + args + ", " + connectInput + ", " + outputStrategy + ", " + workingDirectory + ", " + environmentVariables + ", " + inputs + ", " + outputs + ")"
|
||||
}
|
||||
private[this] def copy(cmd: String = cmd, args: Vector[String] = args, connectInput: Boolean = connectInput, outputStrategy: Option[String] = outputStrategy, workingDirectory: Option[java.net.URI] = workingDirectory, environmentVariables: scala.collection.immutable.Map[String, String] = environmentVariables, inputs: Vector[sbt.internal.worker.FilePath] = inputs, outputs: Vector[sbt.internal.worker.FilePath] = outputs): NativeRunInfo = {
|
||||
new NativeRunInfo(cmd, args, connectInput, outputStrategy, workingDirectory, environmentVariables, inputs, outputs)
|
||||
}
|
||||
def withCmd(cmd: String): NativeRunInfo = {
|
||||
copy(cmd = cmd)
|
||||
}
|
||||
def withArgs(args: Vector[String]): NativeRunInfo = {
|
||||
copy(args = args)
|
||||
}
|
||||
def withConnectInput(connectInput: Boolean): NativeRunInfo = {
|
||||
copy(connectInput = connectInput)
|
||||
}
|
||||
def withOutputStrategy(outputStrategy: Option[String]): NativeRunInfo = {
|
||||
copy(outputStrategy = outputStrategy)
|
||||
}
|
||||
def withOutputStrategy(outputStrategy: String): NativeRunInfo = {
|
||||
copy(outputStrategy = Option(outputStrategy))
|
||||
}
|
||||
def withWorkingDirectory(workingDirectory: Option[java.net.URI]): NativeRunInfo = {
|
||||
copy(workingDirectory = workingDirectory)
|
||||
}
|
||||
def withWorkingDirectory(workingDirectory: java.net.URI): NativeRunInfo = {
|
||||
copy(workingDirectory = Option(workingDirectory))
|
||||
}
|
||||
def withEnvironmentVariables(environmentVariables: scala.collection.immutable.Map[String, String]): NativeRunInfo = {
|
||||
copy(environmentVariables = environmentVariables)
|
||||
}
|
||||
def withInputs(inputs: Vector[sbt.internal.worker.FilePath]): NativeRunInfo = {
|
||||
copy(inputs = inputs)
|
||||
}
|
||||
def withOutputs(outputs: Vector[sbt.internal.worker.FilePath]): NativeRunInfo = {
|
||||
copy(outputs = outputs)
|
||||
}
|
||||
}
|
||||
object NativeRunInfo {
|
||||
|
||||
def apply(cmd: String, args: Vector[String], connectInput: Boolean, outputStrategy: Option[String], workingDirectory: Option[java.net.URI], environmentVariables: scala.collection.immutable.Map[String, String]): NativeRunInfo = new NativeRunInfo(cmd, args, connectInput, outputStrategy, workingDirectory, environmentVariables)
|
||||
def apply(cmd: String, args: Vector[String], connectInput: Boolean, outputStrategy: String, workingDirectory: java.net.URI, environmentVariables: scala.collection.immutable.Map[String, String]): NativeRunInfo = new NativeRunInfo(cmd, args, connectInput, Option(outputStrategy), Option(workingDirectory), environmentVariables)
|
||||
def apply(cmd: String, args: Vector[String], connectInput: Boolean, outputStrategy: Option[String], workingDirectory: Option[java.net.URI], environmentVariables: scala.collection.immutable.Map[String, String], inputs: Vector[sbt.internal.worker.FilePath], outputs: Vector[sbt.internal.worker.FilePath]): NativeRunInfo = new NativeRunInfo(cmd, args, connectInput, outputStrategy, workingDirectory, environmentVariables, inputs, outputs)
|
||||
def apply(cmd: String, args: Vector[String], connectInput: Boolean, outputStrategy: String, workingDirectory: java.net.URI, environmentVariables: scala.collection.immutable.Map[String, String], inputs: Vector[sbt.internal.worker.FilePath], outputs: Vector[sbt.internal.worker.FilePath]): NativeRunInfo = new NativeRunInfo(cmd, args, connectInput, Option(outputStrategy), Option(workingDirectory), environmentVariables, inputs, outputs)
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.worker
|
||||
final class RunInfo private (
|
||||
val jvm: Boolean,
|
||||
val jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo],
|
||||
val nativeRunInfo: Option[sbt.internal.worker.NativeRunInfo]) extends Serializable {
|
||||
|
||||
private def this(jvm: Boolean, jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo]) = this(jvm, jvmRunInfo, None)
|
||||
|
||||
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
|
||||
case x: RunInfo => (this.jvm == x.jvm) && (this.jvmRunInfo == x.jvmRunInfo) && (this.nativeRunInfo == x.nativeRunInfo)
|
||||
case _ => false
|
||||
})
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (17 + "sbt.internal.worker.RunInfo".##) + jvm.##) + jvmRunInfo.##) + nativeRunInfo.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"RunInfo(" + jvm + ", " + jvmRunInfo + ", " + nativeRunInfo + ")"
|
||||
}
|
||||
private[this] def copy(jvm: Boolean = jvm, jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo] = jvmRunInfo, nativeRunInfo: Option[sbt.internal.worker.NativeRunInfo] = nativeRunInfo): RunInfo = {
|
||||
new RunInfo(jvm, jvmRunInfo, nativeRunInfo)
|
||||
}
|
||||
def withJvm(jvm: Boolean): RunInfo = {
|
||||
copy(jvm = jvm)
|
||||
}
|
||||
def withJvmRunInfo(jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo]): RunInfo = {
|
||||
copy(jvmRunInfo = jvmRunInfo)
|
||||
}
|
||||
def withJvmRunInfo(jvmRunInfo: sbt.internal.worker.JvmRunInfo): RunInfo = {
|
||||
copy(jvmRunInfo = Option(jvmRunInfo))
|
||||
}
|
||||
def withNativeRunInfo(nativeRunInfo: Option[sbt.internal.worker.NativeRunInfo]): RunInfo = {
|
||||
copy(nativeRunInfo = nativeRunInfo)
|
||||
}
|
||||
def withNativeRunInfo(nativeRunInfo: sbt.internal.worker.NativeRunInfo): RunInfo = {
|
||||
copy(nativeRunInfo = Option(nativeRunInfo))
|
||||
}
|
||||
}
|
||||
object RunInfo {
|
||||
|
||||
def apply(jvm: Boolean, jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo]): RunInfo = new RunInfo(jvm, jvmRunInfo)
|
||||
def apply(jvm: Boolean, jvmRunInfo: sbt.internal.worker.JvmRunInfo): RunInfo = new RunInfo(jvm, Option(jvmRunInfo))
|
||||
def apply(jvm: Boolean, jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo], nativeRunInfo: Option[sbt.internal.worker.NativeRunInfo]): RunInfo = new RunInfo(jvm, jvmRunInfo, nativeRunInfo)
|
||||
def apply(jvm: Boolean, jvmRunInfo: sbt.internal.worker.JvmRunInfo, nativeRunInfo: sbt.internal.worker.NativeRunInfo): RunInfo = new RunInfo(jvm, Option(jvmRunInfo), Option(nativeRunInfo))
|
||||
}
|
||||
27
protocol/src/main/contraband-scala/sbt/internal/worker/codec/ClientJobParamsFormats.scala
generated
Normal file
27
protocol/src/main/contraband-scala/sbt/internal/worker/codec/ClientJobParamsFormats.scala
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.worker.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait ClientJobParamsFormats { self: sbt.internal.worker.codec.RunInfoFormats with sbt.internal.worker.codec.JvmRunInfoFormats with sbt.internal.worker.codec.FilePathFormats with sjsonnew.BasicJsonProtocol with sbt.internal.worker.codec.NativeRunInfoFormats =>
|
||||
implicit lazy val ClientJobParamsFormat: JsonFormat[sbt.internal.worker.ClientJobParams] = new JsonFormat[sbt.internal.worker.ClientJobParams] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.worker.ClientJobParams = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val runInfo = unbuilder.readField[Option[sbt.internal.worker.RunInfo]]("runInfo")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.worker.ClientJobParams(runInfo)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.worker.ClientJobParams, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("runInfo", obj.runInfo)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
29
protocol/src/main/contraband-scala/sbt/internal/worker/codec/FilePathFormats.scala
generated
Normal file
29
protocol/src/main/contraband-scala/sbt/internal/worker/codec/FilePathFormats.scala
generated
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.worker.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait FilePathFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val FilePathFormat: JsonFormat[sbt.internal.worker.FilePath] = new JsonFormat[sbt.internal.worker.FilePath] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.worker.FilePath = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val path = unbuilder.readField[java.net.URI]("path")
|
||||
val digest = unbuilder.readField[String]("digest")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.worker.FilePath(path, digest)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.worker.FilePath, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("path", obj.path)
|
||||
builder.addField("digest", obj.digest)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
13
protocol/src/main/contraband-scala/sbt/internal/worker/codec/JsonProtocol.scala
generated
Normal file
13
protocol/src/main/contraband-scala/sbt/internal/worker/codec/JsonProtocol.scala
generated
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.worker.codec
|
||||
trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
||||
with sbt.internal.worker.codec.FilePathFormats
|
||||
with sbt.internal.worker.codec.JvmRunInfoFormats
|
||||
with sbt.internal.worker.codec.NativeRunInfoFormats
|
||||
with sbt.internal.worker.codec.RunInfoFormats
|
||||
with sbt.internal.worker.codec.ClientJobParamsFormats
|
||||
object JsonProtocol extends JsonProtocol
|
||||
47
protocol/src/main/contraband-scala/sbt/internal/worker/codec/JvmRunInfoFormats.scala
generated
Normal file
47
protocol/src/main/contraband-scala/sbt/internal/worker/codec/JvmRunInfoFormats.scala
generated
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.worker.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait JvmRunInfoFormats { self: sbt.internal.worker.codec.FilePathFormats with sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val JvmRunInfoFormat: JsonFormat[sbt.internal.worker.JvmRunInfo] = new JsonFormat[sbt.internal.worker.JvmRunInfo] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.worker.JvmRunInfo = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val args = unbuilder.readField[Vector[String]]("args")
|
||||
val classpath = unbuilder.readField[Vector[sbt.internal.worker.FilePath]]("classpath")
|
||||
val mainClass = unbuilder.readField[String]("mainClass")
|
||||
val connectInput = unbuilder.readField[Boolean]("connectInput")
|
||||
val javaHome = unbuilder.readField[Option[java.net.URI]]("javaHome")
|
||||
val outputStrategy = unbuilder.readField[Option[String]]("outputStrategy")
|
||||
val workingDirectory = unbuilder.readField[Option[java.net.URI]]("workingDirectory")
|
||||
val jvmOptions = unbuilder.readField[Vector[String]]("jvmOptions")
|
||||
val environmentVariables = unbuilder.readField[scala.collection.immutable.Map[String, String]]("environmentVariables")
|
||||
val inputs = unbuilder.readField[Vector[sbt.internal.worker.FilePath]]("inputs")
|
||||
val outputs = unbuilder.readField[Vector[sbt.internal.worker.FilePath]]("outputs")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.worker.JvmRunInfo(args, classpath, mainClass, connectInput, javaHome, outputStrategy, workingDirectory, jvmOptions, environmentVariables, inputs, outputs)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.worker.JvmRunInfo, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("args", obj.args)
|
||||
builder.addField("classpath", obj.classpath)
|
||||
builder.addField("mainClass", obj.mainClass)
|
||||
builder.addField("connectInput", obj.connectInput)
|
||||
builder.addField("javaHome", obj.javaHome)
|
||||
builder.addField("outputStrategy", obj.outputStrategy)
|
||||
builder.addField("workingDirectory", obj.workingDirectory)
|
||||
builder.addField("jvmOptions", obj.jvmOptions)
|
||||
builder.addField("environmentVariables", obj.environmentVariables)
|
||||
builder.addField("inputs", obj.inputs)
|
||||
builder.addField("outputs", obj.outputs)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
41
protocol/src/main/contraband-scala/sbt/internal/worker/codec/NativeRunInfoFormats.scala
generated
Normal file
41
protocol/src/main/contraband-scala/sbt/internal/worker/codec/NativeRunInfoFormats.scala
generated
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.worker.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait NativeRunInfoFormats { self: sbt.internal.worker.codec.FilePathFormats with sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val NativeRunInfoFormat: JsonFormat[sbt.internal.worker.NativeRunInfo] = new JsonFormat[sbt.internal.worker.NativeRunInfo] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.worker.NativeRunInfo = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val cmd = unbuilder.readField[String]("cmd")
|
||||
val args = unbuilder.readField[Vector[String]]("args")
|
||||
val connectInput = unbuilder.readField[Boolean]("connectInput")
|
||||
val outputStrategy = unbuilder.readField[Option[String]]("outputStrategy")
|
||||
val workingDirectory = unbuilder.readField[Option[java.net.URI]]("workingDirectory")
|
||||
val environmentVariables = unbuilder.readField[scala.collection.immutable.Map[String, String]]("environmentVariables")
|
||||
val inputs = unbuilder.readField[Vector[sbt.internal.worker.FilePath]]("inputs")
|
||||
val outputs = unbuilder.readField[Vector[sbt.internal.worker.FilePath]]("outputs")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.worker.NativeRunInfo(cmd, args, connectInput, outputStrategy, workingDirectory, environmentVariables, inputs, outputs)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.worker.NativeRunInfo, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("cmd", obj.cmd)
|
||||
builder.addField("args", obj.args)
|
||||
builder.addField("connectInput", obj.connectInput)
|
||||
builder.addField("outputStrategy", obj.outputStrategy)
|
||||
builder.addField("workingDirectory", obj.workingDirectory)
|
||||
builder.addField("environmentVariables", obj.environmentVariables)
|
||||
builder.addField("inputs", obj.inputs)
|
||||
builder.addField("outputs", obj.outputs)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
31
protocol/src/main/contraband-scala/sbt/internal/worker/codec/RunInfoFormats.scala
generated
Normal file
31
protocol/src/main/contraband-scala/sbt/internal/worker/codec/RunInfoFormats.scala
generated
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.worker.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait RunInfoFormats { self: sbt.internal.worker.codec.JvmRunInfoFormats with sbt.internal.worker.codec.FilePathFormats with sjsonnew.BasicJsonProtocol with sbt.internal.worker.codec.NativeRunInfoFormats =>
|
||||
implicit lazy val RunInfoFormat: JsonFormat[sbt.internal.worker.RunInfo] = new JsonFormat[sbt.internal.worker.RunInfo] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.worker.RunInfo = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val jvm = unbuilder.readField[Boolean]("jvm")
|
||||
val jvmRunInfo = unbuilder.readField[Option[sbt.internal.worker.JvmRunInfo]]("jvmRunInfo")
|
||||
val nativeRunInfo = unbuilder.readField[Option[sbt.internal.worker.NativeRunInfo]]("nativeRunInfo")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.worker.RunInfo(jvm, jvmRunInfo, nativeRunInfo)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.worker.RunInfo, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("jvm", obj.jvm)
|
||||
builder.addField("jvmRunInfo", obj.jvmRunInfo)
|
||||
builder.addField("nativeRunInfo", obj.nativeRunInfo)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,22 +7,24 @@ package sbt.protocol
|
|||
final class InitCommand private (
|
||||
val token: Option[String],
|
||||
val execId: Option[String],
|
||||
val skipAnalysis: Option[Boolean]) extends sbt.protocol.CommandMessage() with Serializable {
|
||||
val skipAnalysis: Option[Boolean],
|
||||
val initializationOptions: Option[sbt.internal.protocol.InitializeOption]) extends sbt.protocol.CommandMessage() with Serializable {
|
||||
|
||||
private def this(token: Option[String], execId: Option[String]) = this(token, execId, None)
|
||||
private def this(token: Option[String], execId: Option[String]) = this(token, execId, None, None)
|
||||
private def this(token: Option[String], execId: Option[String], skipAnalysis: Option[Boolean]) = this(token, execId, skipAnalysis, None)
|
||||
|
||||
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
|
||||
case x: InitCommand => (this.token == x.token) && (this.execId == x.execId) && (this.skipAnalysis == x.skipAnalysis)
|
||||
case x: InitCommand => (this.token == x.token) && (this.execId == x.execId) && (this.skipAnalysis == x.skipAnalysis) && (this.initializationOptions == x.initializationOptions)
|
||||
case _ => false
|
||||
})
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (17 + "sbt.protocol.InitCommand".##) + token.##) + execId.##) + skipAnalysis.##)
|
||||
37 * (37 * (37 * (37 * (37 * (17 + "sbt.protocol.InitCommand".##) + token.##) + execId.##) + skipAnalysis.##) + initializationOptions.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"InitCommand(" + token + ", " + execId + ", " + skipAnalysis + ")"
|
||||
"InitCommand(" + token + ", " + execId + ", " + skipAnalysis + ", " + initializationOptions + ")"
|
||||
}
|
||||
private[this] def copy(token: Option[String] = token, execId: Option[String] = execId, skipAnalysis: Option[Boolean] = skipAnalysis): InitCommand = {
|
||||
new InitCommand(token, execId, skipAnalysis)
|
||||
private[this] def copy(token: Option[String] = token, execId: Option[String] = execId, skipAnalysis: Option[Boolean] = skipAnalysis, initializationOptions: Option[sbt.internal.protocol.InitializeOption] = initializationOptions): InitCommand = {
|
||||
new InitCommand(token, execId, skipAnalysis, initializationOptions)
|
||||
}
|
||||
def withToken(token: Option[String]): InitCommand = {
|
||||
copy(token = token)
|
||||
|
|
@ -42,6 +44,12 @@ final class InitCommand private (
|
|||
def withSkipAnalysis(skipAnalysis: Boolean): InitCommand = {
|
||||
copy(skipAnalysis = Option(skipAnalysis))
|
||||
}
|
||||
def withInitializationOptions(initializationOptions: Option[sbt.internal.protocol.InitializeOption]): InitCommand = {
|
||||
copy(initializationOptions = initializationOptions)
|
||||
}
|
||||
def withInitializationOptions(initializationOptions: sbt.internal.protocol.InitializeOption): InitCommand = {
|
||||
copy(initializationOptions = Option(initializationOptions))
|
||||
}
|
||||
}
|
||||
object InitCommand {
|
||||
|
||||
|
|
@ -49,4 +57,6 @@ object InitCommand {
|
|||
def apply(token: String, execId: String): InitCommand = new InitCommand(Option(token), Option(execId))
|
||||
def apply(token: Option[String], execId: Option[String], skipAnalysis: Option[Boolean]): InitCommand = new InitCommand(token, execId, skipAnalysis)
|
||||
def apply(token: String, execId: String, skipAnalysis: Boolean): InitCommand = new InitCommand(Option(token), Option(execId), Option(skipAnalysis))
|
||||
def apply(token: Option[String], execId: Option[String], skipAnalysis: Option[Boolean], initializationOptions: Option[sbt.internal.protocol.InitializeOption]): InitCommand = new InitCommand(token, execId, skipAnalysis, initializationOptions)
|
||||
def apply(token: String, execId: String, skipAnalysis: Boolean, initializationOptions: sbt.internal.protocol.InitializeOption): InitCommand = new InitCommand(Option(token), Option(execId), Option(skipAnalysis), Option(initializationOptions))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@
|
|||
package sbt.protocol.codec
|
||||
|
||||
import _root_.sjsonnew.JsonFormat
|
||||
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.InitCommandFormats with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats with sbt.protocol.codec.AttachFormats with sbt.protocol.codec.TerminalCapabilitiesQueryFormats with sbt.protocol.codec.TerminalSetAttributesCommandFormats with sbt.protocol.codec.TerminalAttributesQueryFormats with sbt.protocol.codec.TerminalGetSizeQueryFormats with sbt.protocol.codec.TerminalSetSizeCommandFormats with sbt.protocol.codec.TerminalSetEchoCommandFormats with sbt.protocol.codec.TerminalSetRawModeCommandFormats =>
|
||||
trait CommandMessageFormats { self: sbt.internal.protocol.codec.InitializeOptionFormats with sjsonnew.BasicJsonProtocol with sbt.protocol.codec.InitCommandFormats with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats with sbt.protocol.codec.AttachFormats with sbt.protocol.codec.TerminalCapabilitiesQueryFormats with sbt.protocol.codec.TerminalSetAttributesCommandFormats with sbt.protocol.codec.TerminalAttributesQueryFormats with sbt.protocol.codec.TerminalGetSizeQueryFormats with sbt.protocol.codec.TerminalSetSizeCommandFormats with sbt.protocol.codec.TerminalSetEchoCommandFormats with sbt.protocol.codec.TerminalSetRawModeCommandFormats =>
|
||||
implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat11[sbt.protocol.CommandMessage, sbt.protocol.InitCommand, sbt.protocol.ExecCommand, sbt.protocol.SettingQuery, sbt.protocol.Attach, sbt.protocol.TerminalCapabilitiesQuery, sbt.protocol.TerminalSetAttributesCommand, sbt.protocol.TerminalAttributesQuery, sbt.protocol.TerminalGetSizeQuery, sbt.protocol.TerminalSetSizeCommand, sbt.protocol.TerminalSetEchoCommand, sbt.protocol.TerminalSetRawModeCommand]("type")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait InitCommandFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
trait InitCommandFormats { self: sbt.internal.protocol.codec.InitializeOptionFormats with sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val InitCommandFormat: JsonFormat[sbt.protocol.InitCommand] = new JsonFormat[sbt.protocol.InitCommand] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.InitCommand = {
|
||||
__jsOpt match {
|
||||
|
|
@ -14,8 +14,9 @@ implicit lazy val InitCommandFormat: JsonFormat[sbt.protocol.InitCommand] = new
|
|||
val token = unbuilder.readField[Option[String]]("token")
|
||||
val execId = unbuilder.readField[Option[String]]("execId")
|
||||
val skipAnalysis = unbuilder.readField[Option[Boolean]]("skipAnalysis")
|
||||
val initializationOptions = unbuilder.readField[Option[sbt.internal.protocol.InitializeOption]]("initializationOptions")
|
||||
unbuilder.endObject()
|
||||
sbt.protocol.InitCommand(token, execId, skipAnalysis)
|
||||
sbt.protocol.InitCommand(token, execId, skipAnalysis, initializationOptions)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
|
|
@ -25,6 +26,7 @@ implicit lazy val InitCommandFormat: JsonFormat[sbt.protocol.InitCommand] = new
|
|||
builder.addField("token", obj.token)
|
||||
builder.addField("execId", obj.execId)
|
||||
builder.addField("skipAnalysis", obj.skipAnalysis)
|
||||
builder.addField("initializationOptions", obj.initializationOptions)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.protocol.codec
|
||||
trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
||||
trait JsonProtocol extends sbt.internal.protocol.codec.InitializeOptionFormats
|
||||
with sjsonnew.BasicJsonProtocol
|
||||
with sbt.protocol.codec.InitCommandFormats
|
||||
with sbt.protocol.codec.ExecCommandFormats
|
||||
with sbt.protocol.codec.SettingQueryFormats
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ type TokenFile {
|
|||
token: String!
|
||||
}
|
||||
|
||||
## Passed into InitializeParams as part of "initialize" request as the user-defined option.
|
||||
## https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize
|
||||
type InitializeOption {
|
||||
token: String
|
||||
skipAnalysis: Boolean @since("1.4.0")
|
||||
canWork: Boolean @since("1.10.8")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ type InitCommand implements CommandMessage {
|
|||
token: String
|
||||
execId: String
|
||||
skipAnalysis: Boolean @since("1.4.0")
|
||||
initializationOptions: sbt.internal.protocol.InitializeOption @since("1.10.8")
|
||||
}
|
||||
|
||||
## Command to execute sbt command.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
package sbt.internal.worker
|
||||
@target(Scala)
|
||||
@codecPackage("sbt.internal.worker.codec")
|
||||
@fullCodec("JsonProtocol")
|
||||
|
||||
type FilePath {
|
||||
path: java.net.URI!
|
||||
digest: String!
|
||||
}
|
||||
|
||||
type JvmRunInfo {
|
||||
args: [String],
|
||||
classpath: [sbt.internal.worker.FilePath],
|
||||
mainClass: String!
|
||||
connectInput: Boolean!
|
||||
javaHome: java.net.URI
|
||||
outputStrategy: String
|
||||
workingDirectory: java.net.URI
|
||||
jvmOptions: [String]
|
||||
environmentVariables: StringStringMap!
|
||||
inputs: [sbt.internal.worker.FilePath] @since("0.1.0"),
|
||||
outputs: [sbt.internal.worker.FilePath] @since("0.1.0"),
|
||||
}
|
||||
|
||||
type NativeRunInfo {
|
||||
cmd: String!,
|
||||
args: [String],
|
||||
connectInput: Boolean!
|
||||
outputStrategy: String
|
||||
workingDirectory: java.net.URI
|
||||
environmentVariables: StringStringMap!
|
||||
inputs: [sbt.internal.worker.FilePath] @since("0.1.0"),
|
||||
outputs: [sbt.internal.worker.FilePath] @since("0.1.0"),
|
||||
}
|
||||
|
||||
type RunInfo {
|
||||
jvm: Boolean!
|
||||
jvmRunInfo: sbt.internal.worker.JvmRunInfo,
|
||||
nativeRunInfo: sbt.internal.worker.NativeRunInfo @since("0.1.0"),
|
||||
}
|
||||
|
||||
## Client-side job support.
|
||||
##
|
||||
## Notification: sbt/clientJob
|
||||
##
|
||||
## Parameter for the sbt/clientJob notification.
|
||||
## A client-side job represents a unit of work that sbt server
|
||||
## can outsourse back to the client, for example for run task.
|
||||
type ClientJobParams {
|
||||
runInfo: sbt.internal.worker.RunInfo
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ object Serialization {
|
|||
private[sbt] val VsCode = "application/vscode-jsonrpc; charset=utf-8"
|
||||
val readSystemIn = "sbt/readSystemIn"
|
||||
val cancelReadSystemIn = "sbt/cancelReadSystemIn"
|
||||
val clientJob = "sbt/clientJob"
|
||||
val systemIn = "sbt/systemIn"
|
||||
val systemOut = "sbt/systemOut"
|
||||
val systemErr = "sbt/systemErr"
|
||||
|
|
@ -67,15 +68,10 @@ object Serialization {
|
|||
command match {
|
||||
case x: InitCommand =>
|
||||
val execId = x.execId.getOrElse(UUID.randomUUID.toString)
|
||||
val analysis = s""""skipAnalysis" : ${x.skipAnalysis.getOrElse(false)}"""
|
||||
val opt = x.token match {
|
||||
case Some(t) =>
|
||||
val json: JValue = Converter.toJson[String](t).get
|
||||
val v = CompactPrinter(json)
|
||||
s"""{ "token": $v, $analysis }"""
|
||||
case None => s"{ $analysis }"
|
||||
}
|
||||
s"""{ "jsonrpc": "2.0", "id": "$execId", "method": "initialize", "params": { "initializationOptions": $opt } }"""
|
||||
val opts = x.initializationOptions.getOrElse(sys.error("expected initializationOptions"))
|
||||
import sbt.protocol.codec.JsonProtocol._
|
||||
val optsJson = CompactPrinter(Converter.toJson(opts).get)
|
||||
s"""{ "jsonrpc": "2.0", "id": "$execId", "method": "initialize", "params": { "initializationOptions": $optsJson } }"""
|
||||
case x: ExecCommand =>
|
||||
val execId = x.execId.getOrElse(UUID.randomUUID.toString)
|
||||
val json: JValue = Converter.toJson[String](x.commandLine).get
|
||||
|
|
|
|||
|
|
@ -33,15 +33,8 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) {
|
|||
* It is configured according to `config`.
|
||||
* If `runnerClass` is defined for this Fork instance, it is prepended to `arguments` to define the arguments passed to the forked command.
|
||||
*/
|
||||
def apply(config: ForkOptions, arguments: Seq[String]): Int = {
|
||||
val p = fork(config, arguments)
|
||||
RunningProcesses.add(p)
|
||||
try p.exitValue()
|
||||
finally {
|
||||
if (p.isAlive()) p.destroy()
|
||||
RunningProcesses.remove(p)
|
||||
}
|
||||
}
|
||||
def apply(config: ForkOptions, arguments: Seq[String]): Int =
|
||||
Fork.blockForExitCode(fork(config, arguments))
|
||||
|
||||
/**
|
||||
* Forks the configured process and returns a `Process` that can be used to wait for completion or to terminate the forked process.
|
||||
|
|
@ -50,37 +43,22 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) {
|
|||
* If `runnerClass` is defined for this Fork instance, it is prepended to `arguments` to define the arguments passed to the forked command.
|
||||
*/
|
||||
def fork(config: ForkOptions, arguments: Seq[String]): Process = {
|
||||
import config.{ envVars => env, _ }
|
||||
import config._
|
||||
val executable = Fork.javaCommand(javaHome, commandName).getAbsolutePath
|
||||
val preOptions = makeOptions(runJVMOptions, bootJars, arguments)
|
||||
val (classpathEnv, options) = Fork.fitClasspath(preOptions)
|
||||
val command = executable +: options
|
||||
|
||||
val environment: List[(String, String)] = env.toList ++
|
||||
(classpathEnv map { value =>
|
||||
Fork.ClasspathEnvKey -> value
|
||||
})
|
||||
val jpb =
|
||||
if (Fork.shouldUseArgumentsFile(options))
|
||||
new JProcessBuilder(executable, Fork.createArgumentsFile(options))
|
||||
else
|
||||
new JProcessBuilder(command.toArray: _*)
|
||||
workingDirectory foreach (jpb directory _)
|
||||
environment foreach { case (k, v) => jpb.environment.put(k, v) }
|
||||
if (connectInput) {
|
||||
jpb.redirectInput(Redirect.INHERIT)
|
||||
()
|
||||
}
|
||||
val process = Process(jpb)
|
||||
|
||||
outputStrategy.getOrElse(StdoutOutput: OutputStrategy) match {
|
||||
case StdoutOutput => process.run(connectInput = false)
|
||||
case out: BufferedOutput =>
|
||||
out.logger.buffer { process.run(out.logger, connectInput = false) }
|
||||
case out: LoggedOutput => process.run(out.logger, connectInput = false)
|
||||
case out: CustomOutput => (process #> out.output).run(connectInput = false)
|
||||
val extraEnv = classpathEnv.toList.map { value =>
|
||||
Fork.ClasspathEnvKey -> value
|
||||
}
|
||||
Fork.forkInternal(config, extraEnv, jpb)
|
||||
}
|
||||
|
||||
private[this] def makeOptions(
|
||||
jvmOptions: Seq[String],
|
||||
bootJars: Iterable[File],
|
||||
|
|
@ -185,4 +163,36 @@ object Fork {
|
|||
pw.close()
|
||||
s"@${file.getAbsolutePath}"
|
||||
}
|
||||
|
||||
private[sbt] def forkInternal(
|
||||
config: ForkOptions,
|
||||
extraEnv: List[(String, String)],
|
||||
jpb: JProcessBuilder
|
||||
): Process = {
|
||||
import config.{ envVars => env, _ }
|
||||
val environment: List[(String, String)] = env.toList ++ extraEnv
|
||||
workingDirectory.foreach(jpb directory _)
|
||||
environment.foreach { case (k, v) => jpb.environment.put(k, v) }
|
||||
if (connectInput) {
|
||||
jpb.redirectInput(Redirect.INHERIT)
|
||||
()
|
||||
}
|
||||
val process = Process(jpb)
|
||||
outputStrategy.getOrElse(StdoutOutput: OutputStrategy) match {
|
||||
case StdoutOutput => process.run(connectInput = false)
|
||||
case out: BufferedOutput =>
|
||||
out.logger.buffer { process.run(out.logger, connectInput = false) }
|
||||
case out: LoggedOutput => process.run(out.logger, connectInput = false)
|
||||
case out: CustomOutput => (process #> out.output).run(connectInput = false)
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] def blockForExitCode(p: Process): Int = {
|
||||
RunningProcesses.add(p)
|
||||
try p.exitValue()
|
||||
finally {
|
||||
if (p.isAlive()) p.destroy()
|
||||
RunningProcesses.remove(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,25 +26,16 @@ sealed trait ScalaRun {
|
|||
}
|
||||
class ForkRun(config: ForkOptions) extends ScalaRun {
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Try[Unit] = {
|
||||
def processExitCode(exitCode: Int, label: String): Try[Unit] =
|
||||
if (exitCode == 0) Success(())
|
||||
else
|
||||
Failure(
|
||||
new MessageOnlyException(
|
||||
s"""Nonzero exit code returned from $label: $exitCode""".stripMargin
|
||||
)
|
||||
)
|
||||
|
||||
log.info(s"running (fork) $mainClass ${Run.runOptionsStr(options)}")
|
||||
val c = configLogged(log)
|
||||
val scalaOpts = scalaOptions(mainClass, classpath, options)
|
||||
val exitCode = try Fork.java(c, scalaOpts)
|
||||
catch {
|
||||
case _: InterruptedException =>
|
||||
log.warn("Run canceled.")
|
||||
log.warn("run canceled")
|
||||
1
|
||||
}
|
||||
processExitCode(exitCode, "runner")
|
||||
Run.processExitCode(exitCode, "runner")
|
||||
}
|
||||
|
||||
def fork(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Process = {
|
||||
|
|
@ -195,4 +186,13 @@ object Run {
|
|||
case str if str.contains(" ") => "\"" + str + "\""
|
||||
case str => str
|
||||
}).mkString(" ")
|
||||
|
||||
private[sbt] def processExitCode(exitCode: Int, label: String): Try[Unit] =
|
||||
if (exitCode == 0) Success(())
|
||||
else
|
||||
Failure(
|
||||
new MessageOnlyException(
|
||||
s"""nonzero exit code returned from $label: $exitCode""".stripMargin
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
scalaVersion := "3.6.3"
|
||||
|
||||
TaskKey[Unit]("willSucceed") := println("success")
|
||||
|
||||
TaskKey[Unit]("willFail") := { throw new Exception("failed") }
|
||||
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test"
|
||||
libraryDependencies += "org.scalameta" %% "munit" % "1.0.4" % Test
|
||||
|
||||
TaskKey[Unit]("fooBar") := { () }
|
||||
|
|
|
|||
|
|
@ -1 +1,3 @@
|
|||
object A
|
||||
class A
|
||||
|
||||
@main def hello() = println("Hello, World!")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
package test.pkg
|
||||
|
||||
class FooSpec extends org.scalatest.FlatSpec
|
||||
class FooSpec extends munit.FunSuite
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ object ClientTest extends AbstractServerTest {
|
|||
case r => r
|
||||
}
|
||||
}
|
||||
private def client(args: String*): Int = {
|
||||
private def client(args: String*): Int =
|
||||
background(
|
||||
NetworkClient.client(
|
||||
testPath.toFile,
|
||||
|
|
@ -68,6 +68,19 @@ object ClientTest extends AbstractServerTest {
|
|||
false
|
||||
)
|
||||
)
|
||||
private def clientWithStdoutLines(args: String*): (Int, Seq[String]) = {
|
||||
val out = new CachingPrintStream
|
||||
val exitCode = background(
|
||||
NetworkClient.client(
|
||||
testPath.toFile,
|
||||
args.toArray,
|
||||
NullInputStream,
|
||||
out,
|
||||
NullPrintStream,
|
||||
false
|
||||
)
|
||||
)
|
||||
(exitCode, out.lines)
|
||||
}
|
||||
// This ensures that the completion command will send a tab that triggers
|
||||
// sbt to call definedTestNames or discoveredMainClasses if there hasn't
|
||||
|
|
@ -107,6 +120,11 @@ object ClientTest extends AbstractServerTest {
|
|||
test("three commands with middle failure") { _ =>
|
||||
assert(client("compile;willFail;willSucceed") == 1)
|
||||
}
|
||||
test("run") { _ =>
|
||||
val (exitCode, lines) = clientWithStdoutLines("run")
|
||||
assert(exitCode == 0)
|
||||
assert(lines.toList.exists(_.endsWith("Hello, World!")))
|
||||
}
|
||||
test("compi completions") { _ =>
|
||||
val expected = Vector(
|
||||
"compile",
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ case class TestServer(
|
|||
|
||||
// initiate handshake
|
||||
sendJsonRpc(
|
||||
s"""{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { "skipAnalysis": true } } }"""
|
||||
s"""{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { "skipAnalysis": true, "canWork": true } } }"""
|
||||
)
|
||||
|
||||
def test(f: TestServer => Future[Assertion]): Future[Assertion] = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue