diff --git a/main/command/src/main/scala/sbt/BasicCommandStrings.scala b/main/command/src/main/scala/sbt/BasicCommandStrings.scala index 43254ad7e..7e22e3a73 100644 --- a/main/command/src/main/scala/sbt/BasicCommandStrings.scala +++ b/main/command/src/main/scala/sbt/BasicCommandStrings.scala @@ -37,6 +37,23 @@ object BasicCommandStrings def exitBrief = "Terminates the build." + def runEarly(command: String) = { + val sep = if(command.isEmpty || Character.isLetter(command.charAt(0))) "" else " " + s"$EarlyCommand$sep$command" + } + private[sbt] def isEarlyCommand(s: String): Boolean = { + s.startsWith(EarlyCommand) && s != Compat.FailureWall && s != Compat.ClearOnFailure + } + + val EarlyCommand = "--" + val EarlyCommandBrief = (s"$EarlyCommand", "Schedules a command to run before other commands on startup.") + val EarlyCommandDetailed = +s"""$EarlyCommand + + 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 --a --b` executes `a` and `b` in order. +""" + def ReadCommand = "<" def ReadFiles = " file1 file2 ..." def ReadDetailed = diff --git a/main/command/src/main/scala/sbt/BasicCommands.scala b/main/command/src/main/scala/sbt/BasicCommands.scala index 91b4b51cd..ba74d618c 100644 --- a/main/command/src/main/scala/sbt/BasicCommands.scala +++ b/main/command/src/main/scala/sbt/BasicCommands.scala @@ -15,11 +15,15 @@ package sbt object BasicCommands { - lazy val allBasicCommands = Seq(nop, ignore, help, multi, ifLast, append, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, reboot, call, exit, continuous, history, shell, read, alias) ++ compatCommands + lazy val allBasicCommands = Seq(nop, ignore, help, multi, ifLast, append, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, reboot, call, early, exit, continuous, history, shell, read, alias) ++ compatCommands def nop = Command.custom(s => success(() => s)) def ignore = Command.command(FailureWall)(idFun) + def early = Command.arb(earlyParser, earlyHelp) { (s, other) => other :: s } + private[this] def earlyParser = (s: State) => token(EarlyCommand).flatMap(_ => otherCommandParser(s)) + private[this] def earlyHelp = Help(EarlyCommand, EarlyCommandBrief, EarlyCommandDetailed) + def help = Command.make(HelpCommand, helpBrief, helpDetailed)(helpParser) def helpParser(s: State) = diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 56d2042fe..be112a4ca 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -22,11 +22,13 @@ final class xMain extends xsbti.AppMain { def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = { + import BasicCommands.early + import BasicCommandStrings.runEarly import BuiltinCommands.{initialize, defaults} import CommandStrings.{BootCommand, DefaultsCommand, InitCommand} runManaged( initialState(configuration, - Seq(initialize, defaults), - DefaultsCommand :: InitCommand :: BootCommand :: Nil) + Seq(defaults, early), + runEarly(DefaultsCommand) :: runEarly(InitCommand) :: BootCommand :: Nil) ) } } @@ -63,7 +65,10 @@ object StandardMain def initialState(configuration: xsbti.AppConfiguration, initialDefinitions: Seq[Command], preCommands: Seq[String]): State = { - val commands = preCommands ++ configuration.arguments.map(_.trim) + import BasicCommandStrings.isEarlyCommand + val userCommands = configuration.arguments.map(_.trim) + val (earlyCommands, normalCommands) = (preCommands ++ userCommands).partition(isEarlyCommand) + val commands = earlyCommands ++ normalCommands val initAttrs = BuiltinCommands.initialAttributes val s = State( configuration, initialDefinitions, Set.empty, None, commands, State.newHistory, initAttrs, initialGlobalLogging, State.Continue ) s.initializeClassLoaderCache @@ -80,12 +85,12 @@ object BuiltinCommands { def initialAttributes = AttributeMap.empty - def ConsoleCommands: Seq[Command] = Seq(ignore, exit, IvyConsole.command, act, nop) - def ScriptCommands: Seq[Command] = Seq(ignore, exit, Script.command, act, nop) + def ConsoleCommands: Seq[Command] = Seq(ignore, exit, IvyConsole.command, early, act, nop) + def ScriptCommands: Seq[Command] = Seq(ignore, exit, Script.command, early, act, nop) def DefaultCommands: Seq[Command] = Seq(ignore, help, about, tasks, settingsCommand, loadProject, projects, project, reboot, read, history, set, sessionCommand, inspect, loadProjectImpl, loadFailed, Cross.crossBuild, Cross.switchVersion, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, - ifLast, multi, shell, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, act) ++ + ifLast, multi, shell, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, early, initialize, act) ++ compatCommands def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil @@ -198,7 +203,7 @@ object BuiltinCommands def taskStrings(key: AttributeKey[_]): Option[(String, String)] = key.description map { d => (key.label, d) } def defaults = Command.command(DefaultsCommand) { s => - s ++ DefaultCommands + s.copy(definedCommands = DefaultCommands) } def initialize = Command.command(InitCommand) { s =>