addPluginSbtFile command fixes

Ref #4211
Fixes #4395
Fixes #4600

This is a reimplementation of `--addPluginSbtFile`. #4211 implemented the command to load extra `*.sbt` files as part of the global plugin subproject. That had the unwanted side effects of not working when `.sbt/1.0/plugins` directory does not exist. This changes the strategy to load the `*.sbt` files as part of the meta build.

```
$ sbt -Dsbt.global.base=/tmp/hello/global --addPluginSbtFile=/tmp/plugins/plugin.sbt
[info] Loading settings for project hello-build from plugin.sbt ...
[info] Loading project definition from /private/tmp/hello/project
sbt:hello> plugins
In file:/private/tmp/hello/
	sbt.plugins.IvyPlugin: enabled in root
	sbt.plugins.JvmPlugin: enabled in root
	sbt.plugins.CorePlugin: enabled in root
	sbt.ScriptedPlugin
	sbt.plugins.SbtPlugin
	sbt.plugins.SemanticdbPlugin: enabled in root
	sbt.plugins.JUnitXmlReportPlugin: enabled in root
	sbt.plugins.Giter8TemplatePlugin: enabled in root
	sbtvimquit.VimquitPlugin: enabled in root
```
This commit is contained in:
Eugene Yokota 2019-11-10 03:01:28 -05:00
parent e17c64dfb6
commit 033601c393
3 changed files with 50 additions and 26 deletions

View File

@ -789,6 +789,9 @@ lazy val mainProj = (project in file("main"))
exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inConfig"), exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inConfig"),
exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inTask"), exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inTask"),
exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inScope"), exclude[IncompatibleSignatureProblem]("sbt.ProjectExtra.inScope"),
exclude[MissingTypesProblem]("sbt.internal.Load*"),
exclude[IncompatibleSignatureProblem]("sbt.internal.Load*"),
) )
) )
.configure( .configure(

View File

@ -18,11 +18,15 @@ import java.io.File
sealed abstract class AddSettings sealed abstract class AddSettings
object 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 object User extends AddSettings
private[sbt] final class AutoPlugins(val include: AutoPlugin => Boolean) 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 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 private[sbt] final object BuildScalaFiles extends AddSettings
/** Adds all settings from autoplugins. */ /** Adds all settings from autoplugins. */

View File

@ -875,10 +875,12 @@ private[sbt] object Load {
def discover(base: File): DiscoveredProjects = { def discover(base: File): DiscoveredProjects = {
val auto = val auto =
if (base == buildBase) AddSettings.allDefaults if (base == buildBase) AddSettings.allDefaults
else if (context.globalPluginProject)
AddSettings.seq(AddSettings.defaultSbtFiles, AddSettings.sbtFiles(extraSbtFiles: _*))
else AddSettings.defaultSbtFiles else AddSettings.defaultSbtFiles
discoverProjects(auto, base, plugins, eval, memoSettings)
val extraFiles =
if (base == buildBase && isMetaBuildContext(context)) extraSbtFiles
else Nil
discoverProjects(auto, base, extraFiles, plugins, eval, memoSettings)
} }
// Step two: // Step two:
@ -888,6 +890,7 @@ private[sbt] object Load {
def finalizeProject( def finalizeProject(
p: Project, p: Project,
files: Seq[File], files: Seq[File],
extraFiles: Seq[File],
expand: Boolean expand: Boolean
): (Project, Seq[Project]) = { ): (Project, Seq[Project]) = {
val configFiles = files.flatMap(f => memoSettings.get(f)) val configFiles = files.flatMap(f => memoSettings.get(f))
@ -895,8 +898,8 @@ private[sbt] object Load {
val autoPlugins: Seq[AutoPlugin] = val autoPlugins: Seq[AutoPlugin] =
try plugins.detected.deducePluginsFromProject(p1, log) try plugins.detected.deducePluginsFromProject(p1, log)
catch { case e: AutoPluginException => throw translateAutoPluginException(e, p) } catch { case e: AutoPluginException => throw translateAutoPluginException(e, p) }
val extra = if (context.globalPluginProject) extraSbtFiles else Nil val p2 =
val p2 = resolveProject(p1, autoPlugins, plugins, injectSettings, memoSettings, extra, log) resolveProject(p1, autoPlugins, plugins, injectSettings, memoSettings, extraFiles, log)
val projectLevelExtra = val projectLevelExtra =
if (expand) { if (expand) {
autoPlugins.flatMap( autoPlugins.flatMap(
@ -908,12 +911,15 @@ private[sbt] object Load {
// Discover any new project definition for the base directory of this project, and load all settings. // Discover any new project definition for the base directory of this project, and load all settings.
def discoverAndLoad(p: Project, rest: Seq[Project]): LoadedProjects = { def discoverAndLoad(p: Project, rest: Seq[Project]): LoadedProjects = {
val DiscoveredProjects(rootOpt, discovered, files, generated) = discover(p.base) 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 // 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 // phony. However, we may want to 'merge' the two, or only do this if the original was a
// default generated project. // default generated project.
val root = rootOpt.getOrElse(p) val root = rootOpt.getOrElse(p)
val (finalRoot, projectLevelExtra) = finalizeProject(root, files, true) val (finalRoot, projectLevelExtra) = finalizeProject(root, files, extraFiles, true)
val newProjects = rest ++ discovered ++ projectLevelExtra val newProjects = rest ++ discovered ++ projectLevelExtra
val newAcc = acc :+ finalRoot val newAcc = acc :+ finalRoot
val newGenerated = generated ++ generatedConfigClassFiles val newGenerated = generated ++ generatedConfigClassFiles
@ -928,7 +934,9 @@ private[sbt] object Load {
discoverAndLoad(next, rest) discoverAndLoad(next, rest)
case Nil if makeOrDiscoverRoot => case Nil if makeOrDiscoverRoot =>
log.debug(s"[Loading] Scanning directory $buildBase") log.debug(s"[Loading] Scanning directory $buildBase")
val DiscoveredProjects(rootOpt, discovered, files, generated) = discover(buildBase) val DiscoveredProjects(rootOpt, discovered, files, extraFiles, generated) = discover(
buildBase
)
val discoveredIdsStr = discovered.map(_.id).mkString(",") val discoveredIdsStr = discovered.map(_.id).mkString(",")
val (root, expand, moreProjects, otherProjects) = rootOpt match { val (root, expand, moreProjects, otherProjects) = rootOpt match {
case Some(root) => case Some(root) =>
@ -950,7 +958,7 @@ private[sbt] object Load {
} }
val (finalRoot, projectLevelExtra) = val (finalRoot, projectLevelExtra) =
timed(s"Load.loadTransitive: finalizeProject($root)", log) { timed(s"Load.loadTransitive: finalizeProject($root)", log) {
finalizeProject(root, files, expand) finalizeProject(root, files, extraFiles, expand)
} }
val newProjects = moreProjects ++ projectLevelExtra val newProjects = moreProjects ++ projectLevelExtra
val newAcc = finalRoot +: (acc ++ otherProjects.projects) val newAcc = finalRoot +: (acc ++ otherProjects.projects)
@ -983,6 +991,7 @@ private[sbt] object Load {
root: Option[Project], root: Option[Project],
nonRoot: Seq[Project], nonRoot: Seq[Project],
sbtFiles: Seq[File], sbtFiles: Seq[File],
extraSbtFiles: Seq[File],
generatedFiles: Seq[File] generatedFiles: Seq[File]
) )
@ -1017,6 +1026,7 @@ private[sbt] object Load {
val allSettings = { val allSettings = {
// TODO - This mechanism of applying settings could be off... It's in two places now... // TODO - This mechanism of applying settings could be off... It's in two places now...
lazy val defaultSbtFiles = configurationSources(p.base) lazy val defaultSbtFiles = configurationSources(p.base)
lazy val sbtFiles = defaultSbtFiles ++ extraSbtFiles
// Filter the AutoPlugin settings we included based on which ones are // Filter the AutoPlugin settings we included based on which ones are
// intended in the AddSettings.AutoPlugins filter. // intended in the AddSettings.AutoPlugins filter.
def autoPluginSettings(f: AutoPlugins) = def autoPluginSettings(f: AutoPlugins) =
@ -1038,23 +1048,14 @@ private[sbt] object Load {
case BuildScalaFiles => p.settings case BuildScalaFiles => p.settings
case User => globalUserSettings.cachedProjectLoaded(loadedPlugins.loader) case User => globalUserSettings.cachedProjectLoaded(loadedPlugins.loader)
case sf: SbtFiles => settings(sf.files.map(f => IO.resolve(p.base, f))) 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 p: AutoPlugins => autoPluginSettings(p)
case q: Sequence => case q: Sequence =>
q.sequence.foldLeft(Seq.empty[Setting[_]]) { (b, add) => q.sequence.foldLeft(Seq.empty[Setting[_]]) { (b, add) =>
b ++ expandSettings(add) b ++ expandSettings(add)
} }
} }
val auto = val auto = AddSettings.allDefaults
if (extraSbtFiles.nonEmpty)
AddSettings.seq(
AddSettings.autoPlugins,
AddSettings.buildScalaFiles,
AddSettings.userSettings,
AddSettings.defaultSbtFiles,
AddSettings.sbtFiles(extraSbtFiles: _*),
)
else AddSettings.allDefaults
expandSettings(auto) expandSettings(auto)
} }
// Finally, a project we can use in buildStructure. // Finally, a project we can use in buildStructure.
@ -1074,6 +1075,7 @@ private[sbt] object Load {
private[this] def discoverProjects( private[this] def discoverProjects(
auto: AddSettings, auto: AddSettings,
projectBase: File, projectBase: File,
extraSbtFiles: Seq[File],
loadedPlugins: LoadedPlugins, loadedPlugins: LoadedPlugins,
eval: () => Eval, eval: () => Eval,
memoSettings: mutable.Map[File, LoadedSbtFile] memoSettings: mutable.Map[File, LoadedSbtFile]
@ -1081,6 +1083,7 @@ private[sbt] object Load {
// Default sbt files to read, if needed // Default sbt files to read, if needed
lazy val defaultSbtFiles = configurationSources(projectBase) lazy val defaultSbtFiles = configurationSources(projectBase)
lazy val sbtFiles = defaultSbtFiles ++ extraSbtFiles
// Classloader of the build // Classloader of the build
val loader = loadedPlugins.loader val loader = loadedPlugins.loader
@ -1117,7 +1120,7 @@ private[sbt] object Load {
import AddSettings.{ DefaultSbtFiles, SbtFiles, Sequence } import AddSettings.{ DefaultSbtFiles, SbtFiles, Sequence }
def associatedFiles(auto: AddSettings): Seq[File] = auto match { def associatedFiles(auto: AddSettings): Seq[File] = auto match {
case sf: SbtFiles => sf.files.map(f => IO.resolve(projectBase, f)).filterNot(_.isHidden) 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 => case q: Sequence =>
q.sequence.foldLeft(Seq.empty[File]) { (b, add) => q.sequence.foldLeft(Seq.empty[File]) { (b, add) =>
b ++ associatedFiles(add) b ++ associatedFiles(add)
@ -1129,7 +1132,13 @@ private[sbt] object Load {
val rawProjects = loadedFiles.projects val rawProjects = loadedFiles.projects
val (root, nonRoot) = rawProjects.partition(_.base == projectBase) val (root, nonRoot) = rawProjects.partition(_.base == projectBase)
// TODO - good error message if more than one root project // 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]] = def globalPluginClasspath(globalPlugin: Option[GlobalPlugin]): Seq[Attributed[File]] =
@ -1184,11 +1193,19 @@ private[sbt] object Load {
case None => config case None => config
} }
def plugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins = def plugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins = {
if (hasDefinition(dir)) 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))) buildPlugins(dir, s, enableSbtPlugin(activateGlobalPlugin(config)))
else else
noPlugins(dir, config) noPlugins(dir, config)
}
private def isMetaBuildContext(context: PluginManagement.Context): Boolean =
!context.globalPluginProject && context.pluginProjectDepth == 1
def hasDefinition(dir: File): Boolean = { def hasDefinition(dir: File): Boolean = {
import sbt.io.syntax._ import sbt.io.syntax._