diff --git a/main/Act.scala b/main/Act.scala index 06709c06b..703fe39c4 100644 --- a/main/Act.scala +++ b/main/Act.scala @@ -15,7 +15,7 @@ object Act val GlobalString = "*" // this does not take aggregation into account - def scopedKey(index: KeyIndex, current: ProjectRef, defaultConfigs: ProjectRef => Seq[String], keyMap: Map[String, AttributeKey[_]]): Parser[ScopedKey[_]] = + def scopedKey(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String], keyMap: Map[String, AttributeKey[_]]): Parser[ScopedKey[_]] = { for { proj <- optProjectRef(index, current) @@ -25,7 +25,7 @@ object Act yield { val (key, conf) = keyConf val (task, extra) = taskExtra - ScopedKey( Scope( Select(proj), toAxis(conf map ConfigKey.apply, Global), task, extra), key ) + ScopedKey( Scope( toAxis(proj, Global), toAxis(conf map ConfigKey.apply, Global), task, extra), key ) } } def examplesStrict(p: Parser[String], exs: Set[String]): Parser[String] = @@ -41,7 +41,7 @@ object Act def config(confs: Set[String]): Parser[Option[String]] = token( (examplesStrict(ID, confs) | GlobalString) <~ ':' ).? - def configs(explicit: Option[String], defaultConfigs: ProjectRef => Seq[String], proj: ProjectRef): List[Option[String]] = + def configs(explicit: Option[String], defaultConfigs: Option[ResolvedReference] => Seq[String], proj: Option[ResolvedReference]): List[Option[String]] = explicit match { case None => None :: defaultConfigs(proj).map(c => Some(c)).toList @@ -49,7 +49,7 @@ object Act case Some(_) => explicit :: Nil } - def key(index: KeyIndex, proj: ProjectRef, confs: Seq[Option[String]], keyMap: Map[String,AttributeKey[_]]): Parser[(AttributeKey[_], Option[String])] = + def key(index: KeyIndex, proj: Option[ResolvedReference], confs: Seq[Option[String]], keyMap: Map[String,AttributeKey[_]]): Parser[(AttributeKey[_], Option[String])] = { val confMap = confs map { conf => (conf, index.keys(proj, conf)) } toMap; val allKeys = (Set.empty[String] /: confMap.values)(_ ++ _) @@ -103,17 +103,22 @@ object Act def knownIDParser[T](knownKeys: Map[String, T]): Parser[T] = token(examplesStrict(ID, knownKeys.keys.toSet)) map knownKeys - def projectRef(index: KeyIndex, currentBuild: URI): Parser[ProjectRef] = + def projectRef(index: KeyIndex, currentBuild: URI): Parser[Option[ResolvedReference]] = { - val uris = index.buildURIs - val build = token( '(' ~> Uri(uris).map(uri => Scope.resolveBuild(currentBuild, uri)) <~ ')') ?? currentBuild def projectID(uri: URI) = token( examplesStrict(ID, index projects uri) <~ '/' ) + def some[T](p: Parser[T]): Parser[Option[T]] = p map { v => Some(v) } - for(uri <- build; id <- projectID(uri)) yield - ProjectRef(uri, id) + val uris = index.buildURIs + val resolvedURI = Uri(uris).map(uri => Scope.resolveBuild(currentBuild, uri)) + val buildRef = token( '[' ~> resolvedURI <~ "]/" ) map { uri => BuildRef(uri) } + val global = token(GlobalString <~ '/') ^^^ None + val build = token( '(' ~> resolvedURI <~ ')') ?? currentBuild + val projectRef = for(uri <- build; id <- projectID(uri)) yield ProjectRef(uri, id) + + global | some(buildRef) | some(projectRef) } - def optProjectRef(index: KeyIndex, current: ProjectRef) = - projectRef(index, current.build) ?? current + def optProjectRef(index: KeyIndex, current: ProjectRef): Parser[Option[ResolvedReference]] = + projectRef(index, current.build) ?? Some(current) def actParser(s: State): Parser[() => State] = requireSession(s, actParser0(s)) @@ -129,11 +134,15 @@ object Act def scopedKeyParser(extracted: Extracted): Parser[ScopedKey[_]] = { import extracted._ - val defaultConfs = (ref: ProjectRef) => if(Project.getProject(ref, structure).isDefined) defaultConfigs(structure.data)(ref) else Nil + def confs(uri: URI) = if(structure.units.contains(uri)) defaultConfigs(structure.data)(ProjectRef(uri, rootProject(uri))) else Nil + val defaultConfs: Option[ResolvedReference] => Seq[String] = { + case None => confs(structure.root) + case Some(BuildRef(uri)) => confs(uri) + case Some(ref: ProjectRef) => if(Project.getProject(ref, structure).isDefined) defaultConfigs(structure.data)(ref) else Nil + } scopedKey(structure.index.keyIndex, currentRef, defaultConfs, structure.index.keyMap) } - def requireSession[T](s: State, p: => Parser[T]): Parser[T] = if(s get sessionSettings isEmpty) failure("No project loaded") else p } \ No newline at end of file diff --git a/main/KeyIndex.scala b/main/KeyIndex.scala index 27c4de7d3..d1c3eae28 100644 --- a/main/KeyIndex.scala +++ b/main/KeyIndex.scala @@ -9,70 +9,84 @@ package sbt object KeyIndex { - def empty: ExtendableKeyIndex = new KeyIndex0(Map.empty) + def empty: ExtendableKeyIndex = new KeyIndex0(emptyBuildIndex) def apply(known: Seq[ScopedKey[_]]): ExtendableKeyIndex = (empty /: known) { _ add _ } def combine(indices: Seq[KeyIndex]): KeyIndex = new KeyIndex { def buildURIs = concat(_.buildURIs) def projects(uri: URI) = concat(_.projects(uri)) - def configs(proj: ResolvedReference) = concat(_.configs(proj)) - def keys(proj: ResolvedReference, conf: Option[String]) = concat(_.keys(proj, conf)) + def configs(proj: Option[ResolvedReference]) = concat(_.configs(proj)) + def keys(proj: Option[ResolvedReference], conf: Option[String]) = concat(_.keys(proj, conf)) def concat[T](f: KeyIndex => Set[T]): Set[T] = (Set.empty[T] /: indices)( (s,k) => s ++ f(k) ) } + private[sbt] def getOr[A,B](m: Map[A,B], key: A, or: B): B = m.getOrElse(key, or) + private[sbt] def keySet[A,B](m: Map[Option[A],B]): Set[A] = m.keys.flatten.toSet + private[sbt] val emptyAKeyIndex = new AKeyIndex(Map.empty) + private[sbt] val emptyProjectIndex = new ProjectIndex(Map.empty) + private[sbt] val emptyBuildIndex = new BuildIndex(Map.empty) } +import KeyIndex._ trait KeyIndex { def buildURIs: Set[URI] def projects(uri: URI): Set[String] - def configs(proj: ResolvedReference): Set[String] - def keys(proj: ResolvedReference, conf: Option[String]): Set[String] + def configs(proj: Option[ResolvedReference]): Set[String] + def keys(proj: Option[ResolvedReference], conf: Option[String]): Set[String] } trait ExtendableKeyIndex extends KeyIndex { def add(scoped: ScopedKey[_]): ExtendableKeyIndex } -private final class KeyIndex0(val data: Map[URI, Map[Option[String], Map[ Option[String], Set[String]] ]]) extends ExtendableKeyIndex +private final class AKeyIndex(val data: Map[ Option[String], Set[String]]) { - def buildURIs: Set[URI] = data.keys.toSet - def projects(uri: URI): Set[String] = get(data, uri).keys.flatten.toSet - def configs(project: ResolvedReference): Set[String] = confMap(project).keys.flatten.toSet - def keys(project: ResolvedReference, conf: Option[String]): Set[String] = get(confMap(project), conf) + def add(config: Option[String], key: AttributeKey[_]): AKeyIndex = + new AKeyIndex(data updated (config, keys(config) + key.label)) + def keys(conf: Option[String]): Set[String] = getOr(data, conf, Set.empty) + def configs: Set[String] = keySet(data) +} +private final class ProjectIndex(val data: Map[Option[String], AKeyIndex]) +{ + def add(id: Option[String], config: Option[String], key: AttributeKey[_]): ProjectIndex = + new ProjectIndex( data updated(id, confIndex(id).add(config, key) )) + def confIndex(id: Option[String]): AKeyIndex = getOr(data, id, emptyAKeyIndex) + def projects: Set[String] = keySet(data) +} +private final class BuildIndex(val data: Map[Option[URI], ProjectIndex]) +{ + def add(build: Option[URI], project: Option[String], config: Option[String], key: AttributeKey[_]): BuildIndex = + new BuildIndex( data updated(build, projectIndex(build).add(project,config,key) ) ) + def projectIndex(build: Option[URI]): ProjectIndex = getOr(data, build, emptyProjectIndex) + def builds: Set[URI] = keySet(data) +} +private final class KeyIndex0(val data: BuildIndex) extends ExtendableKeyIndex +{ + def buildURIs: Set[URI] = data.builds + def projects(uri: URI): Set[String] = data.projectIndex(Some(uri)).projects + def configs(project: Option[ResolvedReference]): Set[String] = confIndex(project).configs + def keys(project: Option[ResolvedReference], conf: Option[String]): Set[String] = confIndex(project).keys(conf) - def confMap(proj: ResolvedReference): Map[Option[String], Set[String]] = + def confIndex(proj: Option[ResolvedReference]): AKeyIndex = + { + val (build, project) = parts(proj) + data.projectIndex(build).confIndex(project) + } + def parts(proj: Option[Reference]): (Option[URI], Option[String]) = proj match { - case ProjectRef(uri, id) => get( get(data, uri), Some(id)) - case BuildRef(uri) => get( get(data, uri), None) + case Some(ProjectRef(uri, id)) => (Some(uri), Some(id)) + case Some(BuildRef(uri)) => (Some(uri), None) + case _ => (None, None) } - private[this] def get[A,B,C](m: Map[A,Map[B,C]], key: A): Map[B,C] = getOr(m, key, Map.empty) - private[this] def get[A,B](m: Map[A,Set[B]], key: A): Set[B] = getOr(m, key, Set.empty) - private[this] def getOr[A,B](m: Map[A,B], key: A, or: B): B = m.getOrElse(key, or) - def add(scoped: ScopedKey[_]): ExtendableKeyIndex = if(validID(scoped.key.label)) add0(scoped) else this private[this] def add0(scoped: ScopedKey[_]): ExtendableKeyIndex = - scoped.scope match - { - case Scope(Select(ref: ResolvedReference), config, _, _) => addRef(ref, config, scoped.key) - case _ => this - } - def addRef(ref: ResolvedReference, config: ScopeAxis[ConfigKey], key: AttributeKey[_]): ExtendableKeyIndex = - ref match - { - case BuildRef(uri) => add(uri, None, config, key) - case ProjectRef(uri, id) => add(uri, Some(id), config, key) - } - def add(uri: URI, id: Option[String], config: ScopeAxis[ConfigKey], key: AttributeKey[_]): ExtendableKeyIndex = - add(uri, id, config match { case Select(c) => Some(c.name); case _ => None }, key) - def add(uri: URI, id: Option[String], config: Option[String], key: AttributeKey[_]): ExtendableKeyIndex = { - val projectMap = get(data, uri) - val configMap = get(projectMap, id) - val newSet = get(configMap, config) + key.label - val newProjectMap = projectMap.updated(id, configMap.updated(config, newSet)) - new KeyIndex0( data.updated(uri, newProjectMap) ) + val (build, project) = parts(scoped.scope.project.toOption) + add(build, project, scoped.scope.config, scoped.key) } + def add(uri: Option[URI], id: Option[String], config: ScopeAxis[ConfigKey], key: AttributeKey[_]): ExtendableKeyIndex = + new KeyIndex0( data.add(uri, id, config match { case Select(c) => Some(c.name); case _ => None }, key) ) }