From 2bf28fffae72728837d5dc5f96572ef0eccaa1f3 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Tue, 14 Jan 2014 16:18:28 -0500 Subject: [PATCH] Remove support for project delegation from setting system. This provides a small performance improvement during delegation stage of settings processing. This commit doesn't remove the deprecated methods in Project yet. It also removes delegation of the extra axis (currently unused, so no effect on users). --- .../src/main/scala/sbt/DelegateIndex.scala | 10 +- main/settings/src/main/scala/sbt/Scope.scala | 99 ++++++++++--------- main/src/main/scala/sbt/Load.scala | 29 +++--- main/src/test/scala/TestBuild.scala | 31 +++--- 4 files changed, 84 insertions(+), 85 deletions(-) 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]] =