From a44a14f2c838745a1ab7c741dafccdd8aa3faa88 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Wed, 5 Mar 2014 18:03:00 -0500 Subject: [PATCH] AutoPlugins appropriately participate in AddSettings. * Add new AutoPlugins type to AddSettings. * Ensure any Plugins filter doesn't just automatically always add autoplugins every time. * Load.scala can now adjust AutoPlugins ordering Note: Adjusting autoplugin ordering is dangerous BUT doing a glob of "put autoplugin settings here" is generally ok. --- main/src/main/scala/sbt/AddSettings.scala | 11 ++++++----- main/src/main/scala/sbt/Load.scala | 12 ++++++++---- main/src/main/scala/sbt/Plugins.scala | 23 ++++++++++++++++++++++- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/main/src/main/scala/sbt/AddSettings.scala b/main/src/main/scala/sbt/AddSettings.scala index ab90c8d8f..ceb0bc751 100644 --- a/main/src/main/scala/sbt/AddSettings.scala +++ b/main/src/main/scala/sbt/AddSettings.scala @@ -12,22 +12,23 @@ object AddSettings private[sbt] final class Sequence(val sequence: Seq[AddSettings]) extends AddSettings private[sbt] final object User extends AddSettings private[sbt] final class Plugins(val include: Plugin => 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 SbtFiles(val files: Seq[File]) extends AddSettings // Settings created with the Project().settings() commands in build.scala files. private[sbt] final object ProjectSettings extends AddSettings - /** Adds all settings from a plugin to a project. */ - val allPlugins: AddSettings = plugins(const(true)) - /** Adds all settings from autoplugins. */ - val autoPlugins: AddSettings = plugins(_.isInstanceOf[AutoPlugin]) + val autoPlugins: AddSettings = new AutoPlugins(const(true)) /** Settings specified in Build.scala `Project` constructors. */ val projectSettings: AddSettings = ProjectSettings /** All plugins that aren't auto plugins. */ - val nonAutoPlugins: AddSettings = plugins(!_.isInstanceOf[AutoPlugin]) + val nonAutoPlugins: AddSettings = plugins(const(true)) + + /** Adds all settings from a plugin to a project. */ + val allPlugins: AddSettings = seq(autoPlugins, nonAutoPlugins) /** Allows the plugins whose names match the `names` filter to automatically add settings to a project. */ def plugins(include: Plugin => Boolean): AddSettings = new Plugins(include) diff --git a/main/src/main/scala/sbt/Load.scala b/main/src/main/scala/sbt/Load.scala index b040ee0ae..a275c907a 100755 --- a/main/src/main/scala/sbt/Load.scala +++ b/main/src/main/scala/sbt/Load.scala @@ -505,19 +505,23 @@ object Load def loadSettingsFile(src: File): LoadedSbtFile = EvaluateConfigurations.evaluateSbtFile(eval(), src, IO.readLines(src), loadedPlugins.detected.imports, 0)(loader) - import AddSettings.{User,SbtFiles,DefaultSbtFiles,Plugins,Sequence, ProjectSettings} + import AddSettings.{User,SbtFiles,DefaultSbtFiles,Plugins,AutoPlugins,Sequence, ProjectSettings} def pluginSettings(f: Plugins) = { val included = loadedPlugins.detected.plugins.values.filter(f.include) // don't apply the filter to AutoPlugins, only Plugins - val oldStyle = included.flatMap(p => p.settings.filter(isProjectThis) ++ p.projectSettings) - val autoStyle = autoPlugins.flatMap(_.projectSettings) - oldStyle ++ autoStyle + included.flatMap(p => p.settings.filter(isProjectThis) ++ p.projectSettings) } + // Filter the AutoPlugin settings we included based on which ones are + // intended in the AddSettings.AutoPlugins filter. + def autoPluginSettings(f: AutoPlugins) = + autoPlugins.filter(f.include).flatMap(_.projectSettings) + def expand(auto: AddSettings): LoadedSbtFile = auto match { case ProjectSettings => settings(projectSettings) case User => settings(injectSettings.projectLoaded(loader)) case sf: SbtFiles => loadSettings( sf.files.map(f => IO.resolve(projectBase, f))) case sf: DefaultSbtFiles => loadSettings( defaultSbtFiles.filter(sf.include)) case p: Plugins => settings(pluginSettings(p)) + case p: AutoPlugins => settings(autoPluginSettings(p)) case q: Sequence => (LoadedSbtFile.empty /: q.sequence) { (b,add) => b.merge( expand(add) ) } } expand(auto) diff --git a/main/src/main/scala/sbt/Plugins.scala b/main/src/main/scala/sbt/Plugins.scala index 5e814d082..a7ada9b6e 100644 --- a/main/src/main/scala/sbt/Plugins.scala +++ b/main/src/main/scala/sbt/Plugins.scala @@ -72,6 +72,24 @@ abstract class AutoPlugin extends Plugins.Basic // TODO?: def commands: Seq[Command] def unary_! : Exclude = Exclude(this) + + + /** If this plugin requries itself to be included, it means we're actually a nature, + * not a normal plugin. The user must specifically enable this plugin + * but other plugins can rely on its existence. + */ + final def isRoot: Boolean = + this match { + case _: RootAutoPlugin => true + case _ => false + } +} +/** + * A root AutoPlugin is a plugin which must be explicitly enabled by users in their `setPlugins` method + * on a project. However, RootAutoPlugins represent the "root" of a tree of dependent auto-plugins. + */ +abstract class RootAutoPlugin extends AutoPlugin { + final def select: Plugins = this } /** An error that occurs when auto-plugins aren't configured properly. @@ -104,7 +122,10 @@ object Plugins val byAtom = defined.map(x => (Atom(x.label), x)) val byAtomMap = byAtom.toMap if(byAtom.size != byAtomMap.size) duplicateProvidesError(byAtom) - val clauses = Clauses( defined.map(d => asClause(d)) ) + // Ignore clauses for plugins that just require themselves be specified. + // Avoids the requirement for pure Nature strings *and* possible + // circular dependencies in the logic. + val clauses = Clauses( defined.filterNot(_.isRoot).map(d => asClause(d)) ) requestedPlugins => Logic.reduce(clauses, flattenConvert(requestedPlugins).toSet) match { case Left(problem) => throw AutoPluginException(problem)