diff --git a/main/settings/src/main/scala/sbt/DelegateIndex.scala b/main/settings/src/main/scala/sbt/DelegateIndex.scala index 422146c46..7af9c157b 100644 --- a/main/settings/src/main/scala/sbt/DelegateIndex.scala +++ b/main/settings/src/main/scala/sbt/DelegateIndex.scala @@ -2,18 +2,16 @@ package sbt sealed trait DelegateIndex { - def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] - def config(ref: ProjectRef, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] + def config(ref: ResolvedReference, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] // def task(ref: ProjectRef, task: ScopedKey[_]): Seq[ScopeAxis[ScopedKey[_]]] // def extra(ref: ProjectRef, e: AttributeMap): Seq[ScopeAxis[AttributeMap]] } -private final class DelegateIndex0(refs: Map[ProjectRef, ProjectDelegates]) extends DelegateIndex +private final class DelegateIndex0(refs: Map[ResolvedReference, ProjectDelegates]) extends DelegateIndex { - def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] = refs.get(ref) match { case Some(pd) => pd.refs; case None => Nil } - def config(ref: ProjectRef, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] = + def config(ref: ResolvedReference, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] = refs.get(ref) match { case Some(pd) => pd.confs.get(conf) match { case Some(cs) => cs; case None => Select(conf) :: Global :: Nil } case None => Select(conf) :: Global :: Nil } } -private final class ProjectDelegates(val ref: ProjectRef, val refs: Seq[ScopeAxis[ResolvedReference]], val confs: Map[ConfigKey, Seq[ScopeAxis[ConfigKey]]]) \ No newline at end of file +private final class ProjectDelegates(val confs: Map[ConfigKey, Seq[ScopeAxis[ConfigKey]]]) \ No newline at end of file diff --git a/main/settings/src/main/scala/sbt/Scope.scala b/main/settings/src/main/scala/sbt/Scope.scala index 2d8741a59..fd3608d96 100644 --- a/main/settings/src/main/scala/sbt/Scope.scala +++ b/main/settings/src/main/scala/sbt/Scope.scala @@ -29,7 +29,7 @@ object Scope def replaceThis(thisScope: Scope): Scope => Scope = (scope: Scope) => Scope(subThis(thisScope.project, scope.project), subThis(thisScope.config, scope.config), subThis(thisScope.task, scope.task), subThis(thisScope.extra, scope.extra)) - + def subThis[T](sub: ScopeAxis[T], into: ScopeAxis[T]): ScopeAxis[T] = if(into == This) sub else into @@ -39,7 +39,7 @@ object Scope case _: Select[_] => scope case _ => scope.copy(task = Select(key)) } - + def mapReference(f: Reference => Reference): Scope => Scope = { case Scope(Select(ref), a,b,c) => Scope(Select(f(ref)), a,b,c) @@ -83,7 +83,7 @@ object Scope 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 { @@ -103,7 +103,7 @@ object Scope def display(scope: Scope, sep: String): String = displayMasked(scope, sep, showProject, ScopeMask()) def displayMasked(scope: Scope, sep: String, mask: ScopeMask): String = displayMasked(scope, sep, showProject, mask) def display(scope: Scope, sep: String, showProject: Reference => String): String = displayMasked(scope, sep, showProject, ScopeMask()) - def displayMasked(scope: Scope, sep: String, showProject: Reference => String, mask: ScopeMask): String = + def displayMasked(scope: Scope, sep: String, showProject: Reference => String, mask: ScopeMask): String = { import scope.{project, config, task, extra} val configPrefix = config.foldStrict(display, "*:", ".:") @@ -130,7 +130,7 @@ object Scope (Scope(pref, conf, This, This), transformTaskName(key)) } val ScopedKeyRegex = """((\w+)\/)?((\w+)\:)?([\w\-]+)""".r - + def transformTaskName(s: String) = { val parts = s.split("-+") @@ -143,32 +143,30 @@ object Scope configurations: Proj => Seq[ConfigKey], resolve: Reference => ResolvedReference, rootProject: URI => String, - projectInherit: ProjectRef => Seq[ProjectRef], configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey], - taskInherit: AttributeKey[_] => Seq[AttributeKey[_]], - extraInherit: (ResolvedReference, AttributeMap) => Seq[AttributeMap]): Scope => Seq[Scope] = + taskInherit: AttributeKey[_] => Seq[AttributeKey[_]]): Scope => Seq[Scope] = { - val index = delegates(refs, configurations, projectInherit, configInherit) - scope => indexedDelegates(resolve, index, rootProject, taskInherit, extraInherit)(scope) + val index = delegates(refs, rootProject, configurations, configInherit) + scope => indexedDelegates(resolve, index, rootProject, taskInherit)(scope) } def indexedDelegates( resolve: Reference => ResolvedReference, index: DelegateIndex, rootProject: URI => String, - taskInherit: AttributeKey[_] => Seq[AttributeKey[_]], - extraInherit: (ResolvedReference, AttributeMap) => Seq[AttributeMap])(rawScope: Scope): Seq[Scope] = + taskInherit: AttributeKey[_] => Seq[AttributeKey[_]])(rawScope: Scope): Seq[Scope] = { val scope = Scope.replaceThis(GlobalScope)(rawScope) - + + def linearizeTask(t: AttributeKey[_]): Seq[ScopeAxis[AttributeKey[_]]] = + linearize[AttributeKey[_]](t)(taskInherit) + def nonProjectScopes(resolvedProj: ResolvedReference)(px: ScopeAxis[ResolvedReference]) = { val p = px.toOption getOrElse resolvedProj - val configProj = p match { case pr: ProjectRef => pr; case br: BuildRef => ProjectRef(br.build, rootProject(br.build)) } - val cLin = scope.config match { case Select(conf) => index.config(configProj, conf); case _ => withGlobalAxis(scope.config) } - val tLin = scope.task match { case t @ Select(task) => linearize(t)(taskInherit); case _ => withGlobalAxis(scope.task) } - val eLin = withGlobalAxis(scope.extra) - for(c <- cLin; t <- tLin; e <- eLin) yield Scope(px, c, t, e) + val cLin = scope.config match { case Select(conf) => index.config(p, conf); case _ => withGlobalAxis(scope.config) } + val tLin = scope.task match { case t @ Select(task) => linearizeTask(task); case _ => withGlobalAxis(scope.task) } + for(c <- cLin; t <- tLin) yield Scope(px, c, t, scope.extra) } scope.project match { @@ -178,7 +176,7 @@ object Scope val projAxes: Seq[ScopeAxis[ResolvedReference]] = resolvedProj match { - case pr: ProjectRef => index.project(pr) + case pr: ProjectRef => Select(pr) :: Select(BuildRef(pr.build)) :: Global :: Nil case br: BuildRef => Select(br) :: Global :: Nil } projAxes flatMap nonProjectScopes(resolvedProj) @@ -187,46 +185,51 @@ object Scope def withGlobalAxis[T](base: ScopeAxis[T]): Seq[ScopeAxis[T]] = if(base.isSelect) base :: Global :: Nil else Global :: Nil def withGlobalScope(base: Scope): Seq[Scope] = if(base == GlobalScope) GlobalScope :: Nil else base :: GlobalScope :: Nil - def withRawBuilds(ps: Seq[ScopeAxis[ProjectRef]]): Seq[ScopeAxis[ResolvedReference]] = - ps ++ (ps flatMap rawBuild).distinct :+ Global - def rawBuild(ps: ScopeAxis[ProjectRef]): Seq[ScopeAxis[BuildRef]] = ps match { case Select(ref) => Select(BuildRef(ref.build)) :: Nil; case _ => Nil } - - def delegates[Proj]( + private[this] def delegates[Proj]( refs: Seq[(ProjectRef, Proj)], + rootProject: URI => String, configurations: Proj => Seq[ConfigKey], - projectInherit: ProjectRef => Seq[ProjectRef], configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey]): DelegateIndex = { val pDelegates = refs map { case (ref, project) => - (ref, delegateIndex(ref, configurations(project))(projectInherit, configInherit) ) + val index = new ProjectDelegates(configurations(project).map( c => axisDelegates(configInherit, ref, c) ).toMap) + (ref, index) } toMap ; - new DelegateIndex0(pDelegates) - } - private[this] def delegateIndex(ref: ProjectRef, confs: Seq[ConfigKey])(projectInherit: ProjectRef => Seq[ProjectRef], configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey]): ProjectDelegates = - { - val refDelegates = withRawBuilds(linearize(Select(ref), false)(projectInherit)) - val configs = confs map { c => axisDelegates(configInherit, ref, c) } - new ProjectDelegates(ref, refDelegates, configs.toMap) - } - def axisDelegates[T](direct: (ResolvedReference, T) => Seq[T], ref: ResolvedReference, init: T): (T, Seq[ScopeAxis[T]]) = - ( init, linearize(Select(init))(direct(ref, _)) ) - - def linearize[T](axis: ScopeAxis[T], appendGlobal: Boolean = true)(inherit: T => Seq[T]): Seq[ScopeAxis[T]] = - axis match - { - case Select(x) => topologicalSort[T](x, appendGlobal)(inherit) - case Global | This => if(appendGlobal) Global :: Nil else Nil + val buildURIs = refs.map(_._1.build).distinct + val bDelegates = for(build <- buildURIs) yield { + val ref = ProjectRef(build, rootProject(build)) + (BuildRef(build), pDelegates(ref)) } - - def topologicalSort[T](node: T, appendGlobal: Boolean)(dependencies: T => Seq[T]): Seq[ScopeAxis[T]] = - { - val o = Dag.topologicalSortUnchecked(node)(dependencies).map(Select.apply) - if(appendGlobal) o ::: Global :: Nil else o + new DelegateIndex0(pDelegates ++ bDelegates) } + + def axisDelegates[T](direct: (ResolvedReference, T) => Seq[T], ref: ResolvedReference, init: T): (T, Seq[ScopeAxis[T]]) = + ( init, linearize(init)(direct(ref, _)) ) + def globalProjectDelegates(scope: Scope): Seq[Scope] = if(scope == GlobalScope) GlobalScope :: Nil else - for( c <- withGlobalAxis(scope.config); t <- withGlobalAxis(scope.task); e <- withGlobalAxis(scope.extra) ) yield Scope(Global, c, t, e) + for( c <- withGlobalAxis(scope.config); t <- withGlobalAxis(scope.task) ) yield Scope(Global, c, t, scope.extra) + + // inlined topologicalSort for performance + private[this] def linearize[T](node: T)(dependencies: T => Seq[T]): Seq[ScopeAxis[T]] = + { + val discovered = new java.util.HashSet[T] + // always include Global at the end + var finished: List[ScopeAxis[T]] = Global :: Nil + + def visitAll(nodes: Seq[T]) = nodes foreach visit + def visit(node : T){ + if (!discovered.contains(node)) { + discovered.add(node) + visitAll(dependencies(node)) + finished ::= Select(node); + } + } + + visit(node) + finished + } } diff --git a/main/src/main/scala/sbt/Load.scala b/main/src/main/scala/sbt/Load.scala index e6a247da4..086ed7a2d 100755 --- a/main/src/main/scala/sbt/Load.scala +++ b/main/src/main/scala/sbt/Load.scala @@ -76,7 +76,7 @@ object Load config.copy(injectSettings = config.injectSettings.copy(projectLoaded = compiled)) } def buildGlobalSettings(base: File, files: Seq[File], config: sbt.LoadBuildConfiguration): ClassLoader => Seq[Setting[_]] = - { + { val eval = mkEval(data(config.classpath), base, defaultEvalOptions) val imports = BuildUtil.baseImports ++ BuildUtil.importAllRoot(config.globalPluginNames) loader => EvaluateConfigurations(eval, files, imports)(loader).settings @@ -104,10 +104,8 @@ object Load (_: ResolvedProject).configurations.map(c => ConfigKey(c.name)), resolveRef, rootProject, - project => projectInherit(lb, project), (project, config) => configInherit(lb, project, config, rootProject), - task => task.extend, - (project, extra) => Nil + task => task.extend ) } def configInherit(lb: sbt.LoadedBuild, ref: ResolvedReference, config: ConfigKey, rootProject: URI => String): Seq[ConfigKey] = @@ -119,9 +117,6 @@ object Load def configInheritRef(lb: sbt.LoadedBuild, ref: ProjectRef, config: ConfigKey): Seq[ConfigKey] = configurationOpt(lb.units, ref.build, ref.project, config).toList.flatMap(_.extendsConfigs).map(c => ConfigKey(c.name)) - def projectInherit(lb: sbt.LoadedBuild, ref: ProjectRef): Seq[ProjectRef] = - getProject(lb.units, ref.build, ref.project).delegates - // build, load, and evaluate all units. // 1) Compile all plugin definitions // 2) Evaluate plugin definitions to obtain and compile plugins and get the resulting classpath for the build definition @@ -211,7 +206,7 @@ object Load def buildConfigurations(loaded: sbt.LoadedBuild, rootProject: URI => String, injectSettings: InjectSettings): Seq[Setting[_]] = { ((loadedBuild in GlobalScope :== loaded) +: - transformProjectOnly(loaded.root, rootProject, injectSettings.global)) ++ + transformProjectOnly(loaded.root, rootProject, injectSettings.global)) ++ inScope(GlobalScope)( pluginGlobalSettings(loaded) ) ++ loaded.units.toSeq.flatMap { case (uri, build) => val plugins = build.unit.plugins.plugins @@ -245,7 +240,7 @@ object Load def transformSettings(thisScope: Scope, uri: URI, rootProject: URI => String, settings: Seq[Setting[_]]): Seq[Setting[_]] = Project.transform(Scope.resolveScope(thisScope, uri, rootProject), settings) def projectScope(project: Reference): Scope = Scope(Select(project), Global, Global, Global) - + def lazyEval(unit: sbt.BuildUnit): () => Eval = { lazy val eval = mkEval(unit) @@ -454,7 +449,7 @@ Time.complete("default builds") new sbt.BuildUnit(uri, normBase, loadedDefs, plugs) } - private[this] def autoID(localBase: File, context: PluginManagement.Context, existingIDs: Seq[String]): String = + private[this] def autoID(localBase: File, context: PluginManagement.Context, existingIDs: Seq[String]): String = { def normalizeID(f: File) = Project.normalizeProjectID(f.getName) match { case Right(id) => id @@ -469,11 +464,11 @@ Time.complete("default builds") if(existingIDs.contains(tryID)) Build.defaultID(localBase) else tryID } - private[this] def autoIDError(base: File, reason: String): String = + private[this] def autoIDError(base: File, reason: String): String = "Could not derive root project ID from directory " + base.getAbsolutePath + ":\n" + reason + "\nRename the directory or explicitly define a root project." - private[this] def projectsFromBuild(b: Build, base: File): Seq[Project] = + private[this] def projectsFromBuild(b: Build, base: File): Seq[Project] = b.projectDefinitions(base).map(resolveBase(base)) private[this] def loadTransitive(newProjects: Seq[Project], buildBase: File, imports: Seq[String], plugins: sbt.LoadedPlugins, eval: () => Eval, injectSettings: InjectSettings, acc: Seq[Project], memoSettings: mutable.Map[File, LoadedSbtFile]): Seq[Project] = @@ -499,7 +494,7 @@ Time.complete("default builds") else loadTransitive(nextProjects, buildBase, imports, plugins, eval, injectSettings, loadedProjects, memoSettings) } - + private[this] def loadSettings(auto: AddSettings, projectBase: File, buildImports: Seq[String], loadedPlugins: sbt.LoadedPlugins, eval: ()=>Eval, injectSettings: InjectSettings, memoSettings: mutable.Map[File, LoadedSbtFile]): LoadedSbtFile = { lazy val defaultSbtFiles = configurationSources(projectBase) @@ -586,7 +581,7 @@ Time.complete("default builds") def pluginDefinitionLoader(config: sbt.LoadBuildConfiguration, dependencyClasspath: Seq[Attributed[File]]): (Seq[Attributed[File]], ClassLoader) = pluginDefinitionLoader(config, dependencyClasspath, Nil) def pluginDefinitionLoader(config: sbt.LoadBuildConfiguration, pluginData: PluginData): (Seq[Attributed[File]], ClassLoader) = - pluginDefinitionLoader(config, pluginData.dependencyClasspath, pluginData.definitionClasspath) + pluginDefinitionLoader(config, pluginData.dependencyClasspath, pluginData.definitionClasspath) def pluginDefinitionLoader(config: sbt.LoadBuildConfiguration, depcp: Seq[Attributed[File]], defcp: Seq[Attributed[File]]): (Seq[Attributed[File]], ClassLoader) = { val definitionClasspath = @@ -686,7 +681,7 @@ Time.complete("default builds") def initialSession(structure: sbt.BuildStructure, rootEval: () => Eval): SessionSettings = new SessionSettings(structure.root, projectMap(structure, Map.empty), structure.settings, Map.empty, Nil, rootEval) - + def projectMap(structure: sbt.BuildStructure, current: Map[URI, String]): Map[URI, String] = { val units = structure.units @@ -715,7 +710,7 @@ Time.complete("default builds") def getImports(unit: sbt.BuildUnit): Seq[String] = BuildUtil.getImports(unit) def referenced[PR <: ProjectReference](definitions: Seq[ProjectDefinition[PR]]): Seq[PR] = definitions flatMap { _.referenced } - + @deprecated("LoadedBuildUnit is now top-level", "0.13.0") type LoadedBuildUnit = sbt.LoadedBuildUnit @@ -727,7 +722,7 @@ Time.complete("default builds") @deprecated("LoadBuildConfiguration is now top-level", "0.13.0") type LoadBuildConfiguration = sbt.LoadBuildConfiguration - @deprecated("LoadBuildConfiguration is now top-level", "0.13.0") + @deprecated("LoadBuildConfiguration is now top-level", "0.13.0") val LoadBuildConfiguration = sbt.LoadBuildConfiguration final class EvaluatedConfigurations(val eval: Eval, val settings: Seq[Setting[_]]) diff --git a/main/src/test/scala/TestBuild.scala b/main/src/test/scala/TestBuild.scala index ac67e98a8..6ea6f01a9 100644 --- a/main/src/test/scala/TestBuild.scala +++ b/main/src/test/scala/TestBuild.scala @@ -105,7 +105,6 @@ object TestBuild def rootProject(uri: URI): String = buildMap(uri).root.id def inheritConfig(ref: ResolvedReference, config: ConfigKey) = projectFor(ref).confMap(config.name).extended map toConfigKey def inheritTask(task: AttributeKey[_]) = taskMap.get(task) match { case None => Nil; case Some(t) => t.delegates map getKey } - def inheritProject(ref: ProjectRef) = project(ref).delegates def resolve(ref: Reference) = Scope.resolveReference(builds.head.uri, rootProject, ref) lazy val delegates: Scope => Seq[Scope] = Scope.delegates( @@ -113,10 +112,8 @@ object TestBuild (_: Proj).configurations.map(toConfigKey), resolve, uri => buildMap(uri).root.id, - inheritProject, inheritConfig, - inheritTask, - (ref, mp) => Nil + inheritTask ) lazy val allFullScopes: Seq[Scope] = for { @@ -135,10 +132,9 @@ object TestBuild val root = projects.head val projectMap = mapBy(projects)(_.id) } - final class Proj(val id: String, val delegates: Seq[ProjectRef], val configurations: Seq[Config]) + final class Proj(val id: String, val configurations: Seq[Config]) { - override def toString = "Project " + id + "\n Delegates:\n " + delegates.mkString("\n ") + - "\n Configurations:\n " + configurations.mkString("\n ") + override def toString = "Project " + id + "\n Configurations:\n " + configurations.mkString("\n ") val confMap = mapBy(configurations)(_.name) } @@ -196,7 +192,7 @@ object TestBuild { implicit val cGen = genConfigs(idGen, MaxDepsGen, MaxConfigsGen) implicit val tGen = genTasks(idGen, MaxDepsGen, MaxTasksGen) - implicit val pGen = (uri: URI) => genProjects(uri)(idGen, MaxDepsGen, MaxProjectsGen, cGen) + implicit val pGen = (uri: URI) => genProjects(uri)(idGen, MaxProjectsGen, cGen) envGen(buildGen(uriGen, pGen), tGen) } @@ -217,12 +213,19 @@ object TestBuild def nGen[T](igen: Gen[Int])(implicit g: Gen[T]): Gen[List[T]] = igen flatMap { ig => listOfN(ig, g) } - implicit def genProjects(build: URI)(implicit genID: Gen[String], maxDeps: Gen[Int], count: Gen[Int], confs: Gen[Seq[Config]]): Gen[Seq[Proj]] = - genAcyclic(maxDeps, genID, count) { (id: String) => - for(cs <- confs) yield { (deps: Seq[Proj]) => - new Proj(id, deps.map{dep => ProjectRef(build, dep.id) }, cs) - } - } + implicit def genProjects(build: URI)(implicit genID: Gen[String], count: Gen[Int], confs: Gen[Seq[Config]]): Gen[Seq[Proj]] = + { + def projects(size: Int, ids: List[String]): Seq[Gen[Proj]] = + for(id <- ids) yield + for(cs <- confs) yield + new Proj(id, cs) + for { + size <- count + ids <- listOfN(size, genID) + projs <- sequence(projects(size, ids)) + } yield projs + } + def genConfigs(implicit genName: Gen[String], maxDeps: Gen[Int], count: Gen[Int]): Gen[Seq[Config]] = genAcyclicDirect[Config,String](maxDeps, genName, count)( (key, deps) => new Config(key, deps) ) def genTasks(implicit genName: Gen[String], maxDeps: Gen[Int], count: Gen[Int]): Gen[Seq[Taskk]] =