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.
This commit is contained in:
Mark Harrah 2013-04-10 20:15:28 -04:00
parent 96b2ca7ce4
commit a4c059cb7e
5 changed files with 38 additions and 14 deletions

View File

@ -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.")

View File

@ -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)

View File

@ -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))

View File

@ -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))

View File

@ -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;