From a4c059cb7e359523e1d59212f7bd2dee15a6b2a2 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 10 Apr 2013 20:15:28 -0400 Subject: [PATCH] Better auto-generated IDs for default projects. Fixes #554. For the global plugins project, the default ID is "global-plugins". For a normal project, the default ID is the name of the build directory. The ID of a build definition for one of the above appends -build. --- ivy/src/main/scala/sbt/StringUtilities.scala | 2 +- main/src/main/scala/sbt/Build.scala | 16 +++++++++------- main/src/main/scala/sbt/GlobalPlugin.scala | 3 ++- main/src/main/scala/sbt/Load.scala | 19 +++++++++++++++++-- .../src/main/scala/sbt/PluginManagement.scala | 12 +++++++++--- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/ivy/src/main/scala/sbt/StringUtilities.scala b/ivy/src/main/scala/sbt/StringUtilities.scala index 3a0075bb4..17f6e75a9 100644 --- a/ivy/src/main/scala/sbt/StringUtilities.scala +++ b/ivy/src/main/scala/sbt/StringUtilities.scala @@ -5,7 +5,7 @@ package sbt object StringUtilities { - def normalize(s: String) = s.toLowerCase.replaceAll("""\s+""", "-") + def normalize(s: String) = s.toLowerCase.replaceAll("""\W+""", "-") def nonEmpty(s: String, label: String) { require(s.trim.length > 0, label + " cannot be empty.") diff --git a/main/src/main/scala/sbt/Build.scala b/main/src/main/scala/sbt/Build.scala index cb9c35652..0501f9259 100644 --- a/main/src/main/scala/sbt/Build.scala +++ b/main/src/main/scala/sbt/Build.scala @@ -36,18 +36,20 @@ trait Plugin object Build { val defaultEmpty: Build = new Build { override def projects = Nil } - val default: Build = new Build { override def projectDefinitions(base: File) = defaultProject(base) :: Nil } - def defaultAggregated(aggregate: Seq[ProjectRef]): Build = new Build { - override def projectDefinitions(base: File) = defaultAggregatedProject(base, aggregate) :: Nil + val default: Build = new Build { override def projectDefinitions(base: File) = defaultProject(defaultID(base), base) :: Nil } + def defaultAggregated(id: String, aggregate: Seq[ProjectRef]): Build = new Build { + override def projectDefinitions(base: File) = defaultAggregatedProject(id, base, aggregate) :: Nil } - def defaultID(base: File): String = "default-" + Hash.trimHashString(base.getAbsolutePath, 6) - def defaultProject(base: File): Project = Project(defaultID(base), base).settings( + def defaultID(base: File, prefix: String = "default"): String = prefix + "-" + Hash.trimHashString(base.getAbsolutePath, 6) + @deprecated("Explicitly specify the ID", "0.13.0") + def defaultProject(base: File): Project = defaultProject(defaultID(base), base) + def defaultProject(id: String, base: File): Project = Project(id, base).settings( // if the user has overridden the name, use the normal organization that is derived from the name. organization <<= (thisProject, organization, name) { (p, o, n) => if(p.id == n) "default" else o } ) - def defaultAggregatedProject(base: File, agg: Seq[ProjectRef]): Project = - defaultProject(base).aggregate(agg : _*) + def defaultAggregatedProject(id: String, base: File, agg: Seq[ProjectRef]): Project = + defaultProject(id, base).aggregate(agg : _*) @deprecated("Use Attributed.data", "0.13.0") def data[T](in: Seq[Attributed[T]]): Seq[T] = Attributed.data(in) diff --git a/main/src/main/scala/sbt/GlobalPlugin.scala b/main/src/main/scala/sbt/GlobalPlugin.scala index 7875456a1..0fa1b40e9 100644 --- a/main/src/main/scala/sbt/GlobalPlugin.scala +++ b/main/src/main/scala/sbt/GlobalPlugin.scala @@ -28,7 +28,8 @@ object GlobalPlugin def build(base: File, s: State, config: LoadBuildConfiguration): (BuildStructure, State) = { - val globalConfig = config.copy(injectSettings = config.injectSettings.copy(global = config.injectSettings.global ++ globalPluginSettings)) + val newInject = config.injectSettings.copy(global = config.injectSettings.global ++ globalPluginSettings) + val globalConfig = config.copy(injectSettings = newInject, pluginManagement = config.pluginManagement.forGlobalPlugin) val (eval, structure) = Load(base, s, globalConfig) val session = Load.initialSession(structure, eval) (structure, Project.setProject(session, structure, s)) diff --git a/main/src/main/scala/sbt/Load.scala b/main/src/main/scala/sbt/Load.scala index 98bc8e3b6..06855c7c2 100755 --- a/main/src/main/scala/sbt/Load.scala +++ b/main/src/main/scala/sbt/Load.scala @@ -51,6 +51,7 @@ object Load val compilers = Compiler.compilers(ClasspathOptions.boot)(state.configuration, log) val evalPluginDef = EvaluateTask.evalPluginDef(log) _ val delegates = defaultDelegates + val initialID = baseDirectory.getName val pluginMgmt = PluginManagement(loader) val inject = InjectSettings(injectGlobal(state), Nil, const(Nil)) new sbt.LoadBuildConfiguration(stagingDirectory, classpath, loader, compilers, evalPluginDef, definesClass, delegates, @@ -405,7 +406,7 @@ object Load val normBase = localBase.getCanonicalFile val defDir = projectStandard(normBase) - val plugs = plugins(defDir, s, config) + val plugs = plugins(defDir, s, config.copy(pluginManagement = config.pluginManagement.forPlugin)) val defNames = analyzed(plugs.fullClasspath) flatMap findDefinitions val defsScala = if(defNames.isEmpty) Nil else loadDefinitions(plugs.loader, defNames) val imports = BuildUtil.getImports(plugs.pluginNames, defNames) @@ -421,7 +422,10 @@ object Load if(hasRoot) (loadedProjectsRaw, Build.defaultEmpty) else { - val b = Build.defaultAggregated(loadedProjectsRaw.map(p => ProjectRef(uri, p.id))) + val existingIDs = loadedProjectsRaw.map(_.id) + val refs = existingIDs.map(id => ProjectRef(uri, id)) + val defaultID = autoID(normBase, config.pluginManagement.context, existingIDs) + val b = Build.defaultAggregated(defaultID, refs) val defaultProjects = loadProjects(projectsFromBuild(b, normBase)) (defaultProjects ++ loadedProjectsRaw, b) } @@ -430,6 +434,17 @@ object Load val loadedDefs = new sbt.LoadedDefinitions(defDir, Nil, plugs.loader, defs, loadedProjects, defNames) new sbt.BuildUnit(uri, normBase, loadedDefs, plugs) } + private[this] def autoID(localBase: File, context: PluginManagement.Context, existingIDs: Seq[String]): String = + { + import StringUtilities.{normalize => norm} + def nthParentName(f: File, i: Int): String = + if(f eq null) Build.defaultID(localBase) else if(i <= 0) norm(f.getName) else nthParentName(f.getParentFile, i - 1) + val pluginDepth = context.pluginProjectDepth + val postfix = "-build" * pluginDepth + val idBase = if(context.globalPluginProject) "global-plugins" else nthParentName(localBase, pluginDepth) + val tryID = idBase + postfix + if(existingIDs.contains(tryID)) Build.defaultID(localBase) else tryID + } private[this] def projectsFromBuild(b: Build, base: File): Seq[Project] = b.projectDefinitions(base).map(resolveBase(base)) diff --git a/main/src/main/scala/sbt/PluginManagement.scala b/main/src/main/scala/sbt/PluginManagement.scala index 031aa03a0..634961569 100644 --- a/main/src/main/scala/sbt/PluginManagement.scala +++ b/main/src/main/scala/sbt/PluginManagement.scala @@ -6,10 +6,10 @@ package sbt import java.net.{URI,URL,URLClassLoader} -final case class PluginManagement(overrides: Set[ModuleID], applyOverrides: Set[ModuleID], loader: PluginClassLoader, initialLoader: ClassLoader) +final case class PluginManagement(overrides: Set[ModuleID], applyOverrides: Set[ModuleID], loader: PluginClassLoader, initialLoader: ClassLoader, context: Context) { def shift: PluginManagement = - PluginManagement(Set.empty, overrides, new PluginClassLoader(initialLoader), initialLoader) + PluginManagement(Set.empty, overrides, new PluginClassLoader(initialLoader), initialLoader, context) def addOverrides(os: Set[ModuleID]): PluginManagement = copy(overrides = overrides ++ os) @@ -20,11 +20,17 @@ final case class PluginManagement(overrides: Set[ModuleID], applyOverrides: Set[ def inject: Seq[Setting[_]] = Seq( Keys.dependencyOverrides ++= overrides ) + + def forGlobalPlugin: PluginManagement = copy(context = Context(globalPluginProject = true, pluginProjectDepth = 0)) + def forPlugin: PluginManagement = copy(context = context.copy(pluginProjectDepth = context.pluginProjectDepth + 1)) } object PluginManagement { + final case class Context private[sbt](globalPluginProject: Boolean, pluginProjectDepth: Int) + val emptyContext: Context = Context(false, 0) + def apply(initialLoader: ClassLoader): PluginManagement = - PluginManagement(Set.empty, Set.empty, new PluginClassLoader(initialLoader), initialLoader) + PluginManagement(Set.empty, Set.empty, new PluginClassLoader(initialLoader), initialLoader, emptyContext) def extractOverrides(classpath: Classpath): Set[ModuleID] = classpath flatMap { _.metadata get Keys.moduleID.key map keepOverrideInfo } toSet;