mirror of https://github.com/sbt/sbt.git
Port commandProj
This commit is contained in:
parent
7a5533e8fa
commit
4ff27d7ab7
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
},
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
Loading…
Reference in New Issue