diff --git a/main/Act.scala b/main/Act.scala index 5e2df906f..dcb3e34fb 100644 --- a/main/Act.scala +++ b/main/Act.scala @@ -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) } diff --git a/main/Aggregation.scala b/main/Aggregation.scala index d8f76692e..5b504e533 100644 --- a/main/Aggregation.scala +++ b/main/Aggregation.scala @@ -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 diff --git a/main/Build.scala b/main/Build.scala index 3524e9033..f0928258c 100644 --- a/main/Build.scala +++ b/main/Build.scala @@ -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)) } diff --git a/main/Default.scala b/main/Default.scala index daf932032..881e2dc10 100755 --- a/main/Default.scala +++ b/main/Default.scala @@ -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 } diff --git a/main/KeyIndex.scala b/main/KeyIndex.scala index c09f66489..858d3da9b 100644 --- a/main/KeyIndex.scala +++ b/main/KeyIndex.scala @@ -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) diff --git a/main/Keys.scala b/main/Keys.scala index ae21c9b0c..e41565c0d 100644 --- a/main/Keys.scala +++ b/main/Keys.scala @@ -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") diff --git a/main/Main.scala b/main/Main.scala index 93b8de9c4..b7bb7443c 100644 --- a/main/Main.scala +++ b/main/Main.scala @@ -237,7 +237,7 @@ object BuiltinCommands val extracted = Project extract s import extracted._ val setting = EvaluateConfigurations.evaluateSetting(session.currentEval(), "", 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) } diff --git a/main/Project.scala b/main/Project.scala index 3d3869567..127b8c87e 100644 --- a/main/Project.scala +++ b/main/Project.scala @@ -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 "") + ")" + (ref.id getOrElse "") + 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 => "" + case BuildRef(uri) => "[" + uri + "]" + } + def display(ref: ProjectReference) = + ref match + { + case ThisProject => "()" + case LocalProject(id) => "()" + id + case RootProject(uri) => "(" + uri + ")" + 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) -} \ No newline at end of file diff --git a/main/Reference.scala b/main/Reference.scala new file mode 100644 index 000000000..09cd963c5 --- /dev/null +++ b/main/Reference.scala @@ -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) +} \ No newline at end of file diff --git a/main/Scope.scala b/main/Scope.scala index f6a934f15..afffe941e 100644 --- a/main/Scope.scala +++ b/main/Scope.scala @@ -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 } } diff --git a/main/Structure.scala b/main/Structure.scala index bba0342c0..e7b1f1d93 100644 --- a/main/Structure.scala +++ b/main/Structure.scala @@ -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 {