new aggregation approach, still need exclusion mechanism

This commit is contained in:
Mark Harrah 2012-01-09 08:00:29 -05:00
parent ec48779829
commit 82326cc899
12 changed files with 272 additions and 139 deletions

View File

@ -7,11 +7,13 @@ package sbt
import Keys.{sessionSettings, thisProject}
import Load.BuildStructure
import complete.{DefaultParsers, Parser}
import Aggregation.{KeyValue,Values}
import DefaultParsers._
import Types.idFun
import java.net.URI
import CommandSupport.ShowCommand
final class ParsedKey(val key: ScopedKey[_], val mask: ScopeMask)
object Act
{
val GlobalString = "*"
@ -19,29 +21,46 @@ object Act
// this does not take aggregation into account
def scopedKey(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String],
keyMap: Map[String, AttributeKey[_]], data: Settings[Scope]): Parser[ScopedKey[_]] =
scopedKeySelected(index, current, defaultConfigs, keyMap, data).map(_.key)
// 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
Resolve.aggregateDeps(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] =
scopedKeyFull(index, current, defaultConfigs, keyMap) flatMap { choices =>
select(choices, data)( Project.showRelativeKey(current, index.buildURIs.size > 1) )
}
def scopedKeyFull(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String], keyMap: Map[String, AttributeKey[_]]): Parser[Seq[Parser[ParsedKey]]] =
{
implicit val show = Project.showRelativeKey(current, index.buildURIs.size > 1)
def taskKeyExtra(proj: Option[ResolvedReference], confAmb: ParsedAxis[String]): Seq[Parser[ScopedKey[_]]] =
def taskKeyExtra(proj: Option[ResolvedReference], confAmb: ParsedAxis[String], baseMask: ScopeMask): Seq[Parser[ParsedKey]] =
for {
conf <- configs(confAmb, defaultConfigs, proj, index)
} yield for {
task <- taskAxis(conf, index.tasks(proj, conf), keyMap) map resolveTask
key <- key(index, proj, conf, task, keyMap, data)
taskAmb <- taskAxis(conf, index.tasks(proj, conf), keyMap)
task = resolveTask(taskAmb)
key <- key(index, proj, conf, task, keyMap)
extra <- extraAxis(keyMap, IMap.empty)
} yield
makeScopedKey( proj, conf, task, extra, key )
} yield {
val mask = baseMask.copy(task = taskAmb.isExplicit, extra = true)
new ParsedKey( makeScopedKey( proj, conf, task, extra, key ), mask)
}
for {
proj <- optProjectRef(index, current)
rawProject <- optProjectRef(index, current)
proj = resolveProject(rawProject, current)
confAmb <- config( index configs proj )
result <- select(taskKeyExtra(proj, confAmb), data)
partialMask = ScopeMask(rawProject.isExplicit, confAmb.isExplicit, false, false)
} yield
result
taskKeyExtra(proj, confAmb, partialMask)
}
def makeScopedKey(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]], extra: ScopeAxis[AttributeMap], key: AttributeKey[_]): ScopedKey[_] =
ScopedKey( Scope( toAxis(proj, Global), toAxis(conf map ConfigKey.apply, Global), toAxis(task, Global), extra), key )
def select(allKeys: Seq[Parser[ScopedKey[_]]], data: Settings[Scope])(implicit show: Show[ScopedKey[_]]): Parser[ScopedKey[_]] =
def select(allKeys: Seq[Parser[ParsedKey]], data: Settings[Scope])(implicit show: Show[ScopedKey[_]]): Parser[ParsedKey] =
seq(allKeys) flatMap { ss =>
val default = ss.headOption match {
case None => noValidKeys
@ -49,27 +68,28 @@ object Act
}
selectFromValid(ss filter isValid(data), default)
}
def selectFromValid(ss: Seq[ScopedKey[_]], default: Parser[ScopedKey[_]])(implicit show: Show[ScopedKey[_]]): Parser[ScopedKey[_]] =
def selectFromValid(ss: Seq[ParsedKey], default: Parser[ParsedKey])(implicit show: Show[ScopedKey[_]]): Parser[ParsedKey] =
selectByTask(selectByConfig(ss)) match
{
case Seq() => default
case Seq(single) => success(single)
case multi => failure("Ambiguous keys: " + showAmbiguous(multi))
case multi => failure("Ambiguous keys: " + showAmbiguous(keys(multi)))
}
def selectByConfig(ss: Seq[ScopedKey[_]]): Seq[ScopedKey[_]] =
private[this] def keys(ss: Seq[ParsedKey]): Seq[ScopedKey[_]] = ss.map(_.key)
def selectByConfig(ss: Seq[ParsedKey]): Seq[ParsedKey] =
ss match
{
case Seq() => Nil
case Seq(x, tail @ _*) => // select the first configuration containing a valid key
tail.takeWhile(_.scope.config == x.scope.config) match
tail.takeWhile(_.key.scope.config == x.key.scope.config) match
{
case Seq() => x :: Nil
case xs => x +: xs
}
}
def selectByTask(ss: Seq[ScopedKey[_]]): Seq[ScopedKey[_]] =
def selectByTask(ss: Seq[ParsedKey]): Seq[ParsedKey] =
{
val (selects, globals) = ss.partition(_.scope.task.isSelect)
val (selects, globals) = ss.partition(_.key.scope.task.isSelect)
if(globals.nonEmpty) globals else selects
}
@ -78,8 +98,11 @@ object Act
def showAmbiguous(keys: Seq[ScopedKey[_]])(implicit show: Show[ScopedKey[_]]): String =
keys.take(3).map(x => show(x)).mkString("", ", ", if(keys.size > 3) ", ..." else "")
def isValid(data: Settings[Scope])(key: ScopedKey[_]): Boolean =
def isValid(data: Settings[Scope])(parsed: ParsedKey): Boolean =
{
val key = parsed.key
data.definingScope(key.scope, key.key) == Some(key.scope)
}
def examples(p: Parser[String], exs: Set[String], label: String): Parser[String] =
p !!! ("Expected " + label) examples exs
@ -90,8 +113,6 @@ object Act
p.? map { opt => toAxis(opt, ifNone) }
def toAxis[T](opt: Option[T], ifNone: ScopeAxis[T]): ScopeAxis[T] =
opt match { case Some(t) => Select(t); case None => ifNone }
def defaultConfigs(data: Settings[Scope])(project: ProjectRef): Seq[String] =
thisProject in project get data map( _.configurations.map(_.name)) getOrElse Nil
def config(confs: Set[String]): Parser[ParsedAxis[String]] =
token( (GlobalString ^^^ ParsedGlobal | value(examples(ID, confs, "configuration")) ) <~ ':' ) ?? Omitted
@ -106,8 +127,7 @@ object Act
def nonEmptyConfig(index: KeyIndex, proj: Option[ResolvedReference]): String => Seq[Option[String]] = config =>
if(index.isEmpty(proj, Some(config))) Nil else Some(config) :: Nil
def key(index: KeyIndex, proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]], keyMap: Map[String,AttributeKey[_]], data: Settings[Scope]):
Parser[AttributeKey[_]] =
def key(index: KeyIndex, proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]], keyMap: Map[String,AttributeKey[_]]): Parser[AttributeKey[_]] =
{
def keyParser(keys: Set[String]): Parser[AttributeKey[_]] =
token(ID !!! "Expected key" examples keys) flatMap { keyString=>
@ -169,10 +189,10 @@ object Act
def knownIDParser[T](knownKeys: Map[String, T], label: String): Parser[T] =
token(examplesStrict(ID, knownKeys.keys.toSet, label)) map knownKeys
def projectRef(index: KeyIndex, currentBuild: URI): Parser[Option[ResolvedReference]] =
def projectRef(index: KeyIndex, currentBuild: URI): Parser[ParsedAxis[ResolvedReference]] =
{
val global = token(GlobalString ~ '/') ^^^ None
global | some(resolvedReference(index, currentBuild, '/'))
val global = token(GlobalString ~ '/') ^^^ ParsedGlobal
global | value(resolvedReference(index, currentBuild, '/'))
}
def resolvedReference(index: KeyIndex, currentBuild: URI, trailing: Parser[_]): Parser[ResolvedReference] =
{
@ -188,37 +208,55 @@ object Act
case Some(uri) => projectRef(uri) | token(trailing ^^^ BuildRef(uri))
}
}
def optProjectRef(index: KeyIndex, current: ProjectRef): Parser[Option[ResolvedReference]] =
projectRef(index, current.build) ?? Some(current)
def optProjectRef(index: KeyIndex, current: ProjectRef): Parser[ParsedAxis[ResolvedReference]] =
projectRef(index, current.build) ?? Omitted
def resolveProject(parsed: ParsedAxis[ResolvedReference], current: ProjectRef): Option[ResolvedReference] =
parsed match
{
case Omitted => Some(current)
case ParsedGlobal => None
case pv: ParsedValue[ResolvedReference] => Some(pv.value)
}
def actParser(s: State): Parser[() => State] = requireSession(s, actParser0(s))
private[this] def actParser0(state: State) =
private[this] def actParser0(state: State): Parser[() => State] =
{
val extracted = Project extract state
import extracted.{showKey, structure}
showParser.flatMap { show =>
scopedKeyParser(extracted) flatMap Aggregation.valueParser(state, structure, show)
aggregatedKeyParser(extracted) flatMap { kvs =>
Aggregation.evaluatingParser(state, structure, show)( keyValues(structure)(kvs) )
}
}
}
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[_]] =
{
def confs(uri: URI) = if(structure.units.contains(uri)) defaultConfigs(structure.data)(ProjectRef(uri, structure.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, structure.extra.configurationsForAxis, structure.index.keyMap, structure.data)
type KeysParser = Parser[Seq[ScopedKey[T]] forSome { type T}]
def aggregatedKeyParser(state: State): KeysParser = aggregatedKeyParser(Project extract state)
def aggregatedKeyParser(extracted: Extracted): KeysParser = aggregatedKeyParser(extracted.structure, extracted.currentRef)
def aggregatedKeyParser(structure: BuildStructure, currentRef: ProjectRef): KeysParser =
scopedKeyAggregated(currentRef, structure.extra.configurationsForAxis, structure)
def keyValues[T](state: State)(keys: Seq[ScopedKey[T]]): Values[T] = keyValues(Project extract state)(keys)
def keyValues[T](extracted: Extracted)(keys: Seq[ScopedKey[T]]): Values[T] = keyValues(extracted.structure)(keys)
def keyValues[T](structure: BuildStructure)(keys: Seq[ScopedKey[T]]): Values[T] =
keys.flatMap { key =>
structure.data.get(key.scope, key.key) map { value =>
KeyValue(key, value)
}
}
scopedKey(structure.index.keyIndex, currentRef, defaultConfs, structure.index.keyMap, structure.data)
}
def requireSession[T](s: State, p: => Parser[T]): Parser[T] =
if(s get sessionSettings isEmpty) failure("No project loaded") else p
sealed trait ParsedAxis[+T]
sealed trait ParsedAxis[+T] {
final def isExplicit = this != Omitted
}
final object ParsedGlobal extends ParsedAxis[Nothing]
final object Omitted extends ParsedAxis[Nothing]
final class ParsedValue[T](val value: T) extends ParsedAxis[T]

View File

@ -22,42 +22,6 @@ final object Aggregation
final class Explicit(val dependencies: Seq[ProjectReference], val transitive: Boolean) extends Aggregation
final case class KeyValue[+T](key: ScopedKey[_], value: T)
def getTasks[T](key: ScopedKey[T], structure: BuildStructure, transitive: Boolean): Seq[KeyValue[T]] =
getTasks0(key, structure, transitive, new mutable.HashMap[(ScopedKey[_], Boolean), Seq[KeyValue[T]]])
private type Memo[T] = mutable.Map[(ScopedKey[_], Boolean), Seq[KeyValue[T]]]
private[this] def getTasks0[T](key: ScopedKey[T], structure: BuildStructure, transitive: Boolean, memo: Memo[T]): Seq[KeyValue[T]] =
memo.getOrElseUpdate( (key, transitive), {
val task = structure.data.get(key.scope, key.key).toList.map(t => KeyValue(key,t))
if(transitive) aggregateDeps(key, structure, memo) ++ task else task
})
def projectAggregate(key: ScopedKey[_], structure: BuildStructure): Seq[ProjectRef] =
{
val project = key.scope.project.toOption.flatMap { ref => Project.getProjectForReference(ref, structure) }
project match { case Some(p) => p.aggregate; case None => Nil }
}
private[this] def aggregateDeps[T](key: ScopedKey[T], structure: BuildStructure, memo: Memo[T]): Seq[KeyValue[T]] =
{
val aggregated = aggregate in Scope.fillTaskAxis(key.scope, key.key) get structure.data getOrElse Enabled
val (agg, transitive) =
aggregated match
{
case Implicit(false) => (Nil, false)
case Implicit(true) => (projectAggregate(key, structure), true)
case e: Explicit => (e.dependencies, e.transitive)
}
val currentBuild = key.scope.project.toOption.flatMap { case ProjectRef(uri, _) => Some(uri); case BuildRef(ref) => Some(ref); case _ => None }
agg flatMap { a =>
val resolved = subCurrentBuild(a, currentBuild)
val newKey = ScopedKey(key.scope.copy(project = Select(resolved)), key.key)
getTasks(newKey, structure, transitive)
}
}
private def subCurrentBuild(ref: Reference, currentBuild: Option[URI]): Reference =
currentBuild match
{
case None => ref
case Some(current) => Scope.resolveBuildOnly(current, ref)
}
def printSettings[T](xs: Seq[KeyValue[T]], log: Logger)(implicit display: Show[ScopedKey[_]]) =
xs match
@ -66,6 +30,7 @@ final object Aggregation
case _ => xs foreach { case KeyValue(key, value) => log.info(display(key) + "\n\t" + value.toString) }
}
type Values[T] = Seq[KeyValue[T]]
type AnyKeys = Values[_]
def seqParser[T](ps: Values[Parser[T]]): Parser[Seq[KeyValue[T]]] = seq(ps.map { case KeyValue(k,p) => p.map(v => KeyValue(k,v) ) })
def applyTasks[T](s: State, structure: BuildStructure, ps: Values[Parser[Task[T]]], show: Boolean)(implicit display: Show[ScopedKey[_]]): Parser[() => State] =
@ -141,10 +106,11 @@ final object Aggregation
runTasks(s, structure, roots, dummies, show)
}
}
def valueParser(s: State, structure: BuildStructure, show: Boolean)(key: ScopedKey[_])(implicit display: Show[ScopedKey[_]]): Parser[() => State] =
getTasks(key, structure, true).toList match
def evaluatingParser[T](s: State, structure: BuildStructure, show: Boolean)(keys: Seq[KeyValue[T]])(implicit display: Show[ScopedKey[_]]): Parser[() => State] =
keys.toList match
{
case Nil => failure("No such setting/task: " + display(key))
case Nil => failure("No such setting/task")
case xs @ KeyValue(_, _: InputStatic[t]) :: _ => applyTasks(s, structure, maps(xs.asInstanceOf[Values[InputStatic[t]]])(_.parser(s)), show)
case xs @ KeyValue(_, _: InputDynamic[t]) :: _ => applyDynamicTasks(s, structure, xs.asInstanceOf[Values[InputDynamic[t]]], show)
case xs @ KeyValue(_, _: Task[t]) :: _ => applyTasks(s, structure, maps(xs.asInstanceOf[Values[Task[t]]])(x => success(x)), show)

39
main/BuildUtil.scala Normal file
View File

@ -0,0 +1,39 @@
package sbt
import java.net.URI
final class BuildUtil[Proj](
val keyIndex: KeyIndex,
val root: URI,
val rootProjectID: URI => String,
val project: (URI, String) => Proj,
val configurations: Proj => Seq[ConfigKey],
val aggregates: Relation[ProjectRef, ProjectRef]
)
{
def rootProject(uri: URI): Proj =
project(uri, rootProjectID(uri))
def resolveRef(ref: Reference): ResolvedReference =
Scope.resolveReference(root, rootProjectID, ref)
def projectFor(ref: ResolvedReference): Proj = ref match {
case ProjectRef(uri, id) => project(uri, id)
case BuildRef(uri) => rootProject(uri)
}
def projectRefFor(ref: ResolvedReference): ProjectRef = ref match {
case p: ProjectRef => p
case BuildRef(uri) => ProjectRef(uri, rootProjectID(uri))
}
def projectForAxis(ref: Option[ResolvedReference]): Proj = ref match {
case Some(ref) => projectFor(ref)
case None => rootProject(root)
}
def exactProject(refOpt: Option[Reference]): Option[Proj] = refOpt map resolveRef flatMap {
case ProjectRef(uri, id) => Some(project(uri, id))
case _ => None
}
val configurationsForAxis: Option[ResolvedReference] => Seq[String] =
refOpt => configurations(projectForAxis(refOpt)).map(_.name)
}

View File

@ -5,6 +5,7 @@ package sbt
import java.net.URI
import Project.ScopedKey
import Load.BuildStructure
import complete.DefaultParsers.validID
import Types.{idFun, some}
@ -13,6 +14,9 @@ object KeyIndex
def empty: ExtendableKeyIndex = new KeyIndex0(emptyBuildIndex)
def apply(known: Iterable[ScopedKey[_]]): ExtendableKeyIndex =
(empty /: known) { _ add _ }
def aggregate(known: Iterable[ScopedKey[_]], extra: BuildUtil[_]): ExtendableKeyIndex =
(empty /: known) { (index, key) => index.addAggregated(key, extra) }
def combine(indices: Seq[KeyIndex]): KeyIndex = new KeyIndex {
def buildURIs = concat(_.buildURIs)
def projects(uri: URI) = concat(_.projects(uri))
@ -52,6 +56,7 @@ trait KeyIndex
trait ExtendableKeyIndex extends KeyIndex
{
def add(scoped: ScopedKey[_]): ExtendableKeyIndex
def addAggregated(scoped: ScopedKey[_], extra: BuildUtil[_]): ExtendableKeyIndex
}
// task axis <-> key
private final class AKeyIndex(val data: Relation[ Option[AttributeKey[_]], String])
@ -110,13 +115,22 @@ private final class KeyIndex0(val data: BuildIndex) extends ExtendableKeyIndex
}
private[this] def optConfigs(project: Option[ResolvedReference]): Seq[Option[String]] = None +: (configs(project).toSeq map some.fn)
def addAggregated(scoped: ScopedKey[_], extra: BuildUtil[_]): ExtendableKeyIndex =
if(validID(scoped.key.label))
{
val aggregateProjects = Resolve.aggregateDeps(scoped, ScopeMask(), extra, reverse = true)
((this: ExtendableKeyIndex) /: aggregateProjects)(_ add _)
}
else
this
def add(scoped: ScopedKey[_]): ExtendableKeyIndex =
if(validID(scoped.key.label)) add0(scoped) else this
private[this] def add0(scoped: ScopedKey[_]): ExtendableKeyIndex =
{
val (build, project) = parts(scoped.scope.project.toOption)
add(build, project, scoped.scope.config, scoped.scope.task, scoped.key)
add1(build, project, scoped.scope.config, scoped.scope.task, scoped.key)
}
def add(uri: Option[URI], id: Option[String], config: ScopeAxis[ConfigKey], task: ScopeAxis[AttributeKey[_]], key: AttributeKey[_]): ExtendableKeyIndex =
private[this] def add1(uri: Option[URI], id: Option[String], config: ScopeAxis[ConfigKey], task: ScopeAxis[AttributeKey[_]], key: AttributeKey[_]): ExtendableKeyIndex =
new KeyIndex0( data.add(uri, id, config.toOption.map(_.name), task.toOption, key) )
}

