mirror of https://github.com/sbt/sbt.git
Better auto project ID handling. Ref #776.
* Consolidate project ID validation and normalization into Project methods * Provide an earlier and more detailed error message when the directory name can't be used for the project ID
This commit is contained in:
parent
6f0028e50d
commit
ce1c8b0ebc
|
|
@ -5,6 +5,7 @@ package sbt
|
||||||
|
|
||||||
object StringUtilities
|
object StringUtilities
|
||||||
{
|
{
|
||||||
|
@deprecated("Different use cases require different normalization. Use Project.normalizeModuleID or normalizeProjectID instead.", "0.13.0")
|
||||||
def normalize(s: String) = s.toLowerCase.replaceAll("""\W+""", "-")
|
def normalize(s: String) = s.toLowerCase.replaceAll("""\W+""", "-")
|
||||||
def nonEmpty(s: String, label: String)
|
def nonEmpty(s: String, label: String)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -923,7 +923,7 @@ object Classpaths
|
||||||
conflictWarning in GlobalScope :== ConflictWarning.default("global"),
|
conflictWarning in GlobalScope :== ConflictWarning.default("global"),
|
||||||
conflictWarning := conflictWarning.value.copy(label = Reference.display(thisProjectRef.value)),
|
conflictWarning := conflictWarning.value.copy(label = Reference.display(thisProjectRef.value)),
|
||||||
unmanagedBase := baseDirectory.value / "lib",
|
unmanagedBase := baseDirectory.value / "lib",
|
||||||
normalizedName := StringUtilities.normalize(name.value),
|
normalizedName := Project.normalizeModuleID(name.value),
|
||||||
isSnapshot <<= isSnapshot or version(_ endsWith "-SNAPSHOT"),
|
isSnapshot <<= isSnapshot or version(_ endsWith "-SNAPSHOT"),
|
||||||
description <<= description or name,
|
description <<= description or name,
|
||||||
homepage in GlobalScope :== None,
|
homepage in GlobalScope :== None,
|
||||||
|
|
|
||||||
|
|
@ -435,17 +435,26 @@ object Load
|
||||||
val loadedDefs = new sbt.LoadedDefinitions(defDir, Nil, plugs.loader, defs, loadedProjects, defNames)
|
val loadedDefs = new sbt.LoadedDefinitions(defDir, Nil, plugs.loader, defs, loadedProjects, defNames)
|
||||||
new sbt.BuildUnit(uri, normBase, loadedDefs, plugs)
|
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 =
|
||||||
{
|
{
|
||||||
import StringUtilities.{normalize => norm}
|
def normalizeID(f: File) = Project.normalizeProjectID(f.getName) match {
|
||||||
|
case Right(id) => id
|
||||||
|
case Left(msg) => error(autoIDError(f, msg))
|
||||||
|
}
|
||||||
def nthParentName(f: File, i: Int): String =
|
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)
|
if(f eq null) Build.defaultID(localBase) else if(i <= 0) normalizeID(f) else nthParentName(f.getParentFile, i - 1)
|
||||||
val pluginDepth = context.pluginProjectDepth
|
val pluginDepth = context.pluginProjectDepth
|
||||||
val postfix = "-build" * pluginDepth
|
val postfix = "-build" * pluginDepth
|
||||||
val idBase = if(context.globalPluginProject) "global-plugins" else nthParentName(localBase, pluginDepth)
|
val idBase = if(context.globalPluginProject) "global-plugins" else nthParentName(localBase, pluginDepth)
|
||||||
val tryID = idBase + postfix
|
val tryID = idBase + postfix
|
||||||
if(existingIDs.contains(tryID)) Build.defaultID(localBase) else tryID
|
if(existingIDs.contains(tryID)) Build.defaultID(localBase) else tryID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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))
|
b.projectDefinitions(base).map(resolveBase(base))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,10 +157,26 @@ object Project extends ProjectExtra
|
||||||
delegates: => Seq[ProjectReference] = Nil, settings: => Seq[Def.Setting[_]] = defaultSettings, configurations: Seq[Configuration] = Configurations.default,
|
delegates: => Seq[ProjectReference] = Nil, settings: => Seq[Def.Setting[_]] = defaultSettings, configurations: Seq[Configuration] = Configurations.default,
|
||||||
auto: AddSettings = AddSettings.allDefaults): Project =
|
auto: AddSettings = AddSettings.allDefaults): Project =
|
||||||
{
|
{
|
||||||
DefaultParsers.parse(id, DefaultParsers.ID).left.foreach(errMsg => sys.error("Invalid project ID: " + errMsg))
|
validProjectID(id).foreach(errMsg => sys.error("Invalid project ID: " + errMsg))
|
||||||
new ProjectDef[ProjectReference](id, base, aggregate, dependencies, delegates, settings, configurations, auto) with Project
|
new ProjectDef[ProjectReference](id, base, aggregate, dependencies, delegates, settings, configurations, auto) with Project
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns None if `id` is a valid Project ID or Some containing the parser error message if it is not.*/
|
||||||
|
def validProjectID(id: String): Option[String] = DefaultParsers.parse(id, DefaultParsers.ID).left.toOption
|
||||||
|
|
||||||
|
|
||||||
|
/** Constructs a valid Project ID based on `id` and returns it in Right or returns the error message in Left if one cannot be constructed.*/
|
||||||
|
def normalizeProjectID(id: String): Either[String, String] =
|
||||||
|
{
|
||||||
|
// TODO: better attempt
|
||||||
|
val attempt = id.toLowerCase.replaceAll("""\W+""", "-")
|
||||||
|
validProjectID(attempt).toLeft(attempt)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Normalize a String so that it is suitable for use as a dependency management module identifier.
|
||||||
|
* This is a best effort implementation, since valid characters are not documented or consistent.*/
|
||||||
|
def normalizeModuleID(id: String): String = id.toLowerCase.replaceAll("""\W+""", "-")
|
||||||
|
|
||||||
def resolved(id: String, base: File, aggregate: => Seq[ProjectRef], dependencies: => Seq[ResolvedClasspathDependency], delegates: => Seq[ProjectRef],
|
def resolved(id: String, base: File, aggregate: => Seq[ProjectRef], dependencies: => Seq[ResolvedClasspathDependency], delegates: => Seq[ProjectRef],
|
||||||
settings: Seq[Def.Setting[_]], configurations: Seq[Configuration], auto: AddSettings): ResolvedProject =
|
settings: Seq[Def.Setting[_]], configurations: Seq[Configuration], auto: AddSettings): ResolvedProject =
|
||||||
new ProjectDef[ProjectRef](id, base, aggregate, dependencies, delegates, settings, configurations, auto) with ResolvedProject
|
new ProjectDef[ProjectRef](id, base, aggregate, dependencies, delegates, settings, configurations, auto) with ResolvedProject
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ project/ directory.
|
||||||
val pom = pomFile(info)
|
val pom = pomFile(info)
|
||||||
val model = readPom(pom)
|
val model = readPom(pom)
|
||||||
|
|
||||||
val n = StringUtilities.normalize(model.getName)
|
val n = Project.normalizeProjectID(model.getName)
|
||||||
val base = Option(model.getProjectDirectory) getOrElse info.base
|
val base = Option(model.getProjectDirectory) getOrElse info.base
|
||||||
val root = Project(n, base) settings( pomSettings(model) : _*)
|
val root = Project(n, base) settings( pomSettings(model) : _*)
|
||||||
val build = new Build { override def projects = Seq(root) }
|
val build = new Build { override def projects = Seq(root) }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue