add build level configuration

made ProjectRef semantics explicit
This commit is contained in:
Mark Harrah 2011-03-03 06:44:19 -05:00
parent 70972c6499
commit 60d9355b54
11 changed files with 330 additions and 224 deletions

View File

@ -14,10 +14,10 @@ package sbt
object Act
{
// this does not take delegation into account
def scopedKey(index: KeyIndex, currentBuild: URI, currentProject: String, defaultConfig: ProjectRef => Option[String], keyMap: Map[String, AttributeKey[_]]): Parser[ScopedKey[_]] =
def scopedKey(index: KeyIndex, current: ProjectRef, defaultConfig: ProjectRef => Option[String], keyMap: Map[String, AttributeKey[_]]): Parser[ScopedKey[_]] =
{
for {
proj <- optProjectRef(index, currentBuild, currentProject)
proj <- optProjectRef(index, current)
confAmb <- config( index configs proj )
(key, conf) <- key(index, proj, configs(confAmb, defaultConfig, proj), keyMap) }
yield
@ -59,10 +59,10 @@ object Act
def projectID(uri: URI) = token( examplesStrict(ID, index projects uri) <~ '/' )
for(uri <- build; id <- projectID(uri)) yield
ProjectRef(Some(uri), Some(id))
ProjectRef(uri, id)
}
def optProjectRef(index: KeyIndex, currentBuild: URI, currentProject: String) =
projectRef(index, currentBuild) ?? ProjectRef(Some(currentBuild), Some(currentProject))
def optProjectRef(index: KeyIndex, current: ProjectRef) =
projectRef(index, current.build) ?? current
def actParser(s: State): Parser[() => State] = requireSession(s, actParser0(s))
@ -79,7 +79,7 @@ object Act
{
import extracted._
val defaultConf = (ref: ProjectRef) => if(Project.getProject(ref, structure).isDefined) defaultConfig(structure.data)(ref) else None
scopedKey(structure.index.keyIndex, curi, cid, defaultConf, structure.index.keyMap)
scopedKey(structure.index.keyIndex, currentRef, defaultConf, structure.index.keyMap)
}

View File

@ -9,6 +9,7 @@ package sbt
import EvaluateTask.parseResult
import Keys.aggregate
import sbt.complete.Parser
import java.net.URI
import Parser._
sealed trait Aggregation
@ -19,7 +20,7 @@ final object Aggregation
val Enabled = new Implicit(true)
val Disabled = new Implicit(false)
final case class Implicit(enabled: Boolean) extends Aggregation
final class Explicit(val dependencies: Seq[ProjectRef], val transitive: Boolean) extends 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]] =
@ -29,7 +30,7 @@ final object Aggregation
}
def projectAggregate(key: ScopedKey[_], structure: BuildStructure): Seq[ProjectRef] =
{
val project = key.scope.project.toOption.flatMap { p => Project.getProject(p, structure) }
val project = key.scope.project.toOption.flatMap { ref => Project.getProjectForReference(ref, structure) }
project match { case Some(p) => p.aggregate; case None => Nil }
}
def aggregateDeps[T](key: ScopedKey[T], structure: BuildStructure): Seq[KeyValue[T]] =
@ -37,24 +38,24 @@ final object Aggregation
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 => (subCurrentBuild(key, e.dependencies), e.transitive)
{
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 newKey = ScopedKey(key.scope.copy(project = Select(a)), key.key)
val resolved = subCurrentBuild(a, currentBuild)
val newKey = ScopedKey(key.scope.copy(project = Select(resolved)), key.key)
getTasks(newKey, structure, transitive)
}
}
private def subCurrentBuild(key: ScopedKey[_], refs: Seq[ProjectRef]): Seq[ProjectRef] =
key.scope.project match
private def subCurrentBuild(ref: Reference, currentBuild: Option[URI]): Reference =
currentBuild match
{
case Select(ProjectRef(Some(current), _)) => refs map { ref => Scope.mapRefBuild(current, ref) }
case _ => refs
case None => ref
case Some(current) => Scope.resolveBuildOnly(current, ref)
}
def printSettings[T](xs: Seq[KeyValue[T]], log: Logger) =
xs match

View File