View File

@ -121,7 +121,7 @@ object Load
val settings = finalTransforms(buildConfigurations(loaded, getRootProject(projects), rootEval, config.injectSettings))
val delegates = config.delegates(loaded)
val data = Project.makeSettings(settings, delegates, config.scopeLocal)( Project.showLoadingKey( loaded ) )
val index = structureIndex(data, settings)
val index = structureIndex(data, settings, loaded.extra)
val streams = mkStreams(projects, loaded.root, data)
(rootEval, new BuildStructure(projects, loaded.root, settings, data, index, streams, delegates, config.scopeLocal))
}
@ -160,12 +160,14 @@ object Load
def setDefinitionKey[T](tk: Task[T], key: ScopedKey[_]): Task[T] =
if(isDummy(tk)) tk else Task(tk.info.set(Keys.taskDefinitionKey, key), tk.work)
def structureIndex(data: Settings[Scope], settings: Seq[Setting[_]]): StructureIndex =
def structureIndex(data: Settings[Scope], settings: Seq[Setting[_]], extra: KeyIndex => BuildUtil[_]): StructureIndex =
{
val keys = Index.allKeys(settings)
val attributeKeys = Index.attributeKeys(data) ++ keys.map(_.key)
val scopedKeys = keys ++ data.allKeys( (s,k) => ScopedKey(s,k))
new StructureIndex(Index.stringToKeyMap(attributeKeys), Index.taskToKeyMap(data), Index.triggers(data), KeyIndex(scopedKeys))
val keyIndex = KeyIndex(scopedKeys)
val aggIndex = KeyIndex.aggregate(scopedKeys, extra(keyIndex))
new StructureIndex(Index.stringToKeyMap(attributeKeys), Index.taskToKeyMap(data), Index.triggers(data), keyIndex, aggIndex)
}
// Reevaluates settings after modifying them. Does not recompile or reload any build components.
@ -173,7 +175,7 @@ object Load
{
val transformed = finalTransforms(newSettings)
val newData = Project.makeSettings(transformed, structure.delegates, structure.scopeLocal)
val newIndex = structureIndex(newData, transformed)
val newIndex = structureIndex(newData, transformed, index => buildUtil(structure.root, structure.units, index))
val newStreams = mkStreams(structure.units, structure.root, newData)
new BuildStructure(units = structure.units, root = structure.root, settings = transformed, data = newData, index = newIndex, streams = newStreams, delegates = structure.delegates, scopeLocal = structure.scopeLocal)
}
@ -545,6 +547,7 @@ object Load
{
checkCycles(units)
def allProjectRefs: Seq[(ProjectRef, ResolvedProject)] = for( (uri, unit) <- units.toSeq; (id, proj) <- unit.defined ) yield ProjectRef(uri, id) -> proj
def extra(keyIndex: KeyIndex): BuildUtil[ResolvedProject] = buildUtil(root, units, keyIndex)
}
def checkCycles(units: Map[URI, LoadedBuildUnit])
{
@ -567,6 +570,7 @@ object Load
final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, ResolvedProject], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase
{
assert(!rootProjects.isEmpty, "No root projects defined for build unit " + unit)
val root = rootProjects.head
def localBase = unit.localBase
def classpath: Seq[File] = unit.definitions.target ++ unit.plugins.classpath
def loader = unit.definitions.loader
@ -584,8 +588,16 @@ object Load
def allProjects(build: URI): Seq[ResolvedProject] = units(build).defined.values.toSeq
def allProjectRefs: Seq[ProjectRef] = units.toSeq flatMap { case (build, unit) => refs(build, unit.defined.values.toSeq) }
def allProjectRefs(build: URI): Seq[ProjectRef] = refs(build, allProjects(build))
val extra: BuildUtil[ResolvedProject] = buildUtil(root, units, index.keyIndex)
private[this] def refs(build: URI, projects: Seq[ResolvedProject]): Seq[ProjectRef] = projects.map { p => ProjectRef(build, p.id) }
}
def buildUtil(root: URI, units: Map[URI, LoadedBuildUnit], keyIndex: KeyIndex): BuildUtil[ResolvedProject] =
{
val getp = (build: URI, project: String) => Load.getProject(units, build, project)
val configs = (_: ResolvedProject).configurations.map(c => ConfigKey(c.name))
val aggregates = Resolve.aggregates(units)
new BuildUtil(keyIndex, root, Load getRootProject units, getp, configs, aggregates)
}
final case class LoadBuildConfiguration(stagingDirectory: File, classpath: Seq[Attributed[File]], loader: ClassLoader, compilers: Compilers, evalPluginDef: (BuildStructure, State) => Seq[Attributed[File]], definesClass: DefinesClass, delegates: LoadedBuild => Scope => Seq[Scope], scopeLocal: ScopeLocal, injectSettings: InjectSettings, globalPlugin: Option[GlobalPlugin], log: Logger)
{
lazy val (globalPluginClasspath, globalPluginLoader) = pluginDefinitionLoader(this, Load.globalPluginClasspath(globalPlugin))
@ -594,5 +606,11 @@ object Load
final case class InjectSettings(global: Seq[Setting[_]], project: Seq[Setting[_]], projectLoaded: ClassLoader => Seq[Setting[_]])
// information that is not original, but can be reconstructed from the rest of BuildStructure
final class StructureIndex(val keyMap: Map[String, AttributeKey[_]], val taskToKey: Map[Task[_], ScopedKey[Task[_]]], val triggers: Triggers[Task], val keyIndex: KeyIndex)
final class StructureIndex(
val keyMap: Map[String, AttributeKey[_]],
val taskToKey: Map[Task[_], ScopedKey[Task[_]]],
val triggers: Triggers[Task],
val keyIndex: KeyIndex,
val aggregateKeyIndex: KeyIndex
)
}

View File

@ -8,6 +8,7 @@ package sbt
import HistoryCommands.{Start => HistoryPrefix}
import compiler.EvalImports
import Types.{const,idFun}
import Aggregation.AnyKeys
import Command.applyEffect
import Keys.{analysis,historyPath,globalLogging,shellPrompt}
@ -427,9 +428,9 @@ object BuiltinCommands
}
}
def lastGrep = Command(LastGrepCommand, lastGrepBrief, lastGrepDetailed)(lastGrepParser) {
case (s, (pattern,Some(sk))) =>
case (s, (pattern,Some(sks))) =>
val (str, ref, display) = extractLast(s)
Output.lastGrep(sk, str, str.streams(s), pattern, printLast(s))(display)
Output.lastGrep(sks, str.streams(s), pattern, printLast(s))(display)
keepLastLog(s)
case (s, (pattern, None)) =>
for(logFile <- lastLogFile(s)) yield
@ -460,13 +461,17 @@ object BuiltinCommands
val keyMap = Project.structure(s).index.keyMap
token(Space ~> (ID !!! "Expected key" examples keyMap.keySet)) flatMap { key => Act.getKey(keyMap, key, idFun) }
}
val spacedKeyParser = (s: State) => Act.requireSession(s, token(Space) ~> Act.scopedKeyParser(s))
val optSpacedKeyParser = (s: State) => spacedKeyParser(s).?
def lastGrepParser(s: State) = Act.requireSession(s, (token(Space) ~> token(NotSpace, "<pattern>")) ~ optSpacedKeyParser(s))
def last = Command(LastCommand, lastBrief, lastDetailed)(optSpacedKeyParser) {
case (s,Some(sk)) =>
val spacedAggregatedParser = (s: State) => Act.requireSession(s, token(Space) ~> Act.aggregatedKeyParser(s))
val aggregatedKeyValueParser: State => Parser[Option[AnyKeys]] =
(s: State) => spacedAggregatedParser(s).map(x => Act.keyValues(s)(x) ).?
def lastGrepParser(s: State) = Act.requireSession(s, (token(Space) ~> token(NotSpace, "<pattern>")) ~ aggregatedKeyValueParser(s))
def last = Command(LastCommand, lastBrief, lastDetailed)(aggregatedKeyValueParser) {
case (s,Some(sks)) =>
val (str, ref, display) = extractLast(s)
Output.last(sk, str, str.streams(s), printLast(s))(display)
Output.last(sks, str.streams(s), printLast(s))(display)
keepLastLog(s)
case (s, None) =>
for(logFile <- lastLogFile(s)) yield

View File

@ -7,8 +7,7 @@ package sbt
import java.io.File
import Keys.{Streams, TaskStreams}
import Project.ScopedKey
import Load.BuildStructure
import Aggregation.{getTasks, KeyValue}
import Aggregation.{KeyValue, Values}
import Types.idFun
import annotation.tailrec
import scala.Console.{BOLD, RESET}
@ -30,16 +29,16 @@ object Output
}
final val DefaultTail = "> "
def last(key: ScopedKey[_], structure: BuildStructure, streams: Streams, printLines: Seq[String] => Unit)(implicit display: Show[ScopedKey[_]]): Unit =
printLines( flatLines(lastLines(key, structure, streams))(idFun) )
def last(keys: Values[_], streams: Streams, printLines: Seq[String] => Unit)(implicit display: Show[ScopedKey[_]]): Unit =
printLines( flatLines(lastLines(keys, streams))(idFun) )
def last(file: File, printLines: Seq[String] => Unit, tailDelim: String = DefaultTail): Unit =
printLines(tailLines(file, tailDelim))
def lastGrep(key: ScopedKey[_], structure: BuildStructure, streams: Streams, patternString: String, printLines: Seq[String] => Unit)(implicit display: Show[ScopedKey[_]]): Unit =
def lastGrep(keys: Values[_], streams: Streams, patternString: String, printLines: Seq[String] => Unit)(implicit display: Show[ScopedKey[_]]): Unit =
{
val pattern = Pattern compile patternString
val lines = flatLines( lastLines(key, structure, streams) )(_ flatMap showMatches(pattern))
val lines = flatLines( lastLines(keys, streams) )(_ flatMap showMatches(pattern))
printLines( lines )
}
def lastGrep(file: File, patternString: String, printLines: Seq[String] => Unit, tailDelim: String = DefaultTail): Unit =
@ -47,7 +46,7 @@ object Output
def grep(lines: Seq[String], patternString: String): Seq[String] =
lines flatMap showMatches(Pattern compile patternString)
def flatLines(outputs: Seq[KeyValue[Seq[String]]])(f: Seq[String] => Seq[String])(implicit display: Show[ScopedKey[_]]): Seq[String] =
def flatLines(outputs: Values[Seq[String]])(f: Seq[String] => Seq[String])(implicit display: Show[ScopedKey[_]]): Seq[String] =
{
val single = outputs.size == 1
outputs flatMap { case KeyValue(key, lines) =>
@ -57,10 +56,9 @@ object Output
}
def bold(s: String) = if(ConsoleLogger.formatEnabled) BOLD + s + RESET else s
def lastLines(key: ScopedKey[_], structure: BuildStructure, streams: Streams): Seq[KeyValue[Seq[String]]] =
def lastLines(keys: Values[_], streams: Streams): Values[Seq[String]] =
{
val aggregated = Aggregation.getTasks(key, structure, true)
val outputs = aggregated map { case KeyValue(key, value) => KeyValue(key, lastLines(key, streams)) }
val outputs = keys map { case KeyValue(key, value) => KeyValue(key, lastLines(key, streams)) }
outputs.filterNot(_.value.isEmpty)
}
def lastLines(key: ScopedKey[_], mgr: Streams): Seq[String] = mgr.use(key) { s => IO.readLines(s.readText( Project.fillTaskAxis(key) )) }

View File

@ -85,7 +85,8 @@ final case class Extracted(structure: BuildStructure, session: SessionSettings,
def runAggregated[T](key: TaskKey[T], state: State): State =
{
val rkey = resolve(key.scopedKey)
val tasks = Aggregation.getTasks(rkey, structure, true)
val keys = Resolve.aggregateDeps(rkey, ScopeMask(), structure.extra)
val tasks = Act.keyValues(structure)(keys)
Aggregation.runTasks(state, structure, tasks, Aggregation.Dummies(KNil, HNil), show = false )(showKey)
}
private[this] def resolve[T](key: ScopedKey[T]): ScopedKey[T] =

84
main/Resolve.scala Normal file
View File

@ -0,0 +1,84 @@
package sbt
import java.net.URI
import Load.LoadedBuildUnit
object Resolve
{
def apply(index: BuildUtil[_], current: ScopeAxis[Reference], key: AttributeKey[_], mask: ScopeMask): Scope => Scope =
{
val rs =
resolveProject(current, mask) _ ::
resolveExtra(mask) _ ::
resolveTask(mask) _ ::
resolveConfig(index, key, mask) _ ::
Nil
scope => (scope /: rs) { (s, f) => f(s) }
}
def resolveTask(mask: ScopeMask)(scope: Scope): Scope =
if(mask.task) scope else scope.copy(task = Global)
def resolveProject(current: ScopeAxis[Reference], mask: ScopeMask)(scope: Scope): Scope =
if(mask.project) scope else scope.copy(project = current)
def resolveExtra(mask: ScopeMask)(scope: Scope): Scope =
if(mask.extra) scope else scope.copy(extra = Global)
def resolveConfig[P](index: BuildUtil[P], key: AttributeKey[_], mask: ScopeMask)(scope: Scope): Scope =
if(mask.config)
scope
else
{
val (resolvedRef, proj) = scope.project match {
case Select(ref) =>
val r = index resolveRef ref
(Some(r), index.projectFor(r))
case Global | This =>
(None, index.rootProject(index.root))
}
val task = scope.task.toOption
val keyIndex = index.keyIndex
val definesKey = (c: ScopeAxis[ConfigKey]) => keyIndex.keys(resolvedRef, c.toOption.map(_.name), task) contains key.label
val projectConfigs = index.configurations(proj).map(ck => Select(ck))
val config: ScopeAxis[ConfigKey] = (Global +: projectConfigs) find definesKey getOrElse Global
scope.copy(config = config)
}
import Load.BuildStructure
import Project.ScopedKey
def projectAggregate[Proj](proj: Option[Reference], extra: BuildUtil[Proj], reverse: Boolean): Seq[ProjectRef] =
{
val resRef = proj.map(p => extra.projectRefFor(extra.resolveRef(p)))
resRef.toList.flatMap(ref =>
if(reverse) extra.aggregates.reverse(ref) else extra.aggregates.forward(ref)
)
}
def aggregateDeps[T, Proj](key: ScopedKey[T], rawMask: ScopeMask, extra: BuildUtil[Proj], reverse: Boolean = false): Seq[ScopedKey[T]] =
{
val mask = rawMask.copy(project = true)
Dag.topologicalSort(key) { k =>
val kref = k.scope.project
for( ref <- projectAggregate(kref.toOption, extra, reverse)) yield
{
val toResolve = k.scope.copy(project = Select(ref))
val resolved = apply(extra, Global, k.key, mask)(toResolve)
ScopedKey(resolved, k.key)
}
}
}
def aggregates(units: Map[URI, LoadedBuildUnit]): Relation[ProjectRef, ProjectRef] =
{
val depPairs =
for {
(uri, unit) <- units.toIterable
project <- unit.defined.values
ref = ProjectRef(uri, project.id)
agg <- project.aggregate
} yield
(ref, agg)
Relation.empty ++ depPairs
}
}

View File

@ -65,7 +65,7 @@ object ParseKey extends Properties("Key parser test")
import skm.{structure, key}
val mask = ScopeMask(config = false)
val string = Project.displayMasked(key, mask)
val resolvedConfig = resolveConfig(structure, key.scope, key.key, mask).config
val resolvedConfig = Resolve.resolveConfig(structure.extra, key.key, mask)(key.scope).config
("Key: " + Project.displayFull(key)) |:
("Mask: " + mask) |:
@ -85,37 +85,7 @@ object ParseKey extends Properties("Key parser test")
final class StructureKeyMask(val structure: Structure, val key: ScopedKey[_], val mask: ScopeMask)
def resolve(structure: Structure, key: ScopedKey[_], mask: ScopeMask): ScopedKey[_] =
{
val scope = (key.scope /: List(resolveProject _, resolveExtra _, resolveTask _, resolveConfig _)) { (scope, f) =>
f(structure, scope, key.key, mask)
}
ScopedKey(scope, key.key)
}
def resolveTask(structure: Structure, scope: Scope, key: AttributeKey[_], mask: ScopeMask): Scope =
if(mask.task)
scope
else
scope.copy(task = Global)
def resolveProject(structure: Structure, scope: Scope, key: AttributeKey[_], mask: ScopeMask): Scope =
if(mask.project) scope else scope.copy(project = Select(structure.current))
def resolveExtra(structure: Structure, scope: Scope, key: AttributeKey[_], mask: ScopeMask): Scope =
if(mask.extra) scope else scope.copy(extra = Global)
def resolveConfig(structure: Structure, scope: Scope, key: AttributeKey[_], mask: ScopeMask): Scope =
if(mask.config)
scope
else
{
val env = structure.env
val resolved = env.resolveAxis(scope.project)
val project = env.projectFor( resolved )
val ref = scope.project.toOption.map(env.resolve)
val task = scope.task.toOption
val projectConfigs = project.configurations.map(c => Select(ConfigKey(c.name)))
val definesKey = (c: ScopeAxis[ConfigKey]) => structure.keyIndex.keys(ref, c.toOption.map(_.name), task) contains key.label
val config = (Global +: projectConfigs) find definesKey getOrElse Global
scope.copy(config = config)
}
ScopedKey(Resolve(structure.extra, Select(structure.current), key.key, mask)(key.scope), key.key)
def parseExpected(structure: Structure, s: String, expected: ScopedKey[_], mask: ScopeMask): Prop =
("Expected: " + Project.displayFull(expected)) |:

View File

@ -53,6 +53,12 @@ object TestBuild
(Scope.display(scope, "<key>"), showKeys(map))
scopeStrings.toSeq.sorted.map(t => t._1 + t._2).mkString("\n\t")
}
val extra: BuildUtil[Proj] =
{
val getp = (build: URI, project: String) => env.buildMap(build).projectMap(project)
new BuildUtil(keyIndex, env.root.uri, env.rootProject, getp, _.configurations.map(c => ConfigKey(c.name)), const(Nil))
}
lazy val allAttributeKeys: Set[AttributeKey[_]] = data.data.values.flatMap(_.keys).toSet
lazy val (taskAxes, globalTaskAxis, onlyTaskAxis, multiTaskAxis) =
{
@ -94,11 +100,6 @@ object TestBuild
val taskMap = mapBy(tasks)(getKey)
def project(ref: ProjectRef) = buildMap(ref.build).projectMap(ref.project)
def projectFor(ref: ResolvedReference) = ref match { case pr: ProjectRef => project(pr); case BuildRef(uri) => buildMap(uri).root }
def resolveAxis(ref: ScopeAxis[Reference]): ResolvedReference =
{
val rootRef = BuildRef(root.uri)
ref.foldStrict(resolve, rootRef, rootRef)
}
lazy val allProjects = builds.flatMap(_.allProjects)
def rootProject(uri: URI): String = buildMap(uri).root.id

View File

@ -17,7 +17,6 @@ object Relation
make(forward, reverse)
}
private[sbt] def remove[X,Y](map: M[X,Y], from: X, to: Y): M[X,Y] =
map.get(from) match {
case Some(tos) =>