From 21a05978dea20788077f94304cf9aa3e5230b0f1 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Fri, 6 Dec 2013 20:43:48 -0500 Subject: [PATCH] Fix parsing and evaluation of 'all' and 'show'. See comment in evaluatingParser. --- main/src/main/scala/sbt/Act.scala | 5 ++- main/src/main/scala/sbt/Aggregation.scala | 50 ++++++++++++++++++++--- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/main/src/main/scala/sbt/Act.scala b/main/src/main/scala/sbt/Act.scala index b87b1fc4c..0679754c1 100644 --- a/main/src/main/scala/sbt/Act.scala +++ b/main/src/main/scala/sbt/Act.scala @@ -14,6 +14,7 @@ package sbt import CommandStrings.{MultiTaskCommand, ShowCommand} final class ParsedKey(val key: ScopedKey[_], val mask: ScopeMask) + object Act { val GlobalString = "*" @@ -25,8 +26,8 @@ object Act // the index should be an aggregated index for proper tab completion def scopedKeyAggregated(current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String], structure: BuildStructure): KeysParser = - for(selected <- scopedKeySelected(structure.index.aggregateKeyIndex, current, defaultConfigs, structure.index.keyMap, structure.data) ) yield - Aggregation.aggregate(selected.key, selected.mask, structure.extra) + for(selected <- scopedKeySelected(structure.index.aggregateKeyIndex, current, defaultConfigs, structure.index.keyMap, structure.data) ) yield + Aggregation.aggregate(selected.key, selected.mask, structure.extra) def scopedKeySelected(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String], keyMap: Map[String, AttributeKey[_]], data: Settings[Scope]): Parser[ParsedKey] = diff --git a/main/src/main/scala/sbt/Aggregation.scala b/main/src/main/scala/sbt/Aggregation.scala index f76cb5190..81a9d5494 100644 --- a/main/src/main/scala/sbt/Aggregation.scala +++ b/main/src/main/scala/sbt/Aggregation.scala @@ -121,13 +121,51 @@ final object Aggregation } 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") - case xs @ KeyValue(_, _: InputTask[t]) :: _ => applyDynamicTasks(s, structure, xs.asInstanceOf[Values[InputTask[t]]], show) - case xs @ KeyValue(_, _: Task[t]) :: _ => applyTasks(s, structure, maps(xs.asInstanceOf[Values[Task[t]]])(x => success(x)), show) - case xs => success(() => { if(show.settingValues) printSettings(xs, show.print); s} ) + { + // to make the call sites clearer + def separate[L](in: Seq[KeyValue[_]])(f: KeyValue[_] => Either[KeyValue[L], KeyValue[_]]): (Seq[KeyValue[L]], Seq[KeyValue[_]]) = + Util.separate(in)(f) + + val kvs = keys.toList + if(kvs.isEmpty) + failure("No such setting/task") + else { + val (inputTasks, other) = separate[InputTask[_]](kvs) { + case KeyValue(k,v: InputTask[_]) => Left(KeyValue(k,v)) + case kv => Right(kv) + } + val (tasks, settings) = separate[Task[_]](other) { + case KeyValue(k, v: Task[_]) => Left(KeyValue(k,v)) + case kv => Right(kv) + } + // currently, disallow input tasks to be mixed with normal tasks. + // This occurs in `all` or `show`, which support multiple tasks. + // Previously, multiple tasks could be run in one execution, but they were all for the same key, just in different scopes. + // When `all` was added, it allowed different keys and thus opened the possibility for mixing settings, + // tasks, and input tasks in the same call. The code below allows settings and tasks to be mixed, but not input tasks. + // One problem with input tasks in `all` is that many input tasks consume all input and would need syntactic delimiters. + // Once that is addressed, the tasks constructed by the input tasks would need to be combined with the explicit tasks. + if(inputTasks.size > 0) { + if(other.size > 0) { + val inputStrings = inputTasks.map(_.key).mkString("Input task(s):\n\t", "\n\t", "\n") + val otherStrings = other.map(_.key).mkString("Task(s)/setting(s):\n\t", "\n\t", "\n") + failure(s"Cannot mix input tasks with plain tasks/settings. $inputStrings $otherStrings") + } else + applyDynamicTasks(s, structure, maps(inputTasks)(castToAny), show) + } else { + val base = if(tasks.isEmpty) success( () => s ) else + applyTasks(s, structure, maps(tasks)(x => success( castToAny(x))), show) + base.map { res => () => + val newState = res() + if(show.settingValues && !settings.isEmpty) printSettings(settings, show.print) + newState + } + } } + } + // this is a hack to avoid duplicating method implementations + private[this] def castToAny[T[_]](t: T[_]): T[Any] = t.asInstanceOf[T[Any]] + private[this] def maps[T, S](vs: Values[T])(f: T => S): Values[S] = vs map { case KeyValue(k,v) => KeyValue(k, f(v)) }