@ -145,7 +145,7 @@ object EvaluateTask
def getTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, streams: Streams, ref: ProjectRef): Option[(Task[T], Execute.NodeView[Task])] =
{
val thisScope = Scope(Select(ref), Global, Global, Global)
val thisScope = Load.projectScope(ref)
val resolvedScope = Scope.replaceThis(thisScope)( taskKey.scope )
for( t <- structure.data.get(resolvedScope, taskKey.key)) yield
(t, nodeView(state, streams))
@ -229,19 +229,31 @@ object Load
}
def defaultDelegates: LoadedBuild => Scope => Seq[Scope] = (lb: LoadedBuild) => {
val rootProject = getRootProject(lb.units)
def resolveRef(project: ProjectRef) = Scope.resolveRef(lb.root, rootProject, project)
def resolveRef(project: Reference): ResolvedReference = Scope.resolveReference(lb.root, rootProject, project)
Scope.delegates(
project => projectInherit(lb, resolveRef(project)),
(project, config) => configInherit(lb, resolveRef(project), config),
(project, config) => configInherit(lb, resolveRef(project), config, rootProject),
(project, task) => Nil,
(project, extra) => Nil
)
}
def configInherit(lb: LoadedBuild, ref: (URI, String), config: ConfigKey): Seq[ConfigKey] =
getConfiguration(lb.units, ref._1, ref._2, config).extendsConfigs.map(c => ConfigKey(c.name))
def projectInherit(lb: LoadedBuild, ref: (URI, String)): Seq[ProjectRef] =
getProject(lb.units, ref._1, ref._2).delegates
def configInherit(lb: LoadedBuild, ref: ResolvedReference, config: ConfigKey, rootProject: URI => String): Seq[ConfigKey] =
ref match
{
case pr: ProjectRef => configInheritRef(lb, pr, config)
case BuildRef(uri) => configInheritRef(lb, ProjectRef(uri, rootProject(uri)), config)
}
def configInheritRef(lb: LoadedBuild, ref: ProjectRef, config: ConfigKey): Seq[ConfigKey] =
getConfiguration(lb.units, ref.build, ref.project, config).extendsConfigs.map(c => ConfigKey(c.name))
def projectInherit(lb: LoadedBuild, ref: ResolvedReference): Seq[ProjectRef] =
ref match
{
case pr: ProjectRef => projectInheritRef(lb, pr)
case BuildRef(uri) => Nil
}
def projectInheritRef(lb: LoadedBuild, ref: ProjectRef): Seq[ProjectRef] =
getProject(lb.units, ref.build, ref.project).delegates
// build, load, and evaluate all units.
// 1) Compile all plugin definitions
@ -296,7 +308,7 @@ object Load
new BuildStructure(units = structure.units, root = structure.root, settings = newSettings, data = newData, index = newIndex, streams = newStreams, delegates = structure.delegates, scopeLocal = structure.scopeLocal)
}
def isProjectThis(s: Setting[_]) = s.key.scope.project == This
def isProjectThis(s: Setting[_]) = s.key.scope.project match { case This | Select(ThisProject) => true; case _ => false }
def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, rootEval: () => Eval): Seq[Setting[_]] =
loaded.units.toSeq flatMap { case (uri, build) =>
val eval = if(uri == loaded.root) rootEval else lazyEval(build.unit)
@ -304,7 +316,7 @@ object Load
val (pluginThisProject, pluginGlobal) = pluginSettings partition isProjectThis
val projectSettings = build.defined flatMap { case (id, project) =>
val srcs = configurationSources(project.base)
val ref = ProjectRef(Some(uri), Some(id))
val ref = ProjectRef(uri, id)
val defineConfig = for(c <- project.configurations) yield ( (configuration in (ref, ConfigKey(c.name))) :== c)
val settings =
(thisProject :== project) +:
@ -312,15 +324,14 @@ object Load
(defineConfig ++ project.settings ++ pluginThisProject ++ configurations(srcs, eval, build.imports))
// map This to thisScope, Select(p) to mapRef(uri, rootProject, p)
transformSettings(projectScope(uri, id), uri, rootProject, settings)
transformSettings(projectScope(ref), uri, rootProject, settings)
}
val buildScope = ThisScope.copy(project = Select(ProjectRef(Some(uri), None)))
val buildScope = Scope(Select(BuildRef(uri)), Global, Global, Global)
pluginGlobal ++ inScope(buildScope)(build.buildSettings) ++ projectSettings
}
def transformSettings(thisScope: Scope, uri: URI, rootProject: URI => String, settings: Seq[Setting[_]]): Seq[Setting[_]] =
Project.transform(Scope.resolveScope(thisScope, uri, rootProject), settings)
def projectScope(uri: URI, id: String): Scope = projectScope(ProjectRef(uri, id))
def projectScope(project: ProjectRef): Scope = Scope(Select(project), Global, Global, Global)
def projectScope(project: Reference): Scope = Scope(Select(project), Global, Global, Global)
def lazyEval(unit: BuildUnit): () => Eval =
@ -335,17 +346,17 @@ object Load
def configurations(srcs: Seq[File], eval: () => Eval, imports: Seq[String]): Seq[Setting[_]] =
if(srcs.isEmpty) Nil else EvaluateConfigurations(eval(), srcs, imports)
def load(file: File, s: State, config: LoadBuildConfiguration): LoadedBuild =
def load(file: File, s: State, config: LoadBuildConfiguration): PartBuild =
load(file, uri => loadUnit(uri, RetrieveUnit(config.stagingDirectory, uri), s, config) )
def load(file: File, loader: URI => BuildUnit): LoadedBuild = loadURI(IO.directoryURI(file), loader)
def loadURI(uri: URI, loader: URI => BuildUnit): LoadedBuild =
def load(file: File, loader: URI => BuildUnit): PartBuild = loadURI(IO.directoryURI(file), loader)
def loadURI(uri: URI, loader: URI => BuildUnit): PartBuild =
{
IO.assertAbsolute(uri)
val (referenced, map) = loadAll(uri :: Nil, Map.empty, loader, Map.empty)
checkAll(referenced, map)
new LoadedBuild(uri, map)
new PartBuild(uri, map)
}
def loaded(unit: BuildUnit): (LoadedBuildUnit, List[ProjectRef]) =
def loaded(unit: BuildUnit): (PartBuildUnit, List[ProjectReference]) =
{
val defined = projects(unit)
if(defined.isEmpty) error("No projects defined in build unit " + unit)
@ -357,12 +368,12 @@ object Load
val externals = referenced(defined).toList
val projectsInRoot = defined.filter(isRoot).map(_.id)
val rootProjects = if(projectsInRoot.isEmpty) defined.head.id :: Nil else projectsInRoot
(new LoadedBuildUnit(unit, defined.map(d => (d.id, d)).toMap, rootProjects, buildSettings(unit)), externals)
(new PartBuildUnit(unit, defined.map(d => (d.id, d)).toMap, rootProjects, buildSettings(unit)), externals)
}
def buildSettings(unit: BuildUnit): Seq[Setting[_]] =
unit.definitions.builds.flatMap(_.settings)
@tailrec def loadAll(bases: List[URI], references: Map[URI, List[ProjectRef]], externalLoader: URI => BuildUnit, builds: Map[URI, LoadedBuildUnit]): (Map[URI, List[ProjectRef]], Map[URI, LoadedBuildUnit]) =
@tailrec def loadAll(bases: List[URI], references: Map[URI, List[ProjectReference]], externalLoader: URI => BuildUnit, builds: Map[URI, PartBuildUnit]): (Map[URI, List[ProjectReference]], Map[URI, PartBuildUnit]) =
bases match
{
case b :: bs =>
@ -371,8 +382,8 @@ object Load
else
{
val (loadedBuild, refs) = loaded(externalLoader(b))
checkBuildBase(loadedBuild.localBase)
loadAll(refs.flatMap(_.uri) reverse_::: bs, references.updated(b, refs), externalLoader, builds.updated(b, loadedBuild))
checkBuildBase(loadedBuild.unit.localBase)
loadAll(refs.flatMap(Reference.uri) reverse_::: bs, references.updated(b, refs), externalLoader, builds.updated(b, loadedBuild))
}
case Nil => (references, builds)
}
@ -390,12 +401,19 @@ object Load
else if(!base.exists)
IO createDirectory base
}
def checkAll(referenced: Map[URI, List[ProjectRef]], builds: Map[URI, LoadedBuildUnit])
def resolveAll(builds: Map[URI, PartBuildUnit]): Map[URI, LoadedBuildUnit] =
{
val rootProject = getRootProject(builds)
builds map { case (uri,unit) =>
(uri, unit.resolveRefs( ref => Scope.resolveProjectRef(uri, rootProject, ref) ))
} toMap;
}
def checkAll(referenced: Map[URI, List[ProjectReference]], builds: Map[URI, PartBuildUnit])
{
val rootProject = getRootProject(builds)
for( (uri, refs) <- referenced; ref <- refs)
{
val (refURI, refID) = Scope.resolveRef(uri, rootProject, ref)
val ProjectRef(refURI, refID) = Scope.resolveProjectRef(uri, rootProject, ref)
val loadedUnit = builds(refURI)
if(! (loadedUnit.defined contains refID) )
error("No project '" + refID + "' in '" + refURI + "'")
@ -412,42 +430,36 @@ object Load
}
p => p.copy(base = resolve(p.base))
}
def resolveProjects(loaded: LoadedBuild): LoadedBuild =
def resolveProjects(loaded: PartBuild): LoadedBuild =
{
val rootProject = getRootProject(loaded.units)
new LoadedBuild(loaded.root, loaded.units.map { case (uri, unit) =>
new LoadedBuild(loaded.root, loaded.units map { case (uri, unit) =>
IO.assertAbsolute(uri)
(uri, resolveProjects(uri, unit, rootProject))
})
}
def resolveProjects(uri: URI, unit: LoadedBuildUnit, rootProject: URI => String): LoadedBuildUnit =
def resolveProjects(uri: URI, unit: PartBuildUnit, rootProject: URI => String): LoadedBuildUnit =
{
IO.assertAbsolute(uri)
val resolve = resolveProject(ref => Scope.mapRef(uri, rootProject, ref))
val resolve = (_: Project).resolve(ref => Scope.resolveProjectRef(uri, rootProject, ref))
new LoadedBuildUnit(unit.unit, unit.defined mapValues resolve, unit.rootProjects, unit.buildSettings)
}
def resolveProject(resolveRef: ProjectRef => ProjectRef): Project => Project =
{
def resolveRefs(prs: Seq[ProjectRef]) = prs map resolveRef
def resolveDeps(ds: Seq[Project.ClasspathDependency]) = ds map resolveDep
def resolveDep(d: Project.ClasspathDependency) = d.copy(project = resolveRef(d.project))
p => p.copy(aggregate = resolveRefs(p.aggregate), dependencies = resolveDeps(p.dependencies), delegates = resolveRefs(p.delegates))
}
def projects(unit: BuildUnit): Seq[Project] =
{
// we don't have the complete build graph loaded, so we don't have the rootProject function yet.
// Therefore, we use mapRefBuild instead of mapRef. After all builds are loaded, we can fully resolve ProjectRefs.
val resolve = resolveProject(ref => Scope.mapRefBuild(unit.uri, ref)) compose resolveBase(unit.localBase)
// Therefore, we use resolveProjectBuild instead of resolveProjectRef. After all builds are loaded, we can fully resolve ProjectReferences.
val resolveBuild = (_: Project).resolveBuild(ref => Scope.resolveProjectBuild(unit.uri, ref))
val resolve = resolveBuild compose resolveBase(unit.localBase)
unit.definitions.builds.flatMap(_.projects map resolve)
}
def getRootProject(map: Map[URI, LoadedBuildUnit]): URI => String =
def getRootProject(map: Map[URI, BuildUnitBase]): URI => String =
uri => getBuild(map, uri).rootProjects.headOption getOrElse emptyBuild(uri)
def getConfiguration(map: Map[URI, LoadedBuildUnit], uri: URI, id: String, conf: ConfigKey): Configuration =
getProject(map, uri, id).configurations.find(_.name == conf.name) getOrElse noConfiguration(uri, id, conf.name)
def getProject(map: Map[URI, LoadedBuildUnit], uri: URI, id: String): Project =
def getProject(map: Map[URI, LoadedBuildUnit], uri: URI, id: String): ResolvedProject =
getBuild(map, uri).defined.getOrElse(id, noProject(uri, id))
def getBuild(map: Map[URI, LoadedBuildUnit], uri: URI): LoadedBuildUnit =
def getBuild[T](map: Map[URI, T], uri: URI): T =
map.getOrElse(uri, noBuild(uri))
def emptyBuild(uri: URI) = error("No root project defined for build unit '" + uri + "'")
@ -562,7 +574,14 @@ object Load
}
final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit])
final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, Project], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]])
final class PartBuild(val root: URI, val units: Map[URI, PartBuildUnit])
sealed trait BuildUnitBase { def rootProjects: Seq[String]; def buildSettings: Seq[Setting[_]] }
final class PartBuildUnit(val unit: BuildUnit, val defined: Map[String, Project], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase
{
def resolve(f: Project => ResolvedProject): LoadedBuildUnit = new LoadedBuildUnit(unit, defined mapValues f, rootProjects, buildSettings)
def resolveRefs(f: ProjectReference => ProjectRef): LoadedBuildUnit = resolve(_ resolve f)
}
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)
def localBase = unit.localBase
@ -573,10 +592,7 @@ object Load
}
def getImports(unit: BuildUnit) = baseImports ++ importAll(unit.plugins.pluginNames ++ unit.definitions.buildNames)
// these are unresolved references
def referenced(definitions: Seq[Project]): Seq[ProjectRef] = definitions flatMap referenced
def referenced(definition: Project): Seq[ProjectRef] = definition.delegates ++ definition.aggregate ++ definition.dependencies.map(_.project)
def referenced[PR <: ProjectReference](definitions: Seq[ProjectDefinition[PR]]): Seq[PR] = definitions flatMap { _.referenced }
final class BuildStructure(val units: Map[URI, LoadedBuildUnit], val root: URI, val settings: Seq[Setting[_]], val data: Settings[Scope], val index: StructureIndex, val streams: Streams, val delegates: Scope => Seq[Scope], val scopeLocal: ScopeLocal)
final class LoadBuildConfiguration(val stagingDirectory: File, val classpath: Seq[File], val loader: ClassLoader, val compilers: Compilers, val evalPluginDef: (BuildStructure, State) => Seq[Attributed[File]], val delegates: LoadedBuild => Scope => Seq[Scope], val scopeLocal: ScopeLocal, val injectSettings: Seq[Setting[_]], val log: Logger)
@ -598,6 +614,7 @@ object BuildStreams
type Streams = std.Streams[ScopedKey[_]]
type TaskStreams = std.TaskStreams[ScopedKey[_]]
val GlobalPath = "$global"
val BuildUnitPath = "$build"
def mkStreams(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope], logRelativePath: Seq[String] = defaultLogPath): Streams =
std.Streams( path(units, root, logRelativePath), display, LogManager.construct(data) )
@ -631,7 +648,8 @@ object BuildStreams
scoped.scope.project match
{
case Global => (units(root).localBase, GlobalPath :: Nil)
case Select(ProjectRef(Some(uri), Some(id))) => (units(uri).defined(id).base, Nil)
case Select(BuildRef(uri)) => (units(uri).localBase, BuildUnitPath :: Nil)
case Select(ProjectRef(uri, id)) => (units(uri).defined(id).base, Nil)
case Select(pr) => error("Unresolved project reference (" + pr + ") in " + display(scoped))
case This => error("Unresolved project reference (This) in " + display(scoped))
}

View File

@ -61,7 +61,7 @@ object Default
fork :== false,
javaOptions :== Nil,
crossPaths :== true,
shellPrompt :== (_ => "> "),
// shellPrompt :== (_ => "> "),
aggregate :== Aggregation.Enabled,
maxErrors :== 100,
commands :== Nil,
@ -281,7 +281,7 @@ object Default
def discoverMainClasses(analysis: inc.Analysis): Seq[String] =
Discovery.applications(Test.allDefs(analysis)) collect { case (definition, discovered) if(discovered.hasMain) => definition.name }
def consoleProjectTask = (EvaluateTask.state, streams, initialCommands in consoleProject) map { (state, s, extra) => Console.sbt(state, extra)(s.log) }
def consoleProjectTask = (EvaluateTask.state, streams, initialCommands in consoleProject) map { (state, s, extra) => Console.sbt(state, extra)(s.log); println() }
def consoleTask: Initialize[Task[Unit]] = consoleTask(fullClasspath, console)
def consoleQuickTask = consoleTask(externalDependencyClasspath, consoleQuick)
def consoleTask(classpath: TaskKey[Classpath], task: TaskKey[_]): Initialize[Task[Unit]] = (compilers, classpath, scalacOptions in task, initialCommands in task, streams) map {
@ -341,14 +341,14 @@ object Default
}
def inAllDeps[T](base: ProjectRef, deps: ProjectRef => Seq[ProjectRef], key: ScopedSetting[T], data: Settings[Scope]): Seq[T] =
inAllProjects(Dag.topologicalSort(base)(deps), key, data)
def inAllProjects[T](allProjects: Seq[ProjectRef], key: ScopedSetting[T], data: Settings[Scope]): Seq[T] =
def inAllProjects[T](allProjects: Seq[Reference], key: ScopedSetting[T], data: Settings[Scope]): Seq[T] =
allProjects.flatMap { p => key in p get data }
val CompletionsID = "completions"
lazy val defaultWebPaths = inConfig(CompileConf)(webPaths)
def noAggregation = Seq(run, console, consoleQuick)
def noAggregation = Seq(run, console, consoleQuick, consoleProject)
lazy val disableAggregation = noAggregation map disableAggregate
def disableAggregate(k: Scoped) =
aggregate in Scope.GlobalScope.copy(task = Select(k.key)) :== false
@ -519,11 +519,11 @@ object Classpaths
def depMap: Initialize[Task[Map[ModuleRevisionId, ModuleDescriptor]]] =
(thisProject, thisProjectRef, settings) flatMap { (root, rootRef, data) =>
val dependencies = (p: (ProjectRef, Project)) => p._2.dependencies.flatMap(pr => thisProject in pr.project get data map { (pr.project, _) })
val dependencies = (p: (ProjectRef, ResolvedProject)) => p._2.dependencies.flatMap(pr => thisProject in pr.project get data map { (pr.project, _) })
depMap(Dag.topologicalSort((rootRef,root))(dependencies).dropRight(1), data)
}
def depMap(projects: Seq[(ProjectRef,Project)], data: Settings[Scope]): Task[Map[ModuleRevisionId, ModuleDescriptor]] =
def depMap(projects: Seq[(ProjectRef,ResolvedProject)], data: Settings[Scope]): Task[Map[ModuleRevisionId, ModuleDescriptor]] =
projects.flatMap { case (p,_) => ivyModule in p get data }.join.map { mods =>
(mods.map{ mod =>
val md = mod.moduleDescriptor
@ -547,17 +547,17 @@ object Classpaths
import java.util.LinkedHashSet
import collection.JavaConversions.asScalaSet
def interSort(projectRef: ProjectRef, project: Project, conf: Configuration, data: Settings[Scope]): Seq[(ProjectRef,String)] =
def interSort(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, data: Settings[Scope]): Seq[(ProjectRef,String)] =
{
val visited = asScalaSet(new LinkedHashSet[(ProjectRef,String)])
def visit(p: ProjectRef, project: Project, c: Configuration)
def visit(p: ProjectRef, project: ResolvedProject, c: Configuration)
{
val applicableConfigs = allConfigs(c)
for(ac <- applicableConfigs) // add all configurations in this project
visited add (p, ac.name)
val masterConfs = configurationNames(project)
for( Project.ClasspathDependency(dep, confMapping) <- project.dependencies)
for( Project.ResolvedClasspathDependency(dep, confMapping) <- project.dependencies)
{
val depProject = thisProject in dep get data getOrElse error("Invalid project: " + dep)
val mapping = mapped(confMapping, masterConfs, configurationNames(depProject), "compile", "*->compile")
@ -573,17 +573,17 @@ object Classpaths
visit(projectRef, project, conf)
visited.toSeq
}
def unmanagedDependencies0(projectRef: ProjectRef, project: Project, conf: Configuration, data: Settings[Scope]): Task[Classpath] =
def unmanagedDependencies0(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, data: Settings[Scope]): Task[Classpath] =
interDependencies(projectRef, project, conf, data, true, unmanagedLibs)
def internalDependencies0(projectRef: ProjectRef, project: Project, conf: Configuration, data: Settings[Scope]): Task[Classpath] =
def internalDependencies0(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, data: Settings[Scope]): Task[Classpath] =
interDependencies(projectRef, project, conf, data, false, productsTask)
def interDependencies(projectRef: ProjectRef, project: Project, conf: Configuration, data: Settings[Scope], includeSelf: Boolean,
def interDependencies(projectRef: ProjectRef, project: ResolvedProject, conf: Configuration, data: Settings[Scope], includeSelf: Boolean,
f: (ProjectRef, String, Settings[Scope]) => Task[Classpath]): Task[Classpath] =
{
val visited = interSort(projectRef, project, conf, data)
val tasks = asScalaSet(new LinkedHashSet[Task[Classpath]])
for( (dep, c) <- visited )
if(includeSelf || (dep != projectRef) || conf.name != c )
if(includeSelf || (dep != projectRef) || conf.name != c )
tasks += f(dep, c, data)
(tasks.toSeq.join).map(_.flatten.distinct)
@ -611,7 +611,7 @@ object Classpaths
def union[A,B](maps: Seq[A => Seq[B]]): A => Seq[B] =
a => (Seq[B]() /: maps) { _ ++ _(a) } distinct;
def configurationNames(p: Project): Seq[String] = p.configurations.map( _.name)
def configurationNames(p: ResolvedProject): Seq[String] = p.configurations.map( _.name)
def parseList(s: String, allConfs: Seq[String]): Seq[String] = (trim(s split ",") flatMap replaceWildcard(allConfs)).distinct
def replaceWildcard(allConfs: Seq[String])(conf: String): Seq[String] =
if(conf == "") Nil else if(conf == "*") allConfs else conf :: Nil
@ -622,15 +622,15 @@ object Classpaths
def allConfigs(conf: Configuration): Seq[Configuration] =
Dag.topologicalSort(conf)(_.extendsConfigs)
def getConfiguration(ref: ProjectRef, dep: Project, conf: String): Configuration =
def getConfiguration(ref: ProjectRef, dep: ResolvedProject, conf: String): Configuration =
dep.configurations.find(_.name == conf) getOrElse missingConfiguration(Project display ref, conf)
def productsTask(dep: ProjectRef, conf: String, data: Settings[Scope]): Task[Classpath] =
def productsTask(dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] =
getClasspath(products, dep, conf, data)
def unmanagedLibs(dep: ProjectRef, conf: String, data: Settings[Scope]): Task[Classpath] =
def unmanagedLibs(dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] =
getClasspath(unmanagedJars, dep, conf, data)
def getClasspath(key: TaskKey[Classpath], dep: ProjectRef, conf: String, data: Settings[Scope]): Task[Classpath] =
def getClasspath(key: TaskKey[Classpath], dep: ResolvedReference, conf: String, data: Settings[Scope]): Task[Classpath] =
( key in (dep, ConfigKey(conf)) ) get data getOrElse const(Nil)
def defaultConfigurationTask(p: ProjectRef, data: Settings[Scope]): Configuration =
def defaultConfigurationTask(p: ResolvedReference, data: Settings[Scope]): Configuration =
flatten(defaultConfiguration in p get data) getOrElse Configurations.Default
def flatten[T](o: Option[Option[T]]): Option[T] = o flatMap identity
}

View File

@ -14,8 +14,8 @@ object KeyIndex
def combine(indices: Seq[KeyIndex]): KeyIndex = new KeyIndex {
def buildURIs = concat(_.buildURIs)
def projects(uri: URI) = concat(_.projects(uri))
def configs(proj: ProjectRef) = concat(_.configs(proj))
def keys(proj: ProjectRef, conf: Option[String]) = concat(_.keys(proj, conf))
def configs(proj: ResolvedReference) = concat(_.configs(proj))
def keys(proj: 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) )
}
@ -25,25 +25,25 @@ trait KeyIndex
{
def buildURIs: Set[URI]
def projects(uri: URI): Set[String]
def configs(proj: ProjectRef): Set[String]
def keys(proj: ProjectRef, conf: Option[String]): Set[String]
def configs(proj: ResolvedReference): Set[String]
def keys(proj: ResolvedReference, conf: Option[String]): Set[String]
}
trait ExtendableKeyIndex extends KeyIndex
{
def add(scoped: ScopedKey[_]): ExtendableKeyIndex
}
private final class KeyIndex0(val data: Map[URI, Map[String, Map[ Option[String], Set[String]] ]]) extends ExtendableKeyIndex
private final class KeyIndex0(val data: Map[URI, Map[Option[String], Map[ Option[String], Set[String]] ]]) extends ExtendableKeyIndex
{
def buildURIs: Set[URI] = data.keys.toSet
def projects(uri: URI): Set[String] = get(data, uri).keys.toSet
def configs(project: ProjectRef): Set[String] = confMap(project).keys.flatten.toSet
def keys(project: ProjectRef, conf: Option[String]): Set[String] = get(confMap(project), conf)
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 confMap(proj: ProjectRef): Map[Option[String], Set[String]] =
def confMap(proj: ResolvedReference): Map[Option[String], Set[String]] =
proj match
{
case ProjectRef(Some(uri), Some(id)) => get( get(data, uri), id)
case _ => Map.empty
case ProjectRef(uri, id) => get( get(data, uri), Some(id))
case BuildRef(uri) => get( get(data, uri), None)
}
private[this] def get[A,B,C](m: Map[A,Map[B,C]], key: A): Map[B,C] = getOr(m, key, Map.empty)
@ -53,12 +53,18 @@ private final class KeyIndex0(val data: Map[URI, Map[String, Map[ Option[String]
def add(scoped: ScopedKey[_]): ExtendableKeyIndex =
scoped.scope match
{
case Scope(Select(ProjectRef(Some(uri), Some(id))), config, _, _) => add(uri, id, config, scoped.key)
case Scope(Select(ref: ResolvedReference), config, _, _) => addRef(ref, config, scoped.key)
case _ => this
}
def add(uri: URI, id: String, config: ScopeAxis[ConfigKey], key: AttributeKey[_]): ExtendableKeyIndex =
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: String, config: Option[String], key: AttributeKey[_]): ExtendableKeyIndex =
def add(uri: URI, id: Option[String], config: Option[String], key: AttributeKey[_]): ExtendableKeyIndex =
{
val projectMap = get(data, uri)
val configMap = get(projectMap, id)

View File

@ -24,7 +24,7 @@ object Keys
val sessionSettings = AttributeKey[SessionSettings]("session-settings")
val buildStructure = AttributeKey[Load.BuildStructure]("build-structure")
val appConfiguration = SettingKey[xsbti.AppConfiguration]("app-configuration")
val thisProject = SettingKey[Project]("this-project")
val thisProject = SettingKey[ResolvedProject]("this-project")
val thisProjectRef = SettingKey[ProjectRef]("this-project-ref")
val configuration = SettingKey[Configuration]("configuration")
val commands = SettingKey[Seq[Command]]("commands")

View File

@ -237,7 +237,7 @@ object BuiltinCommands
val extracted = Project extract s
import extracted._
val setting = EvaluateConfigurations.evaluateSetting(session.currentEval(), "<set>", imports(extracted), arg, 0)
val append = Load.transformSettings(Load.projectScope(curi, cid), curi, rootProject, setting :: Nil)
val append = Load.transformSettings(Load.projectScope(currentRef), curi, rootProject, setting :: Nil)
val newSession = session.appendSettings( append map (a => (a, arg)))
reapply(newSession, structure, s)
}

View File

@ -12,30 +12,60 @@ package sbt
import CommandSupport.logger
import compiler.Eval
final case class Project(id: String, base: File, aggregate: Seq[ProjectRef] = Nil, dependencies: Seq[Project.ClasspathDependency] = Nil, delegates: Seq[ProjectRef] = Nil,
settings: Seq[Project.Setting[_]] = Project.defaultSettings, configurations: Seq[Configuration] = Configurations.default)
sealed trait ProjectDefinition[PR <: ProjectReference]
{
def id: String
def base: File
def configurations: Seq[Configuration]
def settings: Seq[Project.Setting[_]]
def aggregate: Seq[PR]
def delegates: Seq[PR]
def dependencies: Seq[Project.ClasspathDep[PR]]
def uses: Seq[PR] = aggregate ++ dependencies.map(_.project)
def referenced: Seq[PR] = delegates ++ uses
}
final case class ResolvedProject(id: String, base: File, aggregate: Seq[ProjectRef], dependencies: Seq[Project.ResolvedClasspathDependency], delegates: Seq[ProjectRef],
settings: Seq[Project.Setting[_]], configurations: Seq[Configuration]) extends ProjectDefinition[ProjectRef]
final case class Project(id: String, base: File, aggregate: Seq[ProjectReference] = Nil, dependencies: Seq[Project.ClasspathDependency] = Nil, delegates: Seq[ProjectReference] = Nil,
settings: Seq[Project.Setting[_]] = Project.defaultSettings, configurations: Seq[Configuration] = Configurations.default) extends ProjectDefinition[ProjectReference]
{
def dependsOn(deps: Project.ClasspathDependency*): Project = copy(dependencies = dependencies ++ deps)
def delegates(from: ProjectRef*): Project = copy(delegates = delegates ++ from)
def aggregate(refs: ProjectRef*): Project = copy(aggregate = aggregate ++ refs)
def delegates(from: ProjectReference*): Project = copy(delegates = delegates ++ from)
def aggregate(refs: ProjectReference*): Project = copy(aggregate = aggregate ++ refs)
def configs(cs: Configuration*): Project = copy(configurations = configurations ++ cs)
def settings(ss: Project.Setting[_]*): Project = copy(settings = settings ++ ss)
def uses = aggregate ++ dependencies.map(_.project)
def resolve(resolveRef: ProjectReference => ProjectRef): ResolvedProject =
{
def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef
def resolveDeps(ds: Seq[Project.ClasspathDependency]) = ds map resolveDep
def resolveDep(d: Project.ClasspathDependency) = Project.ResolvedClasspathDependency(resolveRef(d.project), d.configuration)
ResolvedProject(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates), settings, configurations)
}
def resolveBuild(resolveRef: ProjectReference => ProjectReference): Project =
{
def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef
def resolveDeps(ds: Seq[Project.ClasspathDependency]) = ds map resolveDep
def resolveDep(d: Project.ClasspathDependency) = Project.ClasspathDependency(resolveRef(d.project), d.configuration)
copy(aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates))
}
}
final case class Extracted(structure: Load.BuildStructure, session: SessionSettings, curi: URI, cid: String, rootProject: URI => String)
final case class Extracted(structure: Load.BuildStructure, session: SessionSettings, currentRef: ProjectRef, rootProject: URI => String)
{
lazy val currentUnit = structure units curi
lazy val currentProject = currentUnit defined cid
lazy val currentRef = ProjectRef(Some(curi), Some(cid))
def curi = currentRef.build
def cid = currentRef.project
}
object Project extends Init[Scope]
{
def defaultSettings: Seq[Setting[_]] = Default.defaultSettings
final case class ClasspathDependency(project: ProjectRef, configuration: Option[String])
final class Constructor(p: ProjectRef) {
sealed trait ClasspathDep[PR <: ProjectReference] { def project: PR; def configuration: Option[String] }
final case class ResolvedClasspathDependency(project: ProjectRef, configuration: Option[String]) extends ClasspathDep[ProjectRef]
final case class ClasspathDependency(project: ProjectReference, configuration: Option[String]) extends ClasspathDep[ProjectReference]
final class Constructor(p: ProjectReference) {
def %(conf: String): ClasspathDependency = new ClasspathDependency(p, Some(conf))
}
@ -45,16 +75,14 @@ object Project extends Init[Scope]
def extract(state: State): Extracted =
{
val se = session(state)
val (curi, cid) = se.current
val st = structure(state)
Extracted(st, se, curi, cid, Load.getRootProject(st.units))
Extracted(st, se, se.current, Load.getRootProject(st.units))
}
def getProject(ref: ProjectRef, structure: Load.BuildStructure): Option[Project] =
ref match {
case ProjectRef(Some(uri), Some(id)) => (structure.units get uri).flatMap(_.defined get id)
case _ => None
}
def getProjectForReference(ref: Reference, structure: Load.BuildStructure): Option[ResolvedProject] =
ref match { case pr: ProjectRef => getProject(pr, structure); case _ => None }
def getProject(ref: ProjectRef, structure: Load.BuildStructure): Option[ResolvedProject] =
(structure.units get ref.build).flatMap(_.defined get ref.project)
def setProject(session: SessionSettings, structure: Load.BuildStructure, s: State): State =
{
@ -62,19 +90,13 @@ object Project extends Init[Scope]
val newState = s.copy(attributes = newAttrs)
updateCurrent(newState.runExitHooks())
}
def current(state: State): (URI, String) = session(state).current
def currentRef(state: State): ProjectRef =
{
val (unit, it) = current(state)
ProjectRef(Some(unit), Some(it))
}
def current(state: State): ProjectRef = session(state).current
def updateCurrent(s: State): State =
{
val structure = Project.structure(s)
val (uri, id) = Project.current(s)
val ref = ProjectRef(uri, id)
val project = Load.getProject(structure.units, uri, id)
logger(s).info("Set current project to " + id + " (in build " + uri +")")
val ref = Project.current(s)
val project = Load.getProject(structure.units, ref.build, ref.project)
logger(s).info("Set current project to " + ref.project + " (in build " + ref.build +")")
def get[T](k: SettingKey[T]): Option[T] = k in ref get structure.data
val history = get(historyPath) flatMap identity
@ -91,7 +113,26 @@ object Project extends Init[Scope]
translateUninitialized( make(settings)(delegates, scopeLocal) )
def display(scoped: ScopedKey[_]): String = Scope.display(scoped.scope, scoped.key.label)
def display(ref: ProjectRef): String = "(" + (ref.uri map (_.toString) getOrElse "<this>") + ")" + (ref.id getOrElse "<root>")
def display(ref: Reference): String =
ref match
{
case pr: ProjectReference => display(pr)
case br: BuildReference => display(br)
}
def display(ref: BuildReference) =
ref match
{
case ThisBuild => "<this>"
case BuildRef(uri) => "[" + uri + "]"
}
def display(ref: ProjectReference) =
ref match
{
case ThisProject => "(<this>)<this>"
case LocalProject(id) => "(<this>)" + id
case RootProject(uri) => "(" + uri + ")<root>"
case ProjectRef(uri, id) => "(" + uri + ")" + id
}
def fillTaskAxis(scoped: ScopedKey[_]): ScopedKey[_] =
ScopedKey(Scope.fillTaskAxis(scoped.scope, scoped.key), scoped.key)
@ -159,7 +200,7 @@ final case class SessionSettings(currentBuild: URI, currentProject: Map[URI, Str
{
assert(currentProject contains currentBuild, "Current build (" + currentBuild + ") not associated with a current project.")
def setCurrent(build: URI, project: String, eval: () => Eval): SessionSettings = copy(currentBuild = build, currentProject = currentProject.updated(build, project), currentEval = eval)
def current: (URI, String) = (currentBuild, currentProject(currentBuild))
def current: ProjectRef = ProjectRef(currentBuild, currentProject(currentBuild))
def appendSettings(s: Seq[SessionSetting]): SessionSettings = copy(append = modify(append, _ ++ s))
def prependSettings(s: Seq[SessionSetting]): SessionSettings = copy(prepend = modify(prepend, s ++ _))
def mergeSettings: Seq[Setting[_]] = merge(prepend) ++ original ++ merge(append)
@ -175,7 +216,7 @@ final case class SessionSettings(currentBuild: URI, currentProject: Map[URI, Str
object SessionSettings
{
type SessionSetting = (Setting[_], String)
type SessionMap = Map[(URI, String), Seq[SessionSetting]]
type SessionMap = Map[ProjectRef, Seq[SessionSetting]]
def reapply(session: SessionSettings, s: State): State =
BuiltinCommands.reapply(session, Project.structure(s), s)
@ -209,16 +250,16 @@ object SessionSettings
val newAppend = session.append.updated(current, removeRanges(session.append.getOrElse(current, Nil), ranges))
reapply(session.copy( append = newAppend ), s)
}
def saveAllSettings(s: State): State = saveSomeSettings(s)((_,_) => true)
def saveAllSettings(s: State): State = saveSomeSettings(s)(_ => true)
def saveSettings(s: State): State =
{
val (curi,cid) = Project.session(s).current
saveSomeSettings(s)( (uri,id) => uri == curi && id == cid)
val current = Project.session(s).current
saveSomeSettings(s)( _ == current)
}
def saveSomeSettings(s: State)(include: (URI,String) => Boolean): State =
def saveSomeSettings(s: State)(include: ProjectRef => Boolean): State =
withSettings(s){session =>
for( ((uri,id), settings) <- session.append if !settings.isEmpty && include(uri,id) )
writeSettings(ProjectRef(uri, id), settings, Project.structure(s))
for( (ref, settings) <- session.append if !settings.isEmpty && include(ref) )
writeSettings(ref, settings, Project.structure(s))
reapply(session.copy(original = session.mergeSettings, append = Map.empty, prepend = Map.empty), s)
}
def writeSettings(pref: ProjectRef, settings: Seq[SessionSetting], structure: Load.BuildStructure)
@ -226,14 +267,14 @@ object SessionSettings
val project = Project.getProject(pref, structure).getOrElse(error("Invalid project reference " + pref))
val appendTo: File = BuildPaths.configurationSources(project.base).headOption.getOrElse(new File(project.base, "build.sbt"))
val baseAppend = settingStrings(settings).flatMap("" :: _ :: Nil)
val adjustedLines = if(appendTo.isFile && hasTrailingBlank(IO.readLines(appendTo)) ) baseAppend else "" +: baseAppend
val adjustedLines = if(appendTo.isFile && hasTrailingBlank(IO readLines appendTo) ) baseAppend else "" +: baseAppend
IO.writeLines(appendTo, adjustedLines, append = true)
}
def hasTrailingBlank(lines: Seq[String]) = lines.takeRight(1).exists(_.trim.isEmpty)
def printAllSettings(s: State): State =
withSettings(s){ session =>
for( ((uri,id), settings) <- session.append if !settings.isEmpty) {
println("In " + Project.display(ProjectRef(uri,id)))
for( (ref, settings) <- session.append if !settings.isEmpty) {
println("In " + Project.display(ref))
printSettings(settings)
}
s
@ -307,26 +348,7 @@ save, save-all
trait ProjectConstructors
{
implicit def configDependencyConstructor[T <% ProjectRef](p: T): Project.Constructor = new Project.Constructor(p)
implicit def classpathDependency[T <% ProjectRef](p: T): Project.ClasspathDependency = new Project.ClasspathDependency(p, None)
implicit def configDependencyConstructor[T <% ProjectReference](p: T): Project.Constructor = new Project.Constructor(p)
implicit def classpathDependency[T <% ProjectReference](p: T): Project.ClasspathDependency = new Project.ClasspathDependency(p, None)
}
// the URI must be resolved and normalized before it is definitive
final case class ProjectRef(uri: Option[URI], id: Option[String])
object ProjectRef
{
def apply(base: URI, id: String): ProjectRef = ProjectRef(Some(base), Some(id))
/** Reference to the project with 'id' in the current build unit.*/
def apply(id: String): ProjectRef = ProjectRef(None, Some(id))
def apply(base: File, id: String): ProjectRef = ProjectRef(Some(IO.toURI(base)), Some(id))
/** Reference to the root project at 'base'.*/
def apply(base: URI): ProjectRef = ProjectRef(Some(base), None)
/** Reference to the root project at 'base'.*/
def apply(base: File): ProjectRef = ProjectRef(Some(IO.toURI(base)), None)
/** Reference to the root project in the current build unit.*/
def root = ProjectRef(None, None)
implicit def stringToRef(s: String): ProjectRef = ProjectRef(s)
implicit def projectToRef(p: Project): ProjectRef = ProjectRef(p.id)
implicit def uriToRef(u: URI): ProjectRef = ProjectRef(u)
implicit def fileToRef(f: File): ProjectRef = ProjectRef(f)
}

45
main/Reference.scala Normal file
View File

@ -0,0 +1,45 @@
/* sbt -- Simple Build Tool
* Copyright 2011 Mark Harrah
*/
package sbt
import java.io.File
import java.net.URI
// in all of these, the URI must be resolved and normalized before it is definitive
sealed trait Reference
sealed trait ResolvedReference extends Reference
sealed trait BuildReference extends Reference
final case object ThisBuild extends BuildReference
final case class BuildRef(build: URI) extends BuildReference with ResolvedReference
sealed trait ProjectReference extends Reference
final case class ProjectRef(build: URI, project: String) extends ProjectReference with ResolvedReference
final case class LocalProject(project: String) extends ProjectReference
final case class RootProject(build: URI) extends ProjectReference
final case object ThisProject extends ProjectReference
object ProjectRef
{
def apply(base: File, id: String): ProjectRef = ProjectRef(IO toURI base, id)
}
object RootProject
{
/** Reference to the root project at 'base'.*/
def apply(base: File): RootProject = RootProject(IO toURI base)
}
object Reference
{
def uri(ref: Reference): Option[URI] = ref match {
case RootProject(b) => Some(b)
case ProjectRef(b, _) => Some(b)
case BuildRef(b) => Some(b)
case _ => None
}
implicit def uriToRef(u: URI): ProjectReference = RootProject(u)
implicit def fileToRef(f: File): ProjectReference = RootProject(f)
implicit def stringToReference(s: String): ProjectReference = LocalProject(s)
implicit def projectToRef(p: Project): ProjectReference = LocalProject(p.id)
}

View File

@ -6,7 +6,7 @@ package sbt
import java.io.File
import java.net.URI
final case class Scope(project: ScopeAxis[ProjectRef], config: ScopeAxis[ConfigKey], task: ScopeAxis[AttributeKey[_]], extra: ScopeAxis[AttributeMap])
final case class Scope(project: ScopeAxis[Reference], config: ScopeAxis[ConfigKey], task: ScopeAxis[AttributeKey[_]], extra: ScopeAxis[AttributeMap])
object Scope
{
val ThisScope = Scope(This, This, This, This)
@ -30,28 +30,54 @@ object Scope
def resolveProject(uri: URI, rootProject: URI => String): Scope => Scope =
{
case Scope(Select(ref), a,b,c) =>
Scope(Select(mapRef(uri, rootProject, ref)), a,b,c)
case Scope(Select(ref), a,b,c) => Scope(Select(resolveReference(uri, rootProject, ref)), a,b,c)
case x => x
}
def mapRef(current: URI, rootProject: URI => String, ref: ProjectRef): ProjectRef =
{
val (uri, id) = resolveRef(current, rootProject, ref)
ProjectRef(Some(uri), Some(id))
}
def mapRefBuild(current: URI, ref: ProjectRef): ProjectRef = ProjectRef(Some(resolveBuild(current, ref)), ref.id)
def resolveBuild(current: URI, ref: ProjectRef): URI =
ref.uri match { case Some(u) => resolveBuild(current, u); case None => current }
def resolveBuildOnly(current: URI, ref: Reference): Reference =
ref match
{
case br: BuildReference => resolveBuild(current, br)
case pr: ProjectReference => resolveProjectBuild(current, pr)
}
def resolveBuild(current: URI, ref: BuildReference): BuildReference =
ref match
{
case ThisBuild => BuildRef(current)
case BuildRef(uri) => BuildRef(resolveBuild(current, uri))
}
def resolveProjectBuild(current: URI, ref: ProjectReference): ProjectReference =
ref match
{
case ThisProject => RootProject(current)
case LocalProject(id) => ProjectRef(current, id)
case RootProject(uri) => RootProject(resolveBuild(current, uri))
case ProjectRef(uri, id) => ProjectRef(resolveBuild(current, uri), id)
}
def resolveBuild(current: URI, uri: URI): URI =
IO.directoryURI(current resolve uri)
def resolveRef(current: URI, rootProject: URI => String, ref: ProjectRef): (URI, String) =
{
val uri = resolveBuild(current, ref)
(uri, ref.id getOrElse rootProject(uri) )
}
def resolveReference(current: URI, rootProject: URI => String, ref: Reference): ResolvedReference =
ref match
{
case br: BuildReference => resolveBuildRef(current, br)
case pr: ProjectReference => resolveProjectRef(current, rootProject, pr)
}
def resolveProjectRef(current: URI, rootProject: URI => String, ref: ProjectReference): ProjectRef =
ref match
{
case ThisProject => ProjectRef(current, rootProject(current))
case LocalProject(id) => ProjectRef(current, id)
case RootProject(uri) => val res = resolveBuild(current, uri); ProjectRef(res, rootProject(res))
case ProjectRef(uri, id) => ProjectRef(resolveBuild(current, uri), id)
}
def resolveBuildRef(current: URI, ref: BuildReference): BuildRef =
ref match
{
case ThisBuild => BuildRef(current)
case BuildRef(uri) => BuildRef(resolveBuild(current, uri))
}
def display(config: ConfigKey): String = if(config.name == "compile") "" else config.name + ":"
def display(scope: Scope, sep: String): String =
@ -69,7 +95,7 @@ object Scope
def parseScopedKey(command: String): (Scope, String) =
{
val ScopedKeyRegex(_, projectID, _, config, key) = command
val pref = if(projectID eq null) This else Select(ProjectRef(None, Some(projectID)))
val pref = if(projectID eq null) This else Select(LocalProject(projectID))
val conf = if(config eq null) This else Select(ConfigKey(config))
(Scope(pref, conf, This, This), transformTaskName(key))
}
@ -82,37 +108,40 @@ object Scope
}
// *Inherit functions should be immediate delegates and not include argument itself. Transitivity will be provided by this method
def delegates(projectInherit: ProjectRef => Seq[ProjectRef],
configInherit: (ProjectRef, ConfigKey) => Seq[ConfigKey],
taskInherit: (ProjectRef, AttributeKey[_]) => Seq[AttributeKey[_]],
extraInherit: (ProjectRef, AttributeMap) => Seq[AttributeMap])(scope: Scope): Seq[Scope] =
scope.project match
def delegates(projectInherit: Reference => Seq[Reference],
configInherit: (Reference, ConfigKey) => Seq[ConfigKey],
taskInherit: (Reference, AttributeKey[_]) => Seq[AttributeKey[_]],
extraInherit: (Reference, AttributeMap) => Seq[AttributeMap])(rawScope: Scope): Seq[Scope] =
{
case Global | This => scope :: GlobalScope :: Nil
case Select(proj) =>
val prod =
for {
c <- linearize(scope.config)(configInherit(proj, _))
t <- linearize(scope.task)(taskInherit(proj,_))
e <- linearize(scope.extra)(extraInherit(proj,_))
} yield
Scope(Select(proj),c,t,e)
val projI =
withRawBuilds(linearize(scope.project)(projectInherit)) map { p => scope.copy(project = p) }
(prod ++ projI :+ GlobalScope).distinct
val scope = Scope.replaceThis(GlobalScope)(rawScope)
scope.project match
{
case Global => scope :: GlobalScope :: Nil
case This => scope.copy(project = Global) :: GlobalScope :: Nil
case Select(proj) =>
val prod =
for {
c <- linearize(scope.config)(configInherit(proj, _))
t <- linearize(scope.task)(taskInherit(proj,_))
e <- linearize(scope.extra)(extraInherit(proj,_))
} yield
Scope(Select(proj),c,t,e)
val projI =
withRawBuilds(linearize(scope.project, Nil)(projectInherit)) map { p => scope.copy(project = p) }
(prod ++ projI :+ GlobalScope).distinct
}
}
def withRawBuilds(ps: Seq[ScopeAxis[ProjectRef]]): Seq[ScopeAxis[ProjectRef]] =
ps ++ ps.flatMap(rawBuilds).distinct.map(Select.apply)
def withRawBuilds(ps: Seq[ScopeAxis[Reference]]): Seq[ScopeAxis[Reference]] =
(ps ++ (ps flatMap rawBuilds).map(Select.apply) :+ Global).distinct
def rawBuilds(ps: ScopeAxis[ProjectRef]): Seq[ProjectRef] = ps match { case Select(ref) => rawBuilds(ref); case _ => Nil }
def rawBuilds(ps: ProjectRef): Seq[ProjectRef] = ps.uri.map(uri => ProjectRef(Some(uri), None)).toList
def rawBuilds(ps: ScopeAxis[Reference]): Seq[Reference] = ps match { case Select(ref) => rawBuilds(ref); case _ => Nil }
def rawBuilds(ps: Reference): Seq[Reference] = (Reference.uri(ps) map BuildRef.apply).toList
def linearize[T](axis: ScopeAxis[T])(inherit: T => Seq[T]): Seq[ScopeAxis[T]] =
def linearize[T](axis: ScopeAxis[T], append: Seq[ScopeAxis[T]] = Global :: Nil)(inherit: T => Seq[T]): Seq[ScopeAxis[T]] =
axis match
{
case Select(x) => (Global +: Dag.topologicalSort(x)(inherit).map(Select.apply) ).reverse
case Global | This => Global :: Nil
case Select(x) => Dag.topologicalSort(x)(inherit).map(Select.apply).reverse ++ append
case Global | This => append
}
}

View File

@ -96,14 +96,14 @@ object Scoped
{
def in(s: Scope): Result = app0(s)
def in(p: ProjectRef): Result = in(Select(p), This, This)
def in(p: Reference): Result = in(Select(p), This, This)
def in(t: Scoped): Result = in(This, This, Select(t.key))
def in(c: ConfigKey): Result = in(This, Select(c), This)
def in(c: ConfigKey, t: Scoped): Result = in(This, Select(c), Select(t.key))
def in(p: ProjectRef, c: ConfigKey): Result = in(Select(p), Select(c), This)
def in(p: ProjectRef, t: Scoped): Result = in(Select(p), This, Select(t.key))
def in(p: ProjectRef, c: ConfigKey, t: Scoped): Result = in(Select(p), Select(c), Select(t.key))
def in(p: ScopeAxis[ProjectRef], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]]): Result = in( Scope(p, c, t, This) )
def in(p: Reference, c: ConfigKey): Result = in(Select(p), Select(c), This)
def in(p: Reference, t: Scoped): Result = in(Select(p), This, Select(t.key))
def in(p: Reference, c: ConfigKey, t: Scoped): Result = in(Select(p), Select(c), Select(t.key))
def in(p: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]]): Result = in( Scope(p, c, t, This) )
}
private[this] def scopedSetting[T](s: Scope, k: AttributeKey[T]): ScopedSetting[T] = new ScopedSetting[T] { val scope = s; val key = k }
@ -407,21 +407,6 @@ object Scoped
def apply[T](z: (A,B,C,D,E,F,G) => T) =
Apply(t7._1 :^: t7._2 :^: t7._3 :^: t7._4 :^: t7._5 :^: t7._6 :^: t7._7 :^: KNil){ case a :+: b :+: c :+: d :+: e :+: f :+: g :+: HNil => z(a,b,c,d,e,f,g) }
}
/*def unresolved(scope: Scope): Seq[String] = unresolvedProject(scope.project) ++ unresolvedThis(scope)
def unresolvedProject(ps: ScopeAxis[ProjectRef]): Seq[String] = ps match {
case Select(p) => ifEmpty(p.unit, "Unspecified build unit") ++ ifEmpty(p.id, "Unspecified project ID")
case _ => Nil
}
def ifEmpty(p: Option[URI], msg: String): Seq[String] = if(p.isEmpty) msg :: Nil else Nil
def unresolvedThis(scope: Scope): Seq[String] =
unresolvedThis(scope.project, "project") ++
unresolvedThis(scope.config, "configuration") ++
unresolvedThis(scope.task, "task") ++
unresolvedThis(scope.extra, "extra")
def unresolvedThis(axis: ScopeAxis[_], label: String): Seq[String] =
if(axis == This) ("Unresolved This for " + label + " axis.") :: Nil else Nil*/
}
object InputKey
{