Port commandProj

This commit is contained in:
Eugene Yokota 2022-01-30 13:23:36 -05:00
parent 7a5533e8fa
commit 4ff27d7ab7
16 changed files with 253 additions and 235 deletions

View File

@ -10,10 +10,10 @@ import scala.util.Try
// ThisBuild settings take lower precedence,
// but can be shared across the multi projects.
ThisBuild / version := {
val v = "1.8.1-SNAPSHOT"
val v = "2.0.0-alpha1-SNAPSHOT"
nightlyVersion.getOrElse(v)
}
ThisBuild / version2_13 := "2.0.0-SNAPSHOT"
ThisBuild / version2_13 := "2.0.0-alpha1-SNAPSHOT"
ThisBuild / versionScheme := Some("early-semver")
ThisBuild / scalafmtOnCompile := !(Global / insideCI).value
ThisBuild / Test / scalafmtOnCompile := !(Global / insideCI).value
@ -183,7 +183,6 @@ lazy val sbtRoot: Project = (project in file("."))
.aggregate(
(allProjects diff Seq(
actionsProj,
commandProj,
mainSettingsProj,
zincLmIntegrationProj,
scriptedSbtReduxProj,

View File

@ -19,7 +19,7 @@ object BasicCommandStrings {
val TemplateCommand: String = "new"
val Cancel: String = "cancel"
/** The command name to terminate the program.*/
/** The command name to terminate the program. */
val TerminateAction: String = Exit
def helpBrief: (String, String) =
@ -74,16 +74,16 @@ $HelpCommand <regular expression>
private[this] def logLevelDetail(level: Level.Value): String =
s"""$level
Sets the global logging level to $level.
This will be used as the default level for logging from commands, settings, and tasks.
Any explicit `logLevel` configuration in a project overrides this setting.
Sets the global logging level to $level.
This will be used as the default level for logging from commands, settings, and tasks.
Any explicit `logLevel` configuration in a project overrides this setting.
-$level OR --$level
Sets the global logging level as described above, but does so before any other commands are executed on startup, including project loading.
This is useful as a startup option:
* it takes effect before any logging occurs
* if no other commands are passed, interactive mode is still entered
Sets the global logging level as described above, but does so before any other commands are executed on startup, including project loading.
This is useful as a startup option:
* it takes effect before any logging occurs
* if no other commands are passed, interactive mode is still entered
"""
def runEarly(command: String): String = s"$EarlyCommand($command)"
@ -102,8 +102,8 @@ $HelpCommand <regular expression>
val EarlyCommandDetailed: String =
s"""$EarlyCommand(<command>)
Schedules an early command, which will be run before other commands on the command line.
The order is preserved between all early commands, so `sbt "early(a)" "early(b)"` executes `a` and `b` in order.
Schedules an early command, which will be run before other commands on the command line.
The order is preserved between all early commands, so `sbt "early(a)" "early(b)"` executes `a` and `b` in order.
"""
def addPluginSbtFileHelp(): Help = {
@ -119,21 +119,21 @@ $HelpCommand <regular expression>
def ReadDetailed: String =
ReadCommand + ReadFiles + """
Reads the lines from the given files and inserts them as commands.
All empty lines and lines that start with '#' are ignored.
If a file does not exist or is not readable, this command fails.
Reads the lines from the given files and inserts them as commands.
All empty lines and lines that start with '#' are ignored.
If a file does not exist or is not readable, this command fails.
All the lines from all the files are read before any of the commands
are executed. Thus, if any file is not readable, none of commands
from any of the files (even the existing ones) will be run.
All the lines from all the files are read before any of the commands
are executed. Thus, if any file is not readable, none of commands
from any of the files (even the existing ones) will be run.
You probably need to escape this command if entering it at your shell."""
You probably need to escape this command if entering it at your shell."""
def ApplyCommand: String = "apply"
def ApplyDetailed: String =
ApplyCommand + """ [-cp|-classpath <classpath>] <module-name>*
Transforms the current State by calling <module-name>.apply(currentState) for each listed module name.
Here, currentState is of type sbt.State.
Transforms the current State by calling <module-name>.apply(currentState) for each listed module name.
Here, currentState is of type sbt.State.
If a classpath is provided, modules are loaded from a new class loader for this classpath.
"""
@ -143,14 +143,14 @@ $HelpCommand <regular expression>
def RebootDetailed: String =
RebootCommand + """ [dev | full]
This command is equivalent to exiting sbt, restarting, and running the
remaining commands with the exception that the JVM is not shut down.
This command is equivalent to exiting sbt, restarting, and running the
remaining commands with the exception that the JVM is not shut down.
If 'dev' is specified, the current sbt artifacts from the boot directory
(`~/.sbt/boot` by default) are deleted before restarting.
This forces an update of sbt and Scala, which is useful when working with development
versions of sbt.
If 'full' is specified, the boot directory is wiped out before restarting.
If 'dev' is specified, the current sbt artifacts from the boot directory
(`~/.sbt/boot` by default) are deleted before restarting.
This forces an update of sbt and Scala, which is useful when working with development
versions of sbt.
If 'full' is specified, the boot directory is wiped out before restarting.
"""
def Multi: String = ";"
@ -197,8 +197,8 @@ $AliasCommand name=
def StartServer = "startServer"
def StartServerDetailed: String =
s"""$StartServer
Starts the server if it has not been started. This is intended to be used with
-Dsbt.server.autostart=false."""
Starts the server if it has not been started. This is intended to be used with
-Dsbt.server.autostart=false."""
def ServerDetailed: String =
"--server always runs sbt in not-daemon mode."
@ -243,7 +243,7 @@ $AliasCommand name=
def IfLastDetailed =
s"""$IfLast <command>
$IfLastCommon"""
$IfLastCommon"""
val ContinuousExecutePrefix = "~"
def continuousDetail: String = "Executes the specified command whenever source files change."

View File

@ -74,7 +74,9 @@ object BasicCommands {
def early: Command = Command.arb(earlyParser, earlyHelp)((s, other) => other :: s)
private[this] def levelParser: Parser[String] =
Iterator(Level.Debug, Level.Info, Level.Warn, Level.Error) map (l => token(l.toString)) reduce (_ | _)
Iterator(Level.Debug, Level.Info, Level.Warn, Level.Error) map (l =>
token(l.toString)
) reduce (_ | _)
private[this] def addPluginSbtFileParser: Parser[File] = {
token(AddPluginSbtFileCommand) ~> (":" | "=" | Space.map(_.toString)) ~> (StringBasic).examples(
@ -87,8 +89,8 @@ object BasicCommands {
private[this] def addPluginSbtFileStringParser: Parser[String] = {
token(
token(AddPluginSbtFileCommand) ~ (":" | "=" | Space.map(_.toString)) ~ (StringBasic)
.examples("/some/extra.sbt") map {
case s1 ~ s2 ~ s3 => s1 + s2 + s3
.examples("/some/extra.sbt") map { case s1 ~ s2 ~ s3 =>
s1 + s2 + s3
}
)
}
@ -106,7 +108,7 @@ object BasicCommands {
* Adds additional *.sbt to the plugin build.
* This must be combined with early command as: --addPluginSbtFile=/tmp/extra.sbt
*/
def addPluginSbtFile: Command = Command.arb(_ => addPluginSbtFileParser, addPluginSbtFileHelp) {
def addPluginSbtFile: Command = Command.arb(_ => addPluginSbtFileParser, addPluginSbtFileHelp()) {
(s, extraSbtFile) =>
val extraFiles = s.get(BasicKeys.extraMetaSbtFiles).toList.flatten
s.put(BasicKeys.extraMetaSbtFiles, (extraFiles: Seq[File]) :+ extraSbtFile)
@ -115,10 +117,9 @@ object BasicCommands {
def help: Command = Command.make(HelpCommand, helpBrief, helpDetailed)(helpParser)
def helpParser(s: State): Parser[() => State] = {
val h = s.definedCommands.foldLeft(Help.empty)(
(a, b) =>
a ++ (try b.help(s)
catch { case NonFatal(_) => Help.empty })
val h = s.definedCommands.foldLeft(Help.empty)((a, b) =>
a ++ (try b.help(s)
catch { case NonFatal(_) => Help.empty })
)
val helpCommands = h.detail.keySet
val spacedArg = singleArgument(helpCommands).?
@ -136,8 +137,9 @@ object BasicCommands {
case Nil => none[String]
case xs => xs.mkString(" ").some
}
val message = try Help.message(h, topic)
catch { case NonFatal(ex) => ex.toString }
val message =
try Help.message(h, topic)
catch { case NonFatal(ex) => ex.toString }
System.out.println(message)
s.copy(remainingCommands = remainingCommands)
}
@ -276,8 +278,8 @@ object BasicCommands {
matched((s.combinedParser: Parser[_]) | token(any, hide = const(true)))
def ifLast: Command =
Command(IfLast, Help.more(IfLast, IfLastDetailed))(otherCommandParser)(
(s, arg) => if (s.remainingCommands.isEmpty) arg :: s else s
Command(IfLast, Help.more(IfLast, IfLastDetailed))(otherCommandParser)((s, arg) =>
if (s.remainingCommands.isEmpty) arg :: s else s
)
def append: Command =
@ -286,15 +288,15 @@ object BasicCommands {
)
def setOnFailure: Command =
Command(OnFailure, Help.more(OnFailure, OnFailureDetailed))(otherCommandParser)(
(s, arg) => s.copy(onFailure = Some(Exec(arg, s.source)))
Command(OnFailure, Help.more(OnFailure, OnFailureDetailed))(otherCommandParser)((s, arg) =>
s.copy(onFailure = Some(Exec(arg, s.source)))
)
def clearOnFailure: Command = Command.command(ClearOnFailure)(s => s.copy(onFailure = None))
def stashOnFailure: Command =
Command.command(StashOnFailure)(
s => s.copy(onFailure = None).update(OnFailureStack)(s.onFailure :: _.toList.flatten)
Command.command(StashOnFailure)(s =>
s.copy(onFailure = None).update(OnFailureStack)(s.onFailure :: _.toList.flatten)
)
def popOnFailure: Command = Command.command(PopOnFailure) { s =>
@ -346,8 +348,8 @@ object BasicCommands {
private[this] def className: Parser[String] = {
val base = StringBasic & not('-' ~> any.*, "Class name cannot start with '-'.")
def single(s: String) = Completions.single(Completion.displayOnly(s))
val compl = TokenCompletions.fixed(
(seen, _) => if (seen.startsWith("-")) Completions.nil else single("<class name>")
val compl = TokenCompletions.fixed((seen, _) =>
if (seen.startsWith("-")) Completions.nil else single("<class name>")
)
token(base, compl)
}
@ -391,7 +393,7 @@ object BasicCommands {
val lines = hp.toList.flatMap(p => IO.readLines(p)).toIndexedSeq
histFun(CHistory(lines, hp)) match {
case Some(commands) =>
commands foreach println //printing is more appropriate than logging
commands foreach println // printing is more appropriate than logging
(commands ::: s).continue
case None => s.fail
}

View File

@ -9,6 +9,7 @@ package sbt
import java.io.File
import java.util.regex.{ Pattern, PatternSyntaxException }
import scala.collection.immutable.StringOps
import sbt.internal.util.AttributeKey
import sbt.internal.util.complete.Parser
@ -42,7 +43,7 @@ object CommandUtil {
for ((a, b) <- in) yield pre + fill(a, width) + sep + b
}
def fill(s: String, size: Int): String = s + " " * math.max(size - s.length, 0)
def fill(s: String, size: Int): String = s + StringOps(" ") * math.max(size - s.length, 0)
def withAttribute[T](s: State, key: AttributeKey[T], ifMissing: String)(f: T => State): State =
s get key match {
@ -74,16 +75,15 @@ object CommandUtil {
def searchHelp(selected: String, detailMap: Map[String, String]): Map[String, String] = {
val pattern = Pattern.compile(selected, HelpPatternFlags)
detailMap flatMap {
case (k, v) =>
val contentMatches = Highlight.showMatches(pattern)(v)
val keyMatches = Highlight.showMatches(pattern)(k)
val keyString = Highlight.bold(keyMatches getOrElse k)
val contentString = contentMatches getOrElse v
if (keyMatches.isDefined || contentMatches.isDefined)
Seq((keyString, contentString))
else
nilSeq
detailMap flatMap { case (k, v) =>
val contentMatches = Highlight.showMatches(pattern)(v)
val keyMatches = Highlight.showMatches(pattern)(k)
val keyString = Highlight.bold(keyMatches getOrElse k)
val contentString = contentMatches getOrElse v
if (keyMatches.isDefined || contentMatches.isDefined)
Seq((keyString, contentString))
else
nilSeq
}
}

View File

@ -99,19 +99,19 @@ trait Identity {
trait StateOps extends Any {
def process(f: (Exec, State) => State): State
/** Schedules `commands` to be run before any remaining commands.*/
/** Schedules `commands` to be run before any remaining commands. */
def :::(newCommands: List[String]): State
/** Schedules `commands` to be run before any remaining commands.*/
/** Schedules `commands` to be run before any remaining commands. */
def ++:(newCommands: List[Exec]): State
/** Schedules `command` to be run before any remaining commands.*/
/** Schedules `command` to be run before any remaining commands. */
def ::(command: String): State
/** Schedules `command` to be run before any remaining commands.*/
/** Schedules `command` to be run before any remaining commands. */
def +:(command: Exec): State
/** Sets the next command processing action to be to continue processing the next command.*/
/** Sets the next command processing action to be to continue processing the next command. */
def continue: State
/**
@ -135,7 +135,7 @@ trait StateOps extends Any {
*/
private[sbt] def reboot(full: Boolean, currentOnly: Boolean): State
/** Sets the next command processing action to do.*/
/** Sets the next command processing action to do. */
def setNext(n: State.Next): State
/**
@ -145,16 +145,16 @@ trait StateOps extends Any {
*/
def reload: State
/** Sets the next command processing action to be to rotate the global log and continue executing commands.*/
/** Sets the next command processing action to be to rotate the global log and continue executing commands. */
def clearGlobalLog: State
/** Sets the next command processing action to be to keep the previous log and continue executing commands. */
/** Sets the next command processing action to be to keep the previous log and continue executing commands. */
def keepLastLog: State
/** Sets the next command processing action to be to exit with a zero exit code if `ok` is true and a nonzero exit code if `ok` if false.*/
/** Sets the next command processing action to be to exit with a zero exit code if `ok` is true and a nonzero exit code if `ok` if false. */
def exit(ok: Boolean): State
/** Marks the currently executing command as failing. This triggers failure handling by the command processor. See also `State.onFailure`*/
/** Marks the currently executing command as failing. This triggers failure handling by the command processor. See also `State.onFailure` */
def fail: State
/**
@ -171,46 +171,46 @@ trait StateOps extends Any {
/** Registers `newCommand` as an available command. */
def +(newCommand: Command): State
/** Gets the value associated with `key` from the custom attributes map.*/
/** Gets the value associated with `key` from the custom attributes map. */
def get[T](key: AttributeKey[T]): Option[T]
/** Sets the value associated with `key` in the custom attributes map.*/
/** Sets the value associated with `key` in the custom attributes map. */
def put[T](key: AttributeKey[T], value: T): State
/** Removes the `key` and any associated value from the custom attributes map.*/
/** Removes the `key` and any associated value from the custom attributes map. */
def remove(key: AttributeKey[_]): State
/** Sets the value associated with `key` in the custom attributes map by transforming the current value.*/
/** Sets the value associated with `key` in the custom attributes map by transforming the current value. */
def update[T](key: AttributeKey[T])(f: Option[T] => T): State
/** Returns true if `key` exists in the custom attributes map, false if it does not exist.*/
/** Returns true if `key` exists in the custom attributes map, false if it does not exist. */
def has(key: AttributeKey[_]): Boolean
/** The application base directory, which is not necessarily the current working directory.*/
/** The application base directory, which is not necessarily the current working directory. */
def baseDir: File
/** The Logger used for general command logging.*/
/** The Logger used for general command logging. */
def log: Logger
/** Evaluates the provided expression with a JVM-wide and machine-wide lock on `file`.*/
/** Evaluates the provided expression with a JVM-wide and machine-wide lock on `file`. */
def locked[T](file: File)(t: => T): T
/** Runs any defined exitHooks and then clears them.*/
/** Runs any defined exitHooks and then clears them. */
def runExitHooks(): State
/** Registers a new exit hook, which will run when sbt exits or restarts.*/
/** Registers a new exit hook, which will run when sbt exits or restarts. */
def addExitHook(f: => Unit): State
/** An advisory flag that is `true` if this application will execute commands based on user input.*/
/** An advisory flag that is `true` if this application will execute commands based on user input. */
def interactive: Boolean
/** Changes the advisory `interactive` flag. */
def setInteractive(flag: Boolean): State
/** Get the class loader cache for the application.*/
/** Get the class loader cache for the application. */
def classLoaderCache: IncClassLoaderCache
/** Create and register a class loader cache. This should be called once at the application entry-point.*/
/** Create and register a class loader cache. This should be called once at the application entry-point. */
def initializeClassLoaderCache: State
}
@ -220,22 +220,22 @@ object State {
override def getURLs: Array[URL] = cp.map(_.toURI.toURL).toArray
}
/** Indicates where command execution should resume after a failure.*/
/** Indicates where command execution should resume after a failure. */
val FailureWall = BasicCommandStrings.FailureWall
/** Represents the next action for the command processor.*/
/** Represents the next action for the command processor. */
sealed trait Next
/** Indicates that the command processor should process the next command.*/
/** Indicates that the command processor should process the next command. */
object Continue extends Next
/** Indicates that the application should exit with the given result.*/
/** Indicates that the application should exit with the given result. */
final class Return(val result: xsbti.MainResult) extends Next
/** Indicates that global logging should be rotated.*/
/** Indicates that global logging should be rotated. */
final object ClearGlobalLog extends Next
/** Indicates that the previous log file should be preserved instead of discarded.*/
/** Indicates that the previous log file should be preserved instead of discarded. */
final object KeepLastLog extends Next
/**
@ -246,21 +246,21 @@ object State {
*/
final class History private[State] (val executed: Seq[Exec], val maxSize: Int) {
/** Adds `command` as the most recently executed command.*/
/** Adds `command` as the most recently executed command. */
def ::(command: Exec): History = {
val prependTo =
if (maxSize > 0 && executed.size >= maxSize) executed.take(maxSize - 1) else executed
new History(command +: prependTo, maxSize)
}
/** Changes the maximum number of commands kept, adjusting the current history if necessary.*/
/** Changes the maximum number of commands kept, adjusting the current history if necessary. */
def setMaxSize(size: Int): History =
new History(if (size <= 0) executed else executed.take(size), size)
def currentOption: Option[Exec] = executed.headOption
def previous: Option[Exec] = executed.drop(1).headOption
}
/** Constructs an empty command History with a default, finite command limit.*/
/** Constructs an empty command History with a default, finite command limit. */
def newHistory = new History(Vector.empty, HistoryCommands.MaxLines)
def defaultReload(state: State): Reboot = {
@ -385,9 +385,12 @@ object State {
s.copy(exitHooks = Set.empty)
}
def locked[T](file: File)(t: => T): T =
s.configuration.provider.scalaProvider.launcher.globalLock.apply(file, new Callable[T] {
def call = t
})
s.configuration.provider.scalaProvider.launcher.globalLock.apply(
file,
new Callable[T] {
def call = t
}
)
def interactive = getBoolean(s, BasicKeys.interactive, false)
def setInteractive(i: Boolean) = s.put(BasicKeys.interactive, i)

View File

@ -39,7 +39,7 @@ trait Watched {
*/
def antiEntropy: FiniteDuration = Watched.AntiEntropy
/** The message to show when triggered execution waits for sources to change.*/
/** The message to show when triggered execution waits for sources to change. */
private[sbt] def watchingMessage(s: WatchState): String = Watched.defaultWatchingMessage(s)
/** The message to show before an action is run. */

View File

@ -91,7 +91,7 @@ abstract class CommandChannel {
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 onFastTrackTask: String => Boolean = { (s: String) =>
fastTrack.synchronized(fastTrack.forEach { q =>
q.add(new FastTrackTask(this, s))
()

View File

@ -46,15 +46,16 @@ private[sbt] object LegacyWatched {
case Some(eventMonitor) =>
Watched.printIfDefined(watched watchingMessage eventMonitor.state())
@tailrec def impl(): State = {
val triggered = try eventMonitor.awaitEvent()
catch {
case NonFatal(e) =>
log.error(
"Error occurred obtaining files to watch. Terminating continuous execution..."
)
s.handleError(e)
false
}
val triggered =
try eventMonitor.awaitEvent()
catch {
case NonFatal(e) =>
log.error(
"Error occurred obtaining files to watch. Terminating continuous execution..."
)
s.handleError(e)
false
}
if (triggered) {
Watched.printIfDefined(watched triggeredMessage eventMonitor.state())
ClearOnFailure :: next :: FailureWall :: repeat :: s

View File

@ -38,20 +38,21 @@ private[sbt] class ClassLoaderCache(
def setParent(parent: ClassLoader): Unit = parentHolder.set(parent)
def this(commonParent: ClassLoader) = this(commonParent, None)
def this(scalaProvider: ScalaProvider) =
this(scalaProvider.launcher.topLoader, {
scalaProvider.jars.find(_.getName == "scala-library.jar").flatMap { lib =>
val clazz = scalaProvider.getClass
try {
val loader = clazz.getDeclaredMethod("libraryLoaderOnly").invoke(scalaProvider)
Some(lib -> loader.asInstanceOf[ClassLoader])
} catch { case NonFatal(_) => None }
}
})
private val scalaProviderKey = miniProvider.map {
case (f, cl) =>
new Key((f -> IO.getModifiedTimeOrZero(f)) :: Nil, commonParent) {
override def toClassLoader: ClassLoader = cl
this(
scalaProvider.launcher.topLoader, {
scalaProvider.jars.find(_.getName == "scala-library.jar").flatMap { lib =>
val clazz = scalaProvider.getClass
try {
val loader = clazz.getDeclaredMethod("libraryLoaderOnly").invoke(scalaProvider)
Some(lib -> loader.asInstanceOf[ClassLoader])
} catch { case NonFatal(_) => None }
}
}
)
private val scalaProviderKey = miniProvider.map { case (f, cl) =>
new Key((f -> IO.getModifiedTimeOrZero(f)) :: Nil, commonParent) {
override def toClassLoader: ClassLoader = cl
}
}
private class Key(val fileStamps: Seq[(File, Long)], val parent: ClassLoader) {
def this(files: List[File], parent: ClassLoader) =
@ -102,19 +103,20 @@ private[sbt] class ClassLoaderCache(
start()
@tailrec
override final def run(): Unit = {
val stop = try {
referenceQueue.remove(1000) match {
case ClassLoaderReference(key, classLoader) =>
close(classLoader)
delegate.remove(key)
()
case _ =>
val stop =
try {
referenceQueue.remove(1000) match {
case ClassLoaderReference(key, classLoader) =>
close(classLoader)
delegate.remove(key)
()
case _ =>
}
clearExpiredLoaders()
false
} catch {
case _: InterruptedException => true
}
clearExpiredLoaders()
false
} catch {
case _: InterruptedException => true
}
if (!stop) run()
}
}

View File

@ -25,7 +25,8 @@ object BspClient {
while (!terminated.get) lock.wait()
}
0
} catch { case _: Throwable => 1 } finally sbtServer.close()
} catch { case _: Throwable => 1 }
finally sbtServer.close()
}
private[sbt] def transferTo(

View File

@ -190,18 +190,19 @@ class NetworkClient(
}
}
@tailrec def connect(attempt: Int): (Socket, Option[String]) = {
val res = try Some(mkSocket(portfile))
catch {
// This catches a pipe busy exception which can happen if two windows clients
// attempt to connect in rapid succession
case e: IOException if e.getMessage.contains("Couldn't open") && attempt < 10 =>
if (e.getMessage.contains("Access is denied") || e.getMessage.contains("(5)")) {
errorStream.println(s"Access denied for portfile $portfile")
throw new NetworkClient.AccessDeniedException
}
None
case e: IOException => throw new ConnectionRefusedException(e)
}
val res =
try Some(mkSocket(portfile))
catch {
// This catches a pipe busy exception which can happen if two windows clients
// attempt to connect in rapid succession
case e: IOException if e.getMessage.contains("Couldn't open") && attempt < 10 =>
if (e.getMessage.contains("Access is denied") || e.getMessage.contains("(5)")) {
errorStream.println(s"Access denied for portfile $portfile")
throw new NetworkClient.AccessDeniedException
}
None
case e: IOException => throw new ConnectionRefusedException(e)
}
res match {
case Some(r) => r
case None =>
@ -431,36 +432,37 @@ class NetworkClient(
}
@tailrec
def blockUntilStart(): Unit = {
val stop = try {
socket match {
case None =>
process.foreach { p =>
val output = p.getInputStream
while (output.available > 0) {
printStream.write(output.read())
val stop =
try {
socket match {
case None =>
process.foreach { p =>
val output = p.getInputStream
while (output.available > 0) {
printStream.write(output.read())
}
}
}
case Some(s) =>
while (!gotInputBack && !stdinBytes.isEmpty && socket.isDefined) {
val out = s.getOutputStream
val b = stdinBytes.poll
if (b == -1) {
// server waits for user input but stinBytes has ended
shutdown.run()
} else {
out.write(b)
out.flush()
case Some(s) =>
while (!gotInputBack && !stdinBytes.isEmpty && socket.isDefined) {
val out = s.getOutputStream
val b = stdinBytes.poll
if (b == -1) {
// server waits for user input but stinBytes has ended
shutdown.run()
} else {
out.write(b)
out.flush()
}
}
}
}
process.foreach { p =>
val error = p.getErrorStream
while (error.available > 0) {
errorStream.write(error.read())
}
}
false
} catch { case e: IOException => true }
process.foreach { p =>
val error = p.getErrorStream
while (error.available > 0) {
errorStream.write(error.read())
}
}
false
} catch { case e: IOException => true }
Thread.sleep(10)
printStream.flush()
errorStream.flush()
@ -489,7 +491,8 @@ class NetworkClient(
}
try blockUntilStart()
catch { case t: Throwable => t.printStackTrace() } finally {
catch { case t: Throwable => t.printStackTrace() }
finally {
sbtProcess.set(null)
Util.ignoreResult(Runtime.getRuntime.removeShutdownHook(shutdown))
}
@ -558,23 +561,22 @@ class NetworkClient(
completions(msg.result match {
case Some(o: JObject) =>
o.value
.foldLeft(CompletionResponse(Vector.empty[String])) {
case (resp, i) =>
if (i.field == "items")
resp.withItems(
Converter
.fromJson[Vector[String]](i.value)
.getOrElse(Vector.empty[String])
)
else if (i.field == "cachedTestNames")
resp.withCachedTestNames(
Converter.fromJson[Boolean](i.value).getOrElse(true)
)
else if (i.field == "cachedMainClassNames")
resp.withCachedMainClassNames(
Converter.fromJson[Boolean](i.value).getOrElse(true)
)
else resp
.foldLeft(CompletionResponse(Vector.empty[String])) { case (resp, i) =>
if (i.field == "items")
resp.withItems(
Converter
.fromJson[Vector[String]](i.value)
.getOrElse(Vector.empty[String])
)
else if (i.field == "cachedTestNames")
resp.withCachedTestNames(
Converter.fromJson[Boolean](i.value).getOrElse(true)
)
else if (i.field == "cachedMainClassNames")
resp.withCachedMainClassNames(
Converter.fromJson[Boolean](i.value).getOrElse(true)
)
else resp
}
case _ => CompletionResponse(Vector.empty[String])
})
@ -632,8 +634,8 @@ class NetworkClient(
)
)
}
splitToMessage foreach {
case (level, msg) => console.appendLog(level, msg)
splitToMessage foreach { case (level, msg) =>
console.appendLog(level, msg)
}
}
@ -802,7 +804,7 @@ class NetworkClient(
}
withSignalHandler(handler, Signals.INT) {
def block(): Int = {
try this.synchronized(this.wait)
try this.synchronized(this.wait())
catch { case _: InterruptedException => }
if (exitClean.get) 0 else 1
}
@ -989,7 +991,8 @@ class NetworkClient(
if (attached.get()) drain()
}
try read()
catch { case _: InterruptedException | NonFatal(_) => stopped.set(true) } finally {
catch { case _: InterruptedException | NonFatal(_) => stopped.set(true) }
finally {
inputThread.set(null)
}
}
@ -1023,6 +1026,7 @@ class NetworkClient(
val secs = f"${total % 60}%02d"
s" ($maybeHours$mins:$secs)"
})
s"Total time: $totalString, completed $nowString"
}
}
@ -1156,7 +1160,8 @@ object NetworkClient {
try {
if (client.connect(log = true, promptCompleteUsers = false)) client.run()
else 1
} catch { case _: Exception => 1 } finally client.close()
} catch { case _: Exception => 1 }
finally client.close()
}
def client(
baseDirectory: File,
@ -1187,7 +1192,8 @@ object NetworkClient {
if (client.connect(log = true, promptCompleteUsers = false)) client.run()
else 1
}
} catch { case _: Exception => 1 } finally client.close()
} catch { case _: Exception => 1 }
finally client.close()
}
def client(
baseDirectory: File,
@ -1240,7 +1246,8 @@ object NetworkClient {
System.exit(Terminal.withStreams(isServer = false, isSubProcess = false) {
val term = Terminal.console
try client(base, parsed, term.inputStream, System.err, term, useJNI)
catch { case _: AccessDeniedException => 1 } finally {
catch { case _: AccessDeniedException => 1 }
finally {
Runtime.getRuntime.removeShutdownHook(hook)
hook.run()
}
@ -1286,7 +1293,8 @@ object NetworkClient {
else Nil
out.println(results.sorted.distinct mkString "\n")
0
} catch { case _: Exception => 1 } finally client.close()
} catch { case _: Exception => 1 }
finally client.close()
} catch { case _: AccessDeniedException => 1 }
}
@ -1301,7 +1309,8 @@ object NetworkClient {
val err = new PrintStream(term.errorStream)
val out = if (redirectOutput) err else new PrintStream(term.outputStream)
val args = parseArgs(arguments.toArray).withBaseDirectory(configuration.baseDirectory)
val useJNI = BootServerSocket.requiresJNI || System.getProperty("sbt.ipcsocket.jni", "false") == "true"
val useJNI =
BootServerSocket.requiresJNI || System.getProperty("sbt.ipcsocket.jni", "false") == "true"
val client = simpleClient(args, term.inputStream, out, err, useJNI = useJNI)
clientImpl(client, args.bsp)
}

View File

@ -114,7 +114,7 @@ private[sbt] object Server {
} catch {
case e: IOException if e.getMessage.contains("connect") =>
case _: SocketTimeoutException => // its ok
case _: SocketException if !running.get => // the server is shutting down
case _: SocketException if !running.get => // the server is shutting down
}
}
serverSocketHolder.get match {

View File

@ -29,10 +29,10 @@ object ServerHandler {
lazy val fallback: ServerHandler = ServerHandler({ handler =>
ServerIntent(
onRequest = { case x => handler.log.debug(s"Unhandled request received: ${x.method}: $x") },
onRequest = { case x => handler.log.debug(s"Unhandled request received: ${x.method}: $x") },
onResponse = { case x => handler.log.debug(s"Unhandled responce received") },
onNotification = {
case x => handler.log.debug(s"Unhandled notification received: ${x.method}: $x")
onNotification = { case x =>
handler.log.debug(s"Unhandled notification received: ${x.method}: $x")
},
)
})

View File

@ -23,7 +23,7 @@ import scala.annotation.tailrec
private[sbt] trait UITask extends Runnable with AutoCloseable {
private[sbt] val channel: CommandChannel
private[sbt] val reader: UITask.Reader
private[sbt] def 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)

View File

@ -45,7 +45,8 @@ private[sbt] object ReadJsonFromInputStream {
var content: Seq[Byte] = Seq.empty[Byte]
var consecutiveLineEndings = 0
var onCarriageReturn = false
do {
def run(): Unit =
val byte = inputStream.read
byte match {
case `newline` =>
@ -54,33 +55,34 @@ private[sbt] object ReadJsonFromInputStream {
onCarriageReturn = false
if (line.startsWith(contentLength)) {
Try(line.drop(contentLength.length).toInt) foreach { len =>
def doDrainHeaders(): Unit =
inputStream.read match
case `newline` if onCarriageReturn =>
getLine()
onCarriageReturn = false
consecutiveLineEndings += 1
case `carriageReturn` => onCarriageReturn = true
case -1 => running.set(false)
case c =>
if (c == newline) getLine()
else {
if (index >= headerBuffer.length) expandHeaderBuffer()
headerBuffer(index) = c.toByte
index += 1
}
onCarriageReturn = false
consecutiveLineEndings = 0
def drainHeaders(): Unit =
do {
inputStream.read match {
case `newline` if onCarriageReturn =>
getLine()
onCarriageReturn = false
consecutiveLineEndings += 1
case `carriageReturn` => onCarriageReturn = true
case -1 => running.set(false)
case c =>
if (c == newline) getLine()
else {
if (index >= headerBuffer.length) expandHeaderBuffer()
headerBuffer(index) = c.toByte
index += 1
}
onCarriageReturn = false
consecutiveLineEndings = 0
}
} while (consecutiveLineEndings < 2 && running.get)
doDrainHeaders()
while consecutiveLineEndings < 2 && running.get do doDrainHeaders()
drainHeaders()
if (running.get) {
val buf = new Array[Byte](len)
var offset = 0
do {
offset += inputStream.read(buf, offset, len - offset)
} while (offset < len && running.get)
def run1(): Unit = offset += inputStream.read(buf, offset, len - offset)
run1()
while offset < len && running.get do run1()
if (running.get) content = buf.toSeq
}
}
@ -99,7 +101,9 @@ private[sbt] object ReadJsonFromInputStream {
index += 1
}
} while (content.isEmpty && running.get)
run()
while content.isEmpty && running.get do run()
content
}

View File

@ -15,8 +15,8 @@ object Dependencies {
// sbt modules
private val ioVersion = nightlyVersion.getOrElse("1.8.0")
private val lmVersion =
sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.8.0")
val zincVersion = nightlyVersion.getOrElse("1.8.0")
sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("2.0.0-alpha1")
val zincVersion = nightlyVersion.getOrElse("2.0.0-alpha1")
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
@ -57,10 +57,7 @@ object Dependencies {
c: Option[Configuration] = None
) = (p: Project) => {
val m0 = moduleId.withConfigurations(c.map(_.name))
val m = m0.name match {
case "compiler-interface" => m0
case _ => m0.cross(CrossVersion.for3Use2_13)
}
val m = m0
path match {
case Some(f) =>
p.dependsOn(ClasspathDependency(ProjectRef(file(f), projectName), c.map(_.name)))