diff --git a/build.sbt b/build.sbt
index 5d03153b8..d02ec41d0 100644
--- a/build.sbt
+++ b/build.sbt
@@ -785,6 +785,9 @@ lazy val mainProj = (project in file("main"))
exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inConfig"),
exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inTask"),
exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inScope"),
+
+ exclude[MissingTypesProblem]("sbt.internal.Load*"),
+ exclude[IncompatibleSignatureProblem]("sbt.internal.Load*"),
)
)
.configure(
diff --git a/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala b/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala
index b437cfd32..a579e766c 100644
--- a/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala
+++ b/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala
@@ -45,6 +45,8 @@ object CoursierInputsTasks {
sv: String,
sbv: String,
auOpt: Option[URL],
+ description: String,
+ homepage: Option[URL],
log: Logger
): CProject = {
@@ -63,12 +65,15 @@ object CoursierInputsTasks {
case (config, dep) =>
(config, dep.withExclusions(dep.exclusions ++ exclusions0))
})
- auOpt match {
+ val proj2 = auOpt match {
case Some(au) =>
val props = proj1.properties :+ ("info.apiURL" -> au.toString)
proj1.withProperties(props)
case _ => proj1
}
+ proj2.withInfo(
+ proj2.info.withDescription(description).withHomePage(homepage.fold("")(_.toString))
+ )
}
def coursierProjectTask: Def.Initialize[sbt.Task[CProject]] =
@@ -81,6 +86,8 @@ object CoursierInputsTasks {
scalaVersion.value,
scalaBinaryVersion.value,
apiURL.value,
+ description.value,
+ homepage.value,
streams.value.log
)
}
diff --git a/main/src/main/scala/sbt/internal/AddSettings.scala b/main/src/main/scala/sbt/internal/AddSettings.scala
index 2fd4a6667..14acc5c05 100644
--- a/main/src/main/scala/sbt/internal/AddSettings.scala
+++ b/main/src/main/scala/sbt/internal/AddSettings.scala
@@ -18,11 +18,15 @@ import java.io.File
sealed abstract class AddSettings
object AddSettings {
- private[sbt] final class Sequence(val sequence: Seq[AddSettings]) extends AddSettings
+ private[sbt] final class Sequence(val sequence: Seq[AddSettings]) extends AddSettings {
+ override def toString: String = s"Sequence($sequence)"
+ }
private[sbt] final object User extends AddSettings
private[sbt] final class AutoPlugins(val include: AutoPlugin => Boolean) extends AddSettings
private[sbt] final class DefaultSbtFiles(val include: File => Boolean) extends AddSettings
- private[sbt] final class SbtFiles(val files: Seq[File]) extends AddSettings
+ private[sbt] final class SbtFiles(val files: Seq[File]) extends AddSettings {
+ override def toString: String = s"SbtFiles($files)"
+ }
private[sbt] final object BuildScalaFiles extends AddSettings
/** Adds all settings from autoplugins. */
diff --git a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala
index 837730bbf..12b94188c 100644
--- a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala
+++ b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala
@@ -421,6 +421,13 @@ private[sbt] class BackgroundThreadPool extends java.io.Closeable {
status = Stopped(Some(thread))
thread.interrupt()
case Stopped(threadOption) =>
+ // sleep to avoid consuming a lot of CPU
+ try {
+ Thread.sleep(10)
+ } catch {
+ case e: InterruptedException =>
+ Thread.currentThread().interrupt();
+ }
// try to interrupt again! woot!
threadOption.foreach(_.interrupt())
}
diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala
index 63a090a9e..622d41cd6 100755
--- a/main/src/main/scala/sbt/internal/Load.scala
+++ b/main/src/main/scala/sbt/internal/Load.scala
@@ -814,25 +814,27 @@ private[sbt] object Load {
* 1. The first `Project` instance we encounter defines AddSettings and gets to specify where we pull other settings.
* 2. Any project manipulation (enable/disablePlugins) is ok to be added in the order we encounter it.
*
- * Any further setting is ignored, as even the SettingSet API should be deprecated/removed with sbt 1.0.
+ * Any further setting is ignored.
*
* Note: Lots of internal details in here that shouldn't be otherwise exposed.
*
- * @param newProjects A sequence of projects we have not yet loaded, but will try to. Must not be Nil
+ * TODO - We want to attach the known (at this time) vals/lazy vals defined in each project's
+ * build.sbt to that project so we can later use this for the `set` command.
+ *
+ * @param newProjects A sequence of projects we have not yet loaded, but will try to.
* @param buildBase The `baseDirectory` for the entire build.
* @param plugins A misnomer, this is actually the compiled BuildDefinition (classpath and such) for this project.
* @param eval A mechanism of generating an "Eval" which can compile scala code for us.
* @param injectSettings Settings we need to inject into projects.
- * @param acc An accumulated list of loaded projects. TODO - how do these differ from newProjects?
+ * @param acc An accumulated list of loaded projects, originally in newProjects.
* @param memoSettings A recording of all sbt files that have been loaded so far.
* @param log The logger used for this project.
- * @param makeOrDiscoverRoot True if we should autogenerate a root project.
+ * @param makeOrDiscoverRoot True if we should auto-generate a root project.
* @param buildUri The URI of the build this is loading
* @param context The plugin management context for autogenerated IDs.
+ * @param generatedConfigClassFiles
+ * @param extraSbtFiles
* @return The completely resolved/updated sequence of projects defined, with all settings expanded.
- *
- * TODO - We want to attach the known (at this time) vals/lazy vals defined in each project's
- * build.sbt to that project so we can later use this for the `set` command.
*/
private[this] def loadTransitive(
newProjects: Seq[Project],
@@ -850,9 +852,37 @@ private[sbt] object Load {
extraSbtFiles: Seq[File]
): LoadedProjects =
/*timed(s"Load.loadTransitive(${ newProjects.map(_.id) })", log)*/ {
+
+ def load(newProjects: Seq[Project], acc: Seq[Project], generated: Seq[File]) = {
+ loadTransitive(
+ newProjects,
+ buildBase,
+ plugins,
+ eval,
+ injectSettings,
+ acc,
+ memoSettings,
+ log,
+ false,
+ buildUri,
+ context,
+ generated,
+ Nil
+ )
+ }
+
// load all relevant configuration files (.sbt, as .scala already exists at this point)
- def discover(auto: AddSettings, base: File): DiscoveredProjects =
- discoverProjects(auto, base, plugins, eval, memoSettings)
+ def discover(base: File): DiscoveredProjects = {
+ val auto =
+ if (base == buildBase) AddSettings.allDefaults
+ else AddSettings.defaultSbtFiles
+
+ val extraFiles =
+ if (base == buildBase && isMetaBuildContext(context)) extraSbtFiles
+ else Nil
+ discoverProjects(auto, base, extraFiles, plugins, eval, memoSettings)
+ }
+
// Step two:
// a. Apply all the project manipulations from .sbt files in order
// b. Deduce the auto plugins for the project
@@ -860,44 +890,40 @@ private[sbt] object Load {
def finalizeProject(
p: Project,
files: Seq[File],
+ extraFiles: Seq[File],
expand: Boolean
): (Project, Seq[Project]) = {
- val configFiles = files flatMap { f =>
- memoSettings.get(f)
- }
- val p1: Project =
- configFiles.flatMap(_.manipulations).foldLeft(p) { (prev, t) =>
- t(prev)
- }
+ val configFiles = files.flatMap(f => memoSettings.get(f))
+ val p1: Project = Function.chain(configFiles.flatMap(_.manipulations))(p)
val autoPlugins: Seq[AutoPlugin] =
try plugins.detected.deducePluginsFromProject(p1, log)
catch { case e: AutoPluginException => throw translateAutoPluginException(e, p) }
- val extra =
- if (context.globalPluginProject) extraSbtFiles
- else Nil
val p2 =
- this.resolveProject(p1, autoPlugins, plugins, injectSettings, memoSettings, extra, log)
+ resolveProject(p1, autoPlugins, plugins, injectSettings, memoSettings, extraFiles, log)
val projectLevelExtra =
- if (expand) autoPlugins flatMap {
- _.derivedProjects(p2) map { _.setProjectOrigin(ProjectOrigin.DerivedProject) }
+ if (expand) {
+ autoPlugins.flatMap(
+ _.derivedProjects(p2).map(_.setProjectOrigin(ProjectOrigin.DerivedProject))
+ )
} else Nil
(p2, projectLevelExtra)
}
+
// Discover any new project definition for the base directory of this project, and load all settings.
- // Also return any newly discovered project instances.
- def discoverAndLoad(p: Project): (Project, Seq[Project], Seq[File]) = {
- val (root, discovered, files, generated) =
- discover(AddSettings.allDefaults, p.base) match {
- case DiscoveredProjects(Some(root), rest, files, generated) =>
- // TODO - We assume here the project defined in a build.sbt WINS because the original was
- // a phony. However, we may want to 'merge' the two, or only do this if the original was a default
- // generated project.
- (root, rest, files, generated)
- case DiscoveredProjects(None, rest, files, generated) => (p, rest, files, generated)
- }
- val (finalRoot, projectLevelExtra) =
- finalizeProject(root, files, true)
- (finalRoot, discovered ++ projectLevelExtra, generated)
+ def discoverAndLoad(p: Project, rest: Seq[Project]): LoadedProjects = {
+ val DiscoveredProjects(rootOpt, discovered, files, extraFiles, generated) = discover(
+ p.base
+ )
+
+ // TODO: We assume here the project defined in a build.sbt WINS because the original was a
+ // phony. However, we may want to 'merge' the two, or only do this if the original was a
+ // default generated project.
+ val root = rootOpt.getOrElse(p)
+ val (finalRoot, projectLevelExtra) = finalizeProject(root, files, extraFiles, true)
+ val newProjects = rest ++ discovered ++ projectLevelExtra
+ val newAcc = acc :+ finalRoot
+ val newGenerated = generated ++ generatedConfigClassFiles
+ load(newProjects, newAcc, newGenerated)
}
// Load all config files AND finalize the project at the root directory, if it exists.
@@ -905,92 +931,43 @@ private[sbt] object Load {
newProjects match {
case Seq(next, rest @ _*) =>
log.debug(s"[Loading] Loading project ${next.id} @ ${next.base}")
- val (finished, discovered, generated) = discoverAndLoad(next)
- loadTransitive(
- rest ++ discovered,
- buildBase,
- plugins,
- eval,
- injectSettings,
- acc :+ finished,
- memoSettings,
- log,
- false,
- buildUri,
- context,
- generated ++ generatedConfigClassFiles,
- Nil
- )
+ discoverAndLoad(next, rest)
case Nil if makeOrDiscoverRoot =>
- log.debug(s"[Loading] Scanning directory ${buildBase}")
- val auto =
- if (context.globalPluginProject)
- AddSettings.seq(AddSettings.defaultSbtFiles, AddSettings.sbtFiles(extraSbtFiles: _*))
- else AddSettings.defaultSbtFiles
- discover(auto, buildBase) match {
- case DiscoveredProjects(Some(root), discovered, files, generated) =>
- log.debug(
- s"[Loading] Found root project ${root.id} w/ remaining ${discovered.map(_.id).mkString(",")}"
- )
- val (finalRoot, projectLevelExtra) =
- timed(s"Load.loadTransitive: finalizeProject($root)", log) {
- finalizeProject(root, files, true)
- }
- loadTransitive(
- discovered ++ projectLevelExtra,
- buildBase,
- plugins,
- eval,
- injectSettings,
- finalRoot +: acc,
- memoSettings,
- log,
- false,
- buildUri,
- context,
- generated ++ generatedConfigClassFiles,
- Nil
- )
- // Here we need to create a root project...
- case DiscoveredProjects(None, discovered, files, generated) =>
- log.debug(s"[Loading] Found non-root projects ${discovered.map(_.id).mkString(",")}")
+ log.debug(s"[Loading] Scanning directory $buildBase")
+ val DiscoveredProjects(rootOpt, discovered, files, extraFiles, generated) = discover(
+ buildBase
+ )
+ val discoveredIdsStr = discovered.map(_.id).mkString(",")
+ val (root, expand, moreProjects, otherProjects) = rootOpt match {
+ case Some(root) =>
+ log.debug(s"[Loading] Found root project ${root.id} w/ remaining $discoveredIdsStr")
+ (root, true, discovered, LoadedProjects(Nil, Nil))
+ case None =>
+ log.debug(s"[Loading] Found non-root projects $discoveredIdsStr")
// Here we do something interesting... We need to create an aggregate root project
- val otherProjects = loadTransitive(
- discovered,
- buildBase,
- plugins,
- eval,
- injectSettings,
- acc,
- memoSettings,
- log,
- false,
- buildUri,
- context,
- Nil,
- Nil
- )
- val otherGenerated = otherProjects.generatedConfigClassFiles
- val existingIds = otherProjects.projects map (_.id)
- val refs = existingIds map (id => ProjectRef(buildUri, id))
- val defaultID = autoID(buildBase, context, existingIds)
- val root0 =
+ val otherProjects = load(discovered, acc, Nil)
+ val root = {
+ val existingIds = otherProjects.projects.map(_.id)
+ val defaultID = autoID(buildBase, context, existingIds)
+ val refs = existingIds.map(id => ProjectRef(buildUri, id))
if (discovered.isEmpty || java.lang.Boolean.getBoolean("sbt.root.ivyplugin"))
BuildDef.defaultAggregatedProject(defaultID, buildBase, refs)
else BuildDef.generatedRootWithoutIvyPlugin(defaultID, buildBase, refs)
- val (root, _) = timed(s"Load.loadTransitive: finalizeProject2($root0)", log) {
- finalizeProject(root0, files, false)
}
- val result = root +: (acc ++ otherProjects.projects)
- log.debug(
- s"[Loading] Done in ${buildBase}, returning: ${result.map(_.id).mkString("(", ", ", ")")}"
- )
- LoadedProjects(result, generated ++ otherGenerated ++ generatedConfigClassFiles)
+ (root, false, Nil, otherProjects)
}
+ val (finalRoot, projectLevelExtra) =
+ timed(s"Load.loadTransitive: finalizeProject($root)", log) {
+ finalizeProject(root, files, extraFiles, expand)
+ }
+ val newProjects = moreProjects ++ projectLevelExtra
+ val newAcc = finalRoot +: (acc ++ otherProjects.projects)
+ val newGenerated =
+ generated ++ otherProjects.generatedConfigClassFiles ++ generatedConfigClassFiles
+ load(newProjects, newAcc, newGenerated)
case Nil =>
- log.debug(
- s"[Loading] Done in ${buildBase}, returning: ${acc.map(_.id).mkString("(", ", ", ")")}"
- )
+ val projectIds = acc.map(_.id).mkString("(", ", ", ")")
+ log.debug(s"[Loading] Done in $buildBase, returning: $projectIds")
LoadedProjects(acc, generatedConfigClassFiles)
}
}
@@ -1014,6 +991,7 @@ private[sbt] object Load {
root: Option[Project],
nonRoot: Seq[Project],
sbtFiles: Seq[File],
+ extraSbtFiles: Seq[File],
generatedFiles: Seq[File]
)
@@ -1048,6 +1026,7 @@ private[sbt] object Load {
val allSettings = {
// TODO - This mechanism of applying settings could be off... It's in two places now...
lazy val defaultSbtFiles = configurationSources(p.base)
+ lazy val sbtFiles = defaultSbtFiles ++ extraSbtFiles
// Filter the AutoPlugin settings we included based on which ones are
// intended in the AddSettings.AutoPlugins filter.
def autoPluginSettings(f: AutoPlugins) =
@@ -1069,23 +1048,14 @@ private[sbt] object Load {
case BuildScalaFiles => p.settings
case User => globalUserSettings.cachedProjectLoaded(loadedPlugins.loader)
case sf: SbtFiles => settings(sf.files.map(f => IO.resolve(p.base, f)))
- case sf: DefaultSbtFiles => settings(defaultSbtFiles.filter(sf.include))
+ case sf: DefaultSbtFiles => settings(sbtFiles.filter(sf.include))
case p: AutoPlugins => autoPluginSettings(p)
case q: Sequence =>
q.sequence.foldLeft(Seq.empty[Setting[_]]) { (b, add) =>
b ++ expandSettings(add)
}
}
- val auto =
- if (extraSbtFiles.nonEmpty)
- AddSettings.seq(
- AddSettings.autoPlugins,
- AddSettings.buildScalaFiles,
- AddSettings.userSettings,
- AddSettings.defaultSbtFiles,
- AddSettings.sbtFiles(extraSbtFiles: _*),
- )
- else AddSettings.allDefaults
+ val auto = AddSettings.allDefaults
expandSettings(auto)
}
// Finally, a project we can use in buildStructure.
@@ -1105,6 +1075,7 @@ private[sbt] object Load {
private[this] def discoverProjects(
auto: AddSettings,
projectBase: File,
+ extraSbtFiles: Seq[File],
loadedPlugins: LoadedPlugins,
eval: () => Eval,
memoSettings: mutable.Map[File, LoadedSbtFile]
@@ -1112,6 +1083,7 @@ private[sbt] object Load {
// Default sbt files to read, if needed
lazy val defaultSbtFiles = configurationSources(projectBase)
+ lazy val sbtFiles = defaultSbtFiles ++ extraSbtFiles
// Classloader of the build
val loader = loadedPlugins.loader
@@ -1148,7 +1120,7 @@ private[sbt] object Load {
import AddSettings.{ DefaultSbtFiles, SbtFiles, Sequence }
def associatedFiles(auto: AddSettings): Seq[File] = auto match {
case sf: SbtFiles => sf.files.map(f => IO.resolve(projectBase, f)).filterNot(_.isHidden)
- case sf: DefaultSbtFiles => defaultSbtFiles.filter(sf.include).filterNot(_.isHidden)
+ case sf: DefaultSbtFiles => sbtFiles.filter(sf.include).filterNot(_.isHidden)
case q: Sequence =>
q.sequence.foldLeft(Seq.empty[File]) { (b, add) =>
b ++ associatedFiles(add)
@@ -1160,7 +1132,13 @@ private[sbt] object Load {
val rawProjects = loadedFiles.projects
val (root, nonRoot) = rawProjects.partition(_.base == projectBase)
// TODO - good error message if more than one root project
- DiscoveredProjects(root.headOption, nonRoot, rawFiles, loadedFiles.generatedFiles)
+ DiscoveredProjects(
+ root.headOption,
+ nonRoot,
+ rawFiles,
+ extraSbtFiles,
+ loadedFiles.generatedFiles
+ )
}
def globalPluginClasspath(globalPlugin: Option[GlobalPlugin]): Seq[Attributed[File]] =
@@ -1215,11 +1193,19 @@ private[sbt] object Load {
case None => config
}
- def plugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins =
- if (hasDefinition(dir))
+ def plugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins = {
+ val context = config.pluginManagement.context
+ val extraSbtFiles: Seq[File] =
+ if (isMetaBuildContext(context)) s.get(BasicKeys.extraMetaSbtFiles).getOrElse(Nil)
+ else Nil
+ if (hasDefinition(dir) || extraSbtFiles.nonEmpty)
buildPlugins(dir, s, enableSbtPlugin(activateGlobalPlugin(config)))
else
noPlugins(dir, config)
+ }
+
+ private def isMetaBuildContext(context: PluginManagement.Context): Boolean =
+ !context.globalPluginProject && context.pluginProjectDepth == 1
def hasDefinition(dir: File): Boolean = {
import sbt.io.syntax._
diff --git a/main/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala b/main/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala
index 9934d485c..95f5bb24f 100644
--- a/main/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala
+++ b/main/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala
@@ -104,6 +104,14 @@ object IvyXml {
}
}
+ val descriptionElem = {
+ val n = {project.info.description}
+ if (project.info.homePage.nonEmpty)
+ n % .attributes
+ else
+ n
+ }
+
val infoElem = {
{licenseElems}
- {project.info.description}
+ {descriptionElem}
} % infoAttrs
diff --git a/sbt/src/sbt-test/dependency-management/make-ivy-xml/build.sbt b/sbt/src/sbt-test/dependency-management/make-ivy-xml/build.sbt
new file mode 100644
index 000000000..98c0eadcb
--- /dev/null
+++ b/sbt/src/sbt-test/dependency-management/make-ivy-xml/build.sbt
@@ -0,0 +1,24 @@
+import scala.xml.XML
+
+val descriptionValue = "This is just a test"
+val homepageValue = "http://example.com"
+
+lazy val root = (project in file(".")) settings(
+ name := "ivy-xml-test",
+ description := descriptionValue,
+ homepage := Some(url(homepageValue)),
+
+ TaskKey[Unit]("checkIvyXml") := {
+ val ivyXml = XML.loadFile(makeIvyXml.value)
+ val description = (ivyXml \ "info" \ "description").head
+ val homepage = (description \ "@homepage").head
+
+ if (description.text != descriptionValue)
+ sys.error(s"Unexpected description: ${description.text}")
+
+ if (homepage.text != homepageValue)
+ sys.error(s"Unexpected homepage: ${homepage.text}")
+
+ ()
+ }
+)
diff --git a/sbt/src/sbt-test/dependency-management/make-ivy-xml/test b/sbt/src/sbt-test/dependency-management/make-ivy-xml/test
new file mode 100644
index 000000000..0c84f997a
--- /dev/null
+++ b/sbt/src/sbt-test/dependency-management/make-ivy-xml/test
@@ -0,0 +1 @@
+> checkIvyXml