From d38450b41f4fb9a2652a854185e6e2d1a7d5efad Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Tue, 26 Nov 2013 22:46:49 -0500 Subject: [PATCH] Command to run multiple tasks concurrently: 'all a b'. Fixes #628. --- main/src/main/scala/sbt/Act.scala | 44 ++++++++++++++----- main/src/main/scala/sbt/Aggregation.scala | 8 ++-- main/src/main/scala/sbt/CommandStrings.scala | 23 ++++++++-- main/src/main/scala/sbt/Main.scala | 2 +- sbt/src/sbt-test/actions/multi-task/build.sbt | 7 +++ sbt/src/sbt-test/actions/multi-task/test | 3 ++ 6 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 sbt/src/sbt-test/actions/multi-task/build.sbt create mode 100644 sbt/src/sbt-test/actions/multi-task/test diff --git a/main/src/main/scala/sbt/Act.scala b/main/src/main/scala/sbt/Act.scala index f92646fac..b87b1fc4c 100644 --- a/main/src/main/scala/sbt/Act.scala +++ b/main/src/main/scala/sbt/Act.scala @@ -11,7 +11,7 @@ package sbt import DefaultParsers._ import Types.idFun import java.net.URI - import CommandStrings.ShowCommand + import CommandStrings.{MultiTaskCommand, ShowCommand} final class ParsedKey(val key: ScopedKey[_], val mask: ScopeMask) object Act @@ -234,20 +234,39 @@ object Act val extracted = Project extract state import extracted.{showKey, structure} import Aggregation.evaluatingParser - showParser.flatMap { show => + actionParser.flatMap { action => val akp = aggregatedKeyParser(extracted) - def evaluate(kvs: Seq[ScopedKey[T]] forSome { type T}): Parser[() => State] = - evaluatingParser(state, structure, Aggregation.defaultShow(state, show))( keyValues(structure)(kvs) ) - def reconstruct(arg: String): String = ShowCommand + " " + arg - if(show) - ( akp ~ (token(Space) ~> matched(akp)).* ) flatMap { case (kvs, tail) => - evaluate(kvs) map { f => () => tail.map(reconstruct) ::: f() } + def evaluate(kvs: Seq[ScopedKey[_]]): Parser[() => State] = { + val preparedPairs = anyKeyValues(structure, kvs) + val showConfig = Aggregation.defaultShow(state, showTasks = action == ShowAction) + evaluatingParser(state, structure, showConfig)(preparedPairs) map { evaluate => + () => { + val keyStrings = preparedPairs.map(pp => showKey(pp.key)).mkString(", ") + state.log.debug("Evaluating tasks: " + keyStrings) + evaluate() + } } - else - akp flatMap evaluate + } + action match { + case SingleAction => akp flatMap evaluate + case ShowAction | MultiAction => + rep1sep(akp, token(Space)).flatMap( kvss => evaluate(kvss.flatten) ) + } } } + + private[this] final class ActAction + private[this] final val ShowAction, MultiAction, SingleAction = new ActAction + + private[this] def actionParser: Parser[ActAction] = + token( + ((ShowCommand ^^^ ShowAction) | + (MultiTaskCommand ^^^ MultiAction) ) <~ Space + ) ?? SingleAction + + @deprecated("No longer used.", "0.13.2") def showParser = token( (ShowCommand ~ Space) ^^^ true) ?? false + def scopedKeyParser(state: State): Parser[ScopedKey[_]] = scopedKeyParser(Project extract state) def scopedKeyParser(extracted: Extracted): Parser[ScopedKey[_]] = scopedKeyParser(extracted.structure, extracted.currentRef) def scopedKeyParser(structure: BuildStructure, currentRef: ProjectRef): Parser[ScopedKey[_]] = @@ -265,6 +284,11 @@ object Act keys.flatMap { key => getValue(structure.data, key.scope, key.key) map { value => KeyValue(key, value) } } + private[this] def anyKeyValues(structure: BuildStructure, keys: Seq[ScopedKey[_]]): Seq[KeyValue[_]] = + keys.flatMap { key => + getValue(structure.data, key.scope, key.key) map { value => KeyValue(key, value) } + } + private[this] def getValue[T](data: Settings[Scope], scope: Scope, key: AttributeKey[T]): Option[T] = if(java.lang.Boolean.getBoolean("sbt.cli.nodelegation")) data.getDirect(scope, key) else data.get(scope, key) diff --git a/main/src/main/scala/sbt/Aggregation.scala b/main/src/main/scala/sbt/Aggregation.scala index c18f2aafb..f76cb5190 100644 --- a/main/src/main/scala/sbt/Aggregation.scala +++ b/main/src/main/scala/sbt/Aggregation.scala @@ -19,7 +19,7 @@ final object Aggregation final case class KeyValue[+T](key: ScopedKey[_], value: T) def defaultShow(state: State, showTasks: Boolean): ShowConfig = ShowConfig(settingValues = true, taskValues = showTasks, s => state.log.info(s), success = true) - def printSettings[T](xs: Seq[KeyValue[T]], print: String => Unit)(implicit display: Show[ScopedKey[_]]) = + def printSettings(xs: Seq[KeyValue[_]], print: String => Unit)(implicit display: Show[ScopedKey[_]]) = xs match { case KeyValue(_,x) :: Nil => print(x.toString) @@ -33,7 +33,7 @@ final object Aggregation Command.applyEffect(seqParser(ps)) { ts => runTasks(s, structure, ts, DummyTaskMap(Nil), show) } - + @deprecated("Use `timedRun` and `showRun` directly or use `runTasks`.", "0.13.0") def runTasksWithResult[T](s: State, structure: BuildStructure, ts: Values[Task[T]], extra: DummyTaskMap, show: ShowConfig)(implicit display: Show[ScopedKey[_]]): (State, Result[Seq[KeyValue[T]]]) = { @@ -120,7 +120,7 @@ final object Aggregation } } - def evaluatingParser[T](s: State, structure: BuildStructure, show: ShowConfig)(keys: Seq[KeyValue[T]])(implicit display: Show[ScopedKey[_]]): Parser[() => State] = + def evaluatingParser(s: State, structure: BuildStructure, show: ShowConfig)(keys: Seq[KeyValue[_]])(implicit display: Show[ScopedKey[_]]): Parser[() => State] = keys.toList match { case Nil => failure("No such setting/task") @@ -166,7 +166,7 @@ final object Aggregation val resolved = Resolve(extra, Global, key.key, mask)(toResolve) ScopedKey(resolved, key.key) } - + def aggregationEnabled(key: ScopedKey[_], data: Settings[Scope]): Boolean = Keys.aggregate in Scope.fillTaskAxis(key.scope, key.key) get data getOrElse true diff --git a/main/src/main/scala/sbt/CommandStrings.scala b/main/src/main/scala/sbt/CommandStrings.scala index 39704ef4b..073480c31 100644 --- a/main/src/main/scala/sbt/CommandStrings.scala +++ b/main/src/main/scala/sbt/CommandStrings.scala @@ -12,6 +12,7 @@ object CommandStrings val ProjectCommand = "project" val ProjectsCommand = "projects" val ShowCommand = "show" + val MultiTaskCommand = "all" val BootCommand = "boot" val EvalCommand = "eval" @@ -21,10 +22,26 @@ EvalCommand + """ Evaluates the given Scala expression and prints the result and type.""" + @deprecated("Misnomer: was only for `show`. Use showBrief.", "0.13.2") + def actBrief = showBrief + @deprecated("Misnomer: was only for `show`. Use showDetailed.", "0.13.2") + def actDetailed = showDetailed + + def actHelp = showHelp ++ multiTaskHelp + + def multiTaskHelp = Help(MultiTaskCommand, (multiTaskSyntax, multiTaskBrief), multiTaskDetailed) + def multiTaskDetailed = +s"""$multiTaskSyntax + + $multiTaskBrief""" + def multiTaskSyntax = s"""$MultiTaskCommand +""" + def multiTaskBrief = """Executes all of the specified tasks concurrently.""" + + def showHelp = Help(ShowCommand, (ShowCommand + " ", actBrief), actDetailed) - def actBrief = "Displays the result of evaluating the setting or task associated with 'key'." - def actDetailed = -s"""ShowCommand + def showBrief = "Displays the result of evaluating the setting or task associated with 'key'." + def showDetailed = +s"""$ShowCommand Displays the value of the specified setting. diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 7ecd5f9cc..295ffca33 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -368,7 +368,7 @@ object BuiltinCommands } def act = Command.customHelp(Act.actParser, actHelp) - def actHelp = (s: State) => CommandStrings.showHelp ++ keysHelp(s) + def actHelp = (s: State) => CommandStrings.showHelp ++ CommandStrings.multiTaskHelp ++ keysHelp(s) def keysHelp(s: State): Help = if(Project.isProjectLoaded(s)) Help.detailOnly(taskDetail(allTaskAndSettingKeys(s))) diff --git a/sbt/src/sbt-test/actions/multi-task/build.sbt b/sbt/src/sbt-test/actions/multi-task/build.sbt new file mode 100644 index 000000000..38db9170e --- /dev/null +++ b/sbt/src/sbt-test/actions/multi-task/build.sbt @@ -0,0 +1,7 @@ +lazy val a = taskKey[Unit]("a") + +lazy val b = taskKey[Unit]("b") + +a := IO.touch(baseDirectory.value / "a") + +b := IO.touch(baseDirectory.value / "b") diff --git a/sbt/src/sbt-test/actions/multi-task/test b/sbt/src/sbt-test/actions/multi-task/test new file mode 100644 index 000000000..e4344dcbd --- /dev/null +++ b/sbt/src/sbt-test/actions/multi-task/test @@ -0,0 +1,3 @@ +$ absent a b +> all a b +$ exists a b