|
|
|
|
@ -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._
|
|
|
|
|
|