Merge branch '1.10.x' into wip/merge-1.10.x

This commit is contained in:
Eugene Yokota 2025-03-08 19:05:30 -05:00
commit 2c3c0f4a7c
46 changed files with 935 additions and 152 deletions

View File

@ -728,7 +728,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, utilCache)
.dependsOn(protocolProj, completeProj, utilLogging, runProj, utilCache)
.settings(
testedBaseSettings,
name := "Command",

View File

@ -121,7 +121,7 @@ val root = (project in file(".")).
file
},
// update sbt.sh at root
sbtnVersion := "1.10.5",
sbtnVersion := "1.10.8",
sbtnJarsBaseUrl := "https://github.com/sbt/sbtn-dist/releases/download",
sbtnJarsMappings := {
val baseUrl = sbtnJarsBaseUrl.value

View File

@ -476,6 +476,11 @@ if "%~0" == "new" (
set sbt_new=true
)
)
if "%~0" == "init" (
if not defined SBT_ARGS (
set sbt_new=true
)
)
if "%g:~0,2%" == "-D" (
rem special handling for -D since '=' gets parsed away

View File

@ -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 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

View File

@ -13,7 +13,7 @@ package client
import java.io.{ File, IOException, InputStream, PrintStream }
import java.lang.ProcessBuilder.Redirect
import java.net.{ Socket, SocketException }
import java.nio.file.Files
import java.nio.file.{ Files, Paths }
import java.util.UUID
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
import java.util.concurrent.{ ConcurrentHashMap, LinkedBlockingQueue, TimeUnit }
@ -21,8 +21,16 @@ import java.text.DateFormat
import sbt.BasicCommandStrings.{ DashDashDetachStdio, DashDashServer, Shutdown, TerminateAction }
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.*
@ -41,6 +49,7 @@ import Serialization.{
attach,
cancelReadSystemIn,
cancelRequest,
clientJob,
promptChannel,
readSystemIn,
systemIn,
@ -61,6 +70,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
@ -141,6 +151,7 @@ class NetworkClient(
private lazy val noTab = arguments.completionArguments.contains("--no-tab")
private lazy val noStdErr = arguments.completionArguments.contains("--no-stderr") &&
!sys.env.contains("SBTN_AUTO_COMPLETE") && !sys.env.contains("SBTC_AUTO_COMPLETE")
private def shutdownOnly = arguments.commandArguments == Seq(Shutdown)
private def mkSocket(file: File): (Socket, Option[String]) = ClientSocket.socket(file, useJNI)
@ -164,6 +175,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,
@ -171,7 +187,10 @@ class NetworkClient(
): (Socket, Option[String]) =
try {
if (!portfile.exists) {
if (promptCompleteUsers) {
if (shutdownOnly) {
console.appendLog(Level.Info, "no sbt server is running. ciao")
System.exit(0)
} else if (promptCompleteUsers) {
val msg = if (noTab) "" else "No sbt server is running. Press <tab> to start one..."
errorStream.print(s"\n$msg")
if (noStdErr) System.exit(0)
@ -294,7 +313,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 +671,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 +723,59 @@ 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(Paths.get),
options = info.args,
log = log
)
}
def nativeRun(info: NativeRunInfo): Try[Unit] = {
import java.lang.{ ProcessBuilder as 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 {

View File

@ -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

View File

@ -28,7 +28,7 @@ private[sbt] trait UITask extends Runnable with AutoCloseable {
private[sbt] def reader: UITask.Reader
private 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 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
@ -77,16 +91,9 @@ private[sbt] object UITask {
this.synchronized(this.wait())
Right("") // should be unreachable
// JLine returns null on ctrl+d when there is no other input. This interprets
// ctrl+d with no input 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)
}
// ctrl+d with no imput as an exit
case None => Left(TerminateAction)
case Some(s: String) => splitCommand(s.trim())
}
}
terminal.setPrompt(Prompt.Pending)

View File

@ -71,6 +71,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,

View File

@ -52,6 +52,7 @@ import sbt.internal.server.{
BspCompileTask,
BuildServerProtocol,
BuildServerReporter,
ClientJob,
Definition,
LanguageServerProtocol,
ServerHandler,
@ -240,7 +241,7 @@ object Defaults extends BuildCommon {
getRootPaths(out, app) + ("CSR_CACHE" -> coursierCache)
},
fileConverter := MappedFileConverter(rootPaths.value, allowMachinePath.value)
) ++ BuildServerProtocol.globalSettings
) ++ BuildServerProtocol.globalSettings ++ ClientJob.globalSettings
private[sbt] def getRootPaths(out: NioPath, app: AppConfiguration): ListMap[String, NioPath] =
val base = app.baseDirectory.getCanonicalFile.toPath
@ -2648,7 +2649,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

View File

@ -29,6 +29,7 @@ import sbt.internal.server.BuildServerProtocol.BspFullWorkspace
import sbt.internal.server.{ BspCompileTask, BuildServerReporter, ServerHandler }
import sbt.internal.util.{ AttributeKey, ProgressState, SourcePosition }
import sbt.internal.util.StringAttributeKey
import sbt.internal.worker.ClientJobParams
import sbt.io.*
import sbt.librarymanagement.Configurations.CompilerPlugin
import sbt.librarymanagement.LibraryManagementCodec.*
@ -483,6 +484,8 @@ object Keys {
@cacheLevel(include = Array.empty)
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 csrCacheDirectory = settingKey[File]("Coursier cache directory. Uses -Dsbt.coursier.home or Coursier's default.").withRank(CSetting)
val csrMavenProfiles = settingKey[Set[String]]("").withRank(CSetting)

View File

@ -146,6 +146,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,
@ -155,8 +165,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))

View File

@ -13,10 +13,13 @@ import sbt.internal.bsp.*
import sbt.librarymanagement.Configuration
import sbt.util.InterfaceUtil
import sjsonnew.support.scalajson.unsafe.Converter
import xsbti.compile.{ CompileAnalysis, Inputs }
import xsbti.{ CompileFailed, Problem, Severity }
import xsbti.CompileFailed
import xsbti.Problem
import xsbti.Severity
import xsbti.compile.CompileAnalysis
import xsbti.compile.Inputs
object BspCompileTask {
object BspCompileTask:
def start(
targetId: BuildTargetIdentifier,
project: ProjectRef,
@ -29,7 +32,7 @@ object BspCompileTask {
task.notifyStart()
task
}
}
end BspCompileTask
case class BspCompileTask private (
targetId: BuildTargetIdentifier,

View File

@ -264,7 +264,7 @@ object BuildServerProtocol {
val result = ScalaMainClassesResult(successfulItems.toVector, None)
state.value.respondEvent(result)
}.evaluated,
bspScalaMainClasses / aggregate := false
bspScalaMainClasses / aggregate := false,
)
// This will be scoped to Compile, Test, IntegrationTest etc
@ -357,7 +357,7 @@ object BuildServerProtocol {
} else {
new BuildServerForwarder(meta, logger, underlying)
}
}
},
)
private[sbt] object Method {
final val Initialize = "build/initialize"

View File

@ -0,0 +1,95 @@
/*
* 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.Def.*
import sbt.Keys.*
import sbt.UpperStateOps.*
import sbt.internal.util.complete.Parser
import sbt.internal.worker.{ ClientJobParams, FilePath, JvmRunInfo, RunInfo }
import sbt.io.IO
import sbt.protocol.Serialization
import sbt.Keys.fileConverter
/**
* 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.rescope(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 conv = fileConverter.value
val cp = service.copyClasspath(
exportedProductJars.value,
fullClasspathAsJars.value,
workingDir,
conv,
)
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(conv.toPath(x.data).toFile)).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
}
}

View File

@ -64,6 +64,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))

View File

@ -25,6 +25,7 @@ import sbt.BasicCommandStrings.{ Shutdown, TerminateAction }
import sbt.ProjectExtra.extract
import sbt.internal.langserver.{ CancelRequestParams, ErrorCodes, LogMessageParams, MessageType }
import sbt.internal.protocol.{
InitializeOption,
JsonRpcNotificationMessage,
JsonRpcRequestMessage,
JsonRpcResponseError,
@ -85,6 +86,11 @@ 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 val inputBuffer = new LinkedBlockingQueue[Int]()
@ -126,7 +132,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)
@ -135,6 +141,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 =
@ -143,6 +151,15 @@ final class NetworkChannel(
self.onCancellationRequest(execId, crp)
}
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
@ -374,40 +391,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 =>

View File

@ -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 def copy(token: Option[String] = token, skipAnalysis: Option[Boolean] = skipAnalysis): InitializeOption = {
new InitializeOption(token, skipAnalysis)
private 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))
}

View File

@ -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()
}
}

View File

@ -0,0 +1,45 @@
/**
* This code is generated using [[https://www.scala-sbt.org/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 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))
}

View File

@ -0,0 +1,36 @@
/**
* This code is generated using [[https://www.scala-sbt.org/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 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)
}

View File

@ -0,0 +1,84 @@
/**
* This code is generated using [[https://www.scala-sbt.org/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 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)
}

View File

@ -0,0 +1,69 @@
/**
* This code is generated using [[https://www.scala-sbt.org/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 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)
}

View File

@ -0,0 +1,49 @@
/**
* This code is generated using [[https://www.scala-sbt.org/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 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))
}

View File

@ -0,0 +1,27 @@
/**
* This code is generated using [[https://www.scala-sbt.org/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 & sbt.internal.worker.codec.JvmRunInfoFormats & sbt.internal.worker.codec.FilePathFormats & sjsonnew.BasicJsonProtocol & 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()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[https://www.scala-sbt.org/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()
}
}
}

View File

@ -0,0 +1,13 @@
/**
* This code is generated using [[https://www.scala-sbt.org/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

View File

@ -0,0 +1,47 @@
/**
* This code is generated using [[https://www.scala-sbt.org/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 & 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()
}
}
}

View File

@ -0,0 +1,41 @@
/**
* This code is generated using [[https://www.scala-sbt.org/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 & 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()
}
}
}

View File

@ -0,0 +1,31 @@
/**
* This code is generated using [[https://www.scala-sbt.org/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 & sbt.internal.worker.codec.FilePathFormats & sjsonnew.BasicJsonProtocol & 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()
}
}
}

View File

@ -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 def copy(token: Option[String] = token, execId: Option[String] = execId, skipAnalysis: Option[Boolean] = skipAnalysis): InitCommand = {
new InitCommand(token, execId, skipAnalysis)
private 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))
}

View File

@ -6,6 +6,6 @@
package sbt.protocol.codec
import _root_.sjsonnew.JsonFormat
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol & sbt.protocol.codec.InitCommandFormats & sbt.protocol.codec.ExecCommandFormats & sbt.protocol.codec.SettingQueryFormats & sbt.protocol.codec.AttachFormats & sbt.protocol.codec.TerminalCapabilitiesQueryFormats & sbt.protocol.codec.TerminalSetAttributesCommandFormats & sbt.protocol.codec.TerminalAttributesQueryFormats & sbt.protocol.codec.TerminalGetSizeQueryFormats & sbt.protocol.codec.TerminalSetSizeCommandFormats & sbt.protocol.codec.TerminalSetEchoCommandFormats & sbt.protocol.codec.TerminalSetRawModeCommandFormats =>
trait CommandMessageFormats { self: sbt.internal.protocol.codec.InitializeOptionFormats & sjsonnew.BasicJsonProtocol & sbt.protocol.codec.InitCommandFormats & sbt.protocol.codec.ExecCommandFormats & sbt.protocol.codec.SettingQueryFormats & sbt.protocol.codec.AttachFormats & sbt.protocol.codec.TerminalCapabilitiesQueryFormats & sbt.protocol.codec.TerminalSetAttributesCommandFormats & sbt.protocol.codec.TerminalAttributesQueryFormats & sbt.protocol.codec.TerminalGetSizeQueryFormats & sbt.protocol.codec.TerminalSetSizeCommandFormats & sbt.protocol.codec.TerminalSetEchoCommandFormats & 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")
}

View File

@ -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 & 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()
}
}

View File

@ -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

View File

@ -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")
}

View File

@ -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.

View File

@ -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
}

View File

@ -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

View File

@ -35,15 +35,8 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) {
* `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
@ -58,30 +51,17 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) {
val preOptions = makeOptions(runJVMOptions, bootJars, arguments)
val (classpathEnv, options) = Fork.fitClasspath(preOptions)
val command = executable +: options
val environment: List[(String, String)] = envVars.toList ++
classpathEnv.map(value => Fork.ClasspathEnvKey -> value)
val jpb =
if (Fork.shouldUseArgumentsFile(options))
new JProcessBuilder(executable, Fork.createArgumentsFile(options))
else
new JProcessBuilder(command*)
workingDirectory.foreach(jpb.directory(_))
environment.foreach { (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)
new JProcessBuilder(command.toArray*)
val extraEnv = classpathEnv.toList.map { value =>
Fork.ClasspathEnvKey -> value
}
Fork.forkInternal(config, extraEnv, jpb)
}
private def makeOptions(
jvmOptions: Seq[String],
bootJars: Iterable[File],
@ -190,4 +170,36 @@ object Fork {
pw.close()
s"@${file.getAbsolutePath}"
}
private[sbt] def forkInternal(
config: ForkOptions,
extraEnv: List[(String, String)],
jpb: JProcessBuilder
): Process = {
import config.{ envVars as 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)
}
}
}

View File

@ -32,15 +32,6 @@ class ForkRun(config: ForkOptions) extends ScalaRun {
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)
@ -48,10 +39,10 @@ class ForkRun(config: ForkOptions) extends ScalaRun {
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(
@ -70,7 +61,7 @@ class ForkRun(config: ForkOptions) extends ScalaRun {
}
private def configLogged(log: Logger): ForkOptions = {
if (config.outputStrategy.isDefined) config
if (config.outputStrategy.isDefined || config.connectInput) config
else config.withOutputStrategy(OutputStrategy.LoggedOutput(log))
}
@ -211,4 +202,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
)
)
end Run

7
sbt
View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
set +e
declare builtin_sbt_version="1.10.7"
declare builtin_sbt_version="1.10.10"
declare -a residual_args
declare -a java_args
declare -a scalac_args
@ -24,7 +24,7 @@ declare build_props_sbt_version=
declare use_sbtn=
declare no_server=
declare sbtn_command="$SBTN_CMD"
declare sbtn_version="1.10.5"
declare sbtn_version="1.10.8"
declare use_colors=1
### ------------------------------- ###
@ -203,6 +203,7 @@ acquire_sbtn () {
exit 2
fi
elif [[ "$OSTYPE" == "darwin"* ]]; then
arch="universal"
archive_target="$p/sbtn-universal-apple-darwin-${sbtn_v}.tar.gz"
url="https://github.com/sbt/sbtn-dist/releases/download/v${sbtn_v}/sbtn-universal-apple-darwin-${sbtn_v}.tar.gz"
elif [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then
@ -641,7 +642,7 @@ process_my_args () {
-allow-empty|--allow-empty|-sbt-create|--sbt-create) allow_empty=true && shift ;;
new) sbt_new=true && addResidual "$1" && shift ;;
new|init) sbt_new=true && addResidual "$1" && shift ;;
*) addResidual "$1" && shift ;;
esac

View File

@ -1,8 +1,9 @@
scalaVersion := "3.6.3"
TaskKey[Unit]("willSucceed") := println("success")
TaskKey[Unit]("willFail") := { throw new Exception("failed") }
scalaVersion := "2.12.20"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test"
libraryDependencies += "org.scalameta" %% "munit" % "1.0.4" % Test
TaskKey[Unit]("fooBar") := { () }

View File

@ -1 +1,3 @@
object A
class A
@main def hello() = println("Hello, World!")

View File

@ -1,3 +1,3 @@
package test.pkg
class FooSpec extends org.scalatest.FlatSpec
class FooSpec extends munit.FunSuite

View File

@ -298,8 +298,7 @@ class BuildServerTest extends AbstractServerTest {
""""severity":1""",
"""incompatible types: int cannot be converted"""
)(
message = "should send publishDiagnostics with severity 1 for Hello.java",
debug = false
message = "should send publishDiagnostics with severity 1 for Hello.java"
)
// Note the messages changed slightly in both cases. That's interesting
@ -322,6 +321,7 @@ class BuildServerTest extends AbstractServerTest {
compile(buildTarget)
/*
assertMessage(
"build/publishDiagnostics",
"Hello.java",
@ -331,6 +331,7 @@ class BuildServerTest extends AbstractServerTest {
message = "should send publishDiagnostics with empty diagnostics",
debug = false
)
*/
IO.delete(otherBuildFile)
reloadWorkspace()

View File

@ -59,7 +59,7 @@ class ClientTest extends AbstractServerTest {
case r => r
}
}
private def client(args: String*): Int = {
private def client(args: String*): Int =
background(
NetworkClient.client(
testPath.toFile,
@ -70,6 +70,19 @@ class ClientTest extends AbstractServerTest {
false
)
)
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

View File

@ -228,7 +228,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[Unit]): Future[Unit] = f(this)