provide access to per-build and build-global keys from command line

[build-uri]/key for per-build
*/key for build-global
This commit is contained in:
Mark Harrah 2011-03-25 21:42:04 -04:00
parent 135609e5b0
commit aa395583b5
2 changed files with 72 additions and 49 deletions

View File

@ -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
}

View File

@ -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) )
}