From 7f8d21c2f1a54d9180eecc82baaacedbfd7b7790 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Wed, 5 Mar 2014 17:56:34 -0500 Subject: [PATCH] Remove Natures from AutoPlugins feature. * remove the notion of Natures from Autoplugins. * Update tests to use AutoPlugins with no selection for inclusion. * Rename exisitng Natures code to Plugins/PluginsDebug. --- main/src/main/scala/sbt/BuildStructure.scala | 6 +- main/src/main/scala/sbt/Load.scala | 2 +- main/src/main/scala/sbt/Main.scala | 6 +- .../sbt/{Natures.scala => Plugins.scala} | 95 ++++++++-------- ...{NaturesDebug.scala => PluginsDebug.scala} | 102 +++++++++--------- main/src/main/scala/sbt/Project.scala | 50 ++++----- .../sbt-test/project/auto-plugins/build.sbt | 6 +- .../project/auto-plugins/project/Q.scala | 13 ++- .../binary-plugin/changes/define/A.scala | 6 +- 9 files changed, 140 insertions(+), 146 deletions(-) rename main/src/main/scala/sbt/{Natures.scala => Plugins.scala} (69%) rename main/src/main/scala/sbt/{NaturesDebug.scala => PluginsDebug.scala} (82%) diff --git a/main/src/main/scala/sbt/BuildStructure.scala b/main/src/main/scala/sbt/BuildStructure.scala index 1fddbf2a0..d63752d87 100644 --- a/main/src/main/scala/sbt/BuildStructure.scala +++ b/main/src/main/scala/sbt/BuildStructure.scala @@ -71,7 +71,7 @@ final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, Resolv * In addition to auto-discovered [[Build]]s, this includes any auto-generated default [[Build]]s. * @param projects The list of all [[Project]]s from all [[Build]]s. * These projects have not yet been resolved, but they have had auto-plugins applied. -* In particular, each [[Project]]'s `autoPlugins` field is populated according to their configured `natures` +* In particular, each [[Project]]'s `autoPlugins` field is populated according to their configured `plugins` * and their `settings` and `configurations` updated as appropriate. * @param buildNames No longer used and will be deprecated once feasible. */ @@ -99,8 +99,8 @@ final class DetectedPlugins(val plugins: DetectedModules[Plugin], val autoImport /** Sequence of import expressions for the build definition. This includes the names of the [[Plugin]], [[Build]], and [[AutoImport]] modules, but not the [[AutoPlugin]] modules. */ lazy val imports: Seq[String] = BuildUtil.getImports(plugins.names ++ builds.names ++ autoImports.names) - /** A function to select the right [[AutoPlugin]]s from [[autoPlugins]] given the defined [[Natures]] for a [[Project]]. */ - lazy val compileNatures: Natures => Seq[AutoPlugin] = Natures.compile(autoPlugins.values.toList) + /** A function to select the right [[AutoPlugin]]s from [[autoPlugins]] for a [[Project]]. */ + lazy val compilePlugins: Plugins => Seq[AutoPlugin] = Plugins.compile(autoPlugins.values.toList) } /** The built and loaded build definition project. diff --git a/main/src/main/scala/sbt/Load.scala b/main/src/main/scala/sbt/Load.scala index 2a00e7329..d6c9eff85 100755 --- a/main/src/main/scala/sbt/Load.scala +++ b/main/src/main/scala/sbt/Load.scala @@ -463,7 +463,7 @@ object Load loadSettings(auto, base, plugins, eval, injectSettings, memoSettings, autoPlugins) def loadForProjects = newProjects map { project => val autoPlugins = - try plugins.detected.compileNatures(project.natures) + try plugins.detected.compilePlugins(project.plugins) catch { case e: AutoPluginException => throw translateAutoPluginException(e, project) } val autoConfigs = autoPlugins.flatMap(_.projectConfigurations) val loadedSbtFiles = loadSbtFiles(project.auto, project.base, autoPlugins) diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index ad5291ec2..c44bd5a1b 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -376,16 +376,16 @@ object BuiltinCommands else Help.empty def plugins = Command.command(PluginsCommand, pluginsBrief, pluginsDetailed) { s => - val helpString = NaturesDebug.helpAll(s) + val helpString = PluginsDebug.helpAll(s) System.out.println(helpString) s } val pluginParser: State => Parser[AutoPlugin] = s => { - val autoPlugins: Map[String, AutoPlugin] = NaturesDebug.autoPluginMap(s) + val autoPlugins: Map[String, AutoPlugin] = PluginsDebug.autoPluginMap(s) token(Space) ~> Act.knownIDParser(autoPlugins, "plugin") } def plugin = Command(PluginCommand)(pluginParser) { (s, plugin) => - val helpString = NaturesDebug.help(plugin, s) + val helpString = PluginsDebug.help(plugin, s) System.out.println(helpString) s } diff --git a/main/src/main/scala/sbt/Natures.scala b/main/src/main/scala/sbt/Plugins.scala similarity index 69% rename from main/src/main/scala/sbt/Natures.scala rename to main/src/main/scala/sbt/Plugins.scala index 06b0a0e2f..5e814d082 100644 --- a/main/src/main/scala/sbt/Natures.scala +++ b/main/src/main/scala/sbt/Plugins.scala @@ -8,7 +8,7 @@ TODO: import logic.{Atom, Clause, Clauses, Formula, Literal, Logic, Negated} import Logic.{CyclicNegation, InitialContradictions, InitialOverlap, LogicException} import Def.Setting - import Natures._ + import Plugins._ /** Marks a top-level object so that sbt will wildcard import it for .sbt files, `consoleProject`, and `set`. */ trait AutoImport @@ -18,11 +18,11 @@ An AutoPlugin defines a group of settings and the conditions where the settings The `select` method defines the conditions and a method like `projectSettings` defines the settings to add. Steps for plugin authors: -1. Determine the [[Nature]]s that, when present (or absent), activate the AutoPlugin. +1. Determine the [[AutoPlugins]]s that, when present (or absent), activate the AutoPlugin. 2. Determine the settings/configurations to automatically inject when activated. For example, the following will automatically add the settings in `projectSettings` - to a project that has both the `Web` and `Javascript` natures enabled. + to a project that has both the `Web` and `Javascript` plugins enabled. object MyPlugin extends AutoPlugin { def select = Web && Javascript @@ -30,28 +30,28 @@ For example, the following will automatically add the settings in `projectSettin } Steps for users: -1. Add dependencies on plugins as usual with addSbtPlugin -2. Add Natures to Projects, which will automatically select the plugin settings to add for those Projects. +1. Add dependencies on plugins in `project/plugins.sbt` as usual with `addSbtPlugin` +2. Add key plugins to Projects, which will automatically select the plugin + dependent plugin settings to add for those Projects. 3. Exclude plugins, if desired. -For example, given natures Web and Javascript (perhaps provided by plugins added with addSbtPlugin), +For example, given plugins Web and Javascript (perhaps provided by plugins added with addSbtPlugin), - .natures( Web && Javascript ) + .plugins( Web && Javascript ) will activate `MyPlugin` defined above and have its settings automatically added. If the user instead defines - .natures( Web && Javascript && !MyPlugin) + .plugins( Web && Javascript && !MyPlugin) then the `MyPlugin` settings (and anything that activates only when `MyPlugin` is activated) will not be added. */ -abstract class AutoPlugin extends Natures.Basic +abstract class AutoPlugin extends Plugins.Basic { - /** This AutoPlugin will be activated for a project when the [[Natures]] matcher returned by this method matches that project's natures - * AND the user does not explicitly exclude the Nature returned by `provides`. + /** This AutoPlugin will be activated for a project when the [[Plugins]] matcher returned by this method matches that project's plugins + * AND the user does not explicitly exclude the Plugin returned by `provides`. * * For example, if this method returns `Web && Javascript`, this plugin instance will only be added - * if the `Web` and `Javascript` natures are enabled. */ - def select: Natures + * if the `Web` and `Javascript` plugins are enabled. */ + def select: Plugins val label: String = getClass.getName.stripSuffix("$") @@ -84,26 +84,19 @@ final class AutoPluginException private(val message: String, val origin: Option[ object AutoPluginException { def apply(msg: String): AutoPluginException = new AutoPluginException(msg, None) - def apply(origin: LogicException): AutoPluginException = new AutoPluginException(Natures.translateMessage(origin), Some(origin)) + def apply(origin: LogicException): AutoPluginException = new AutoPluginException(Plugins.translateMessage(origin), Some(origin)) } -/** An expression that matches `Nature`s. */ -sealed trait Natures { - def && (o: Basic): Natures +/** An expression that matches `AutoPlugin`s. */ +sealed trait Plugins { + def && (o: Basic): Plugins } -/** Represents a feature or conceptual group of settings. -* `label` is the unique ID for this nature. */ -final case class Nature(label: String) extends Basic { - /** Constructs a Natures matcher that excludes this Nature. */ - override def toString = label -} - -object Natures +object Plugins { - /** Given the available auto plugins `defined`, returns a function that selects [[AutoPlugin]]s for the provided [[Nature]]s. + /** Given the available auto plugins `defined`, returns a function that selects [[AutoPlugin]]s for the provided [[AutoPlugin]]s. * The [[AutoPlugin]]s are topologically sorted so that a selected [[AutoPlugin]] comes before its selecting [[AutoPlugin]].*/ - def compile(defined: List[AutoPlugin]): Natures => Seq[AutoPlugin] = + def compile(defined: List[AutoPlugin]): Plugins => Seq[AutoPlugin] = if(defined.isEmpty) Types.const(Nil) else @@ -112,8 +105,8 @@ object Natures val byAtomMap = byAtom.toMap if(byAtom.size != byAtomMap.size) duplicateProvidesError(byAtom) val clauses = Clauses( defined.map(d => asClause(d)) ) - requestedNatures => - Logic.reduce(clauses, flattenConvert(requestedNatures).toSet) match { + requestedPlugins => + Logic.reduce(clauses, flattenConvert(requestedPlugins).toSet) match { case Left(problem) => throw AutoPluginException(problem) case Right(results) => // results includes the originally requested (positive) atoms, @@ -123,8 +116,8 @@ object Natures } private[sbt] def translateMessage(e: LogicException) = e match { - case ic: InitialContradictions => s"Contradiction in selected natures. These natures were both included and excluded: ${literalsString(ic.literals.toSeq)}" - case io: InitialOverlap => s"Cannot directly enable plugins. Plugins are enabled when their required natures are satisifed. The directly selected plugins were: ${literalsString(io.literals.toSeq)}" + case ic: InitialContradictions => s"Contradiction in selected plugins. These plguins were both included and excluded: ${literalsString(ic.literals.toSeq)}" + case io: InitialOverlap => s"Cannot directly enable plugins. Plugins are enabled when their required plugins are satisifed. The directly selected plugins were: ${literalsString(io.literals.toSeq)}" case cn: CyclicNegation => s"Cycles in plugin requirements cannot involve excludes. The problematic cycle is: ${literalsString(cn.cycle)}" } private[this] def literalsString(lits: Seq[Literal]): String = @@ -135,34 +128,36 @@ object Natures val dupStrings = for( (atom, dups) <- dupsByAtom if dups.size > 1 ) yield s"${atom.label} by ${dups.mkString(", ")}" val (ns, nl) = if(dupStrings.size > 1) ("s", "\n\t") else ("", " ") - val message = s"Nature$ns provided by multiple AutoPlugins:$nl${dupStrings.mkString(nl)}" + val message = s"Plugin$ns provided by multiple AutoPlugins:$nl${dupStrings.mkString(nl)}" throw AutoPluginException(message) } - /** [[Natures]] instance that doesn't require any [[Nature]]s. */ - def empty: Natures = Empty - private[sbt] final object Empty extends Natures { - def &&(o: Basic): Natures = o + /** [[Plugins]] instance that doesn't require any [[Plugins]]s. */ + def empty: Plugins = Empty + private[sbt] final object Empty extends Plugins { + def &&(o: Basic): Plugins = o override def toString = "" } - /** An included or excluded Nature. TODO: better name than Basic. */ - sealed abstract class Basic extends Natures { - def &&(o: Basic): Natures = And(this :: o :: Nil) + /** An included or excluded Nature/Plugin. TODO: better name than Basic. Also, can we dump + * this class. + */ + sealed abstract class Basic extends Plugins { + def &&(o: Basic): Plugins = And(this :: o :: Nil) } private[sbt] final case class Exclude(n: AutoPlugin) extends Basic { override def toString = s"!$n" } - private[sbt] final case class And(natures: List[Basic]) extends Natures { - def &&(o: Basic): Natures = And(o :: natures) - override def toString = natures.mkString(", ") + private[sbt] final case class And(plugins: List[Basic]) extends Plugins { + def &&(o: Basic): Plugins = And(o :: plugins) + override def toString = plugins.mkString(", ") } - private[sbt] def and(a: Natures, b: Natures) = b match { + private[sbt] def and(a: Plugins, b: Plugins) = b match { case Empty => a case And(ns) => (a /: ns)(_ && _) case b: Basic => a && b } - private[sbt] def remove(a: Natures, del: Set[Basic]): Natures = a match { + private[sbt] def remove(a: Plugins, del: Set[Basic]): Plugins = a match { case b: Basic => if(del(b)) Empty else b case Empty => Empty case And(ns) => @@ -170,38 +165,36 @@ object Natures if(removed.isEmpty) Empty else And(removed) } - /** Defines a clause for `ap` such that the [[Nature]] provided by `ap` is the head and the selector for `ap` is the body. */ + /** Defines a clause for `ap` such that the [[AutPlugin]] provided by `ap` is the head and the selector for `ap` is the body. */ private[sbt] def asClause(ap: AutoPlugin): Clause = Clause( convert(ap.select), Set(Atom(ap.label)) ) - private[this] def flattenConvert(n: Natures): Seq[Literal] = n match { + private[this] def flattenConvert(n: Plugins): Seq[Literal] = n match { case And(ns) => convertAll(ns) case b: Basic => convertBasic(b) :: Nil case Empty => Nil } - private[sbt] def flatten(n: Natures): Seq[Basic] = n match { + private[sbt] def flatten(n: Plugins): Seq[Basic] = n match { case And(ns) => ns case b: Basic => b :: Nil case Empty => Nil } - private[this] def convert(n: Natures): Formula = n match { + private[this] def convert(n: Plugins): Formula = n match { case And(ns) => convertAll(ns).reduce[Formula](_ && _) case b: Basic => convertBasic(b) case Empty => Formula.True } private[this] def convertBasic(b: Basic): Literal = b match { case Exclude(n) => !convertBasic(n) - case Nature(s) => Atom(s) case a: AutoPlugin => Atom(a.label) } private[this] def convertAll(ns: Seq[Basic]): Seq[Literal] = ns map convertBasic /** True if the select clause `n` is satisifed by `model`. */ - def satisfied(n: Natures, model: Set[AutoPlugin], natures: Set[Nature]): Boolean = + def satisfied(n: Plugins, model: Set[AutoPlugin]): Boolean = flatten(n) forall { case Exclude(a) => !model(a) - case n: Nature => natures(n) case ap: AutoPlugin => model(ap) } } \ No newline at end of file diff --git a/main/src/main/scala/sbt/NaturesDebug.scala b/main/src/main/scala/sbt/PluginsDebug.scala similarity index 82% rename from main/src/main/scala/sbt/NaturesDebug.scala rename to main/src/main/scala/sbt/PluginsDebug.scala index d0e27a9dd..e130b2c8b 100644 --- a/main/src/main/scala/sbt/NaturesDebug.scala +++ b/main/src/main/scala/sbt/PluginsDebug.scala @@ -1,11 +1,11 @@ package sbt import Def.Setting - import Natures._ - import NaturesDebug._ + import Plugins._ + import PluginsDebug._ import java.net.URI -private[sbt] class NaturesDebug(val available: List[AutoPlugin], val nameToKey: Map[String, AttributeKey[_]], val provided: Relation[AutoPlugin, AttributeKey[_]]) +private[sbt] class PluginsDebug(val available: List[AutoPlugin], val nameToKey: Map[String, AttributeKey[_]], val provided: Relation[AutoPlugin, AttributeKey[_]]) { /** The set of [[AutoPlugin]]s that might define a key named `keyName`. * Because plugins can define keys in different scopes, this should only be used as a guideline. */ @@ -79,7 +79,7 @@ private[sbt] class NaturesDebug(val available: List[AutoPlugin], val nameToKey: private[this] def multi(strs: Seq[String]): String = strs.mkString(if(strs.size > 4) "\n\t" else ", ") } -private[sbt] object NaturesDebug +private[sbt] object PluginsDebug { def helpAll(s: State): String = if(Project.isProjectLoaded(s)) @@ -118,8 +118,8 @@ private[sbt] object NaturesDebug def projectForRef(ref: ProjectRef): ResolvedProject = get(Keys.thisProject in ref) val perBuild: Map[URI, Set[AutoPlugin]] = structure.units.mapValues(unit => availableAutoPlugins(unit).toSet) val pluginsThisBuild = perBuild.getOrElse(currentRef.build, Set.empty).toList - lazy val context = Context(currentProject.natures, currentProject.autoPlugins, Natures.compile(pluginsThisBuild), pluginsThisBuild) - lazy val debug = NaturesDebug(context.available) + lazy val context = Context(currentProject.plugins, currentProject.autoPlugins, Plugins.compile(pluginsThisBuild), pluginsThisBuild) + lazy val debug = PluginsDebug(context.available) if(!pluginsThisBuild.contains(plugin)) { val availableInBuilds: List[URI] = perBuild.toList.filter(_._2(plugin)).map(_._1) s"Plugin ${plugin.label} is only available in builds:\n\t${availableInBuilds.mkString("\n\t")}\nSwitch to a project in one of those builds using `project` and rerun this command for more information." @@ -141,20 +141,20 @@ private[sbt] object NaturesDebug } } - /** Precomputes information for debugging natures and plugins. */ - def apply(available: List[AutoPlugin]): NaturesDebug = + /** Precomputes information for debugging plugins. */ + def apply(available: List[AutoPlugin]): PluginsDebug = { val keyR = definedKeys(available) val nameToKey: Map[String, AttributeKey[_]] = keyR._2s.toList.map(key => (key.label, key)).toMap - new NaturesDebug(available, nameToKey, keyR) + new PluginsDebug(available, nameToKey, keyR) } /** The context for debugging a plugin (de)activation. - * @param initial The initially defined [[Nature]]s. + * @param initial The initially defined [[AutoPlugin]]s. * @param enabled The resulting model. * @param compile The function used to compute the model. * @param available All [[AutoPlugin]]s available for consideration. */ - final case class Context(initial: Natures, enabled: Seq[AutoPlugin], compile: Natures => Seq[AutoPlugin], available: List[AutoPlugin]) + final case class Context(initial: Plugins, enabled: Seq[AutoPlugin], compile: Plugins => Seq[AutoPlugin], available: List[AutoPlugin]) /** Describes the steps to activate a plugin in some context. */ sealed abstract class PluginEnable @@ -165,19 +165,19 @@ private[sbt] object NaturesDebug final case class PluginImpossible(plugin: AutoPlugin, context: Context, contradictions: Set[AutoPlugin]) extends EnableDeactivated /** Describes the requirements for activating [[plugin]] in [[context]]. - * @param context The base natures, exclusions, and ultimately activated plugins + * @param context The base plguins, exclusions, and ultimately activated plugins * @param blockingExcludes Existing exclusions that prevent [[plugin]] from being activated and must be dropped - * @param enablingNatures [[Nature]]s that are not currently enabled, but need to be enabled for [[plugin]] to activate + * @param enablingPlguins [[AutoPlugin]]s that are not currently enabled, but need to be enabled for [[plugin]] to activate * @param extraEnabledPlugins Plugins that will be enabled as a result of [[plugin]] activating, but are not required for [[plugin]] to activate * @param willRemove Plugins that will be deactivated as a result of [[plugin]] activating - * @param deactivate Describes plugins that must be deactivated for [[plugin]] to activate. These require an explicit exclusion or dropping a transitive [[Nature]].*/ - final case class PluginRequirements(plugin: AutoPlugin, context: Context, blockingExcludes: Set[AutoPlugin], enablingNatures: Set[Nature], extraEnabledPlugins: Set[AutoPlugin], willRemove: Set[AutoPlugin], deactivate: List[DeactivatePlugin]) extends EnableDeactivated + * @param deactivate Describes plugins that must be deactivated for [[plugin]] to activate. These require an explicit exclusion or dropping a transitive [[AutoPlugin]].*/ + final case class PluginRequirements(plugin: AutoPlugin, context: Context, blockingExcludes: Set[AutoPlugin], enablingPlugins: Set[AutoPlugin], extraEnabledPlugins: Set[AutoPlugin], willRemove: Set[AutoPlugin], deactivate: List[DeactivatePlugin]) extends EnableDeactivated /** Describes a [[plugin]] that must be removed in order to activate another plugin in some context. * The [[plugin]] can always be directly, explicitly excluded. - * @param removeOneOf If non-empty, removing one of these [[Nature]]s will deactivate [[plugin]] without affecting the other plugin. If empty, a direct exclusion is required. + * @param removeOneOf If non-empty, removing one of these [[AutoPlugin]]s will deactivate [[plugin]] without affecting the other plugin. If empty, a direct exclusion is required. * @param newlySelected If false, this plugin was selected in the original context. */ - final case class DeactivatePlugin(plugin: AutoPlugin, removeOneOf: Set[Nature], newlySelected: Boolean) + final case class DeactivatePlugin(plugin: AutoPlugin, removeOneOf: Set[AutoPlugin], newlySelected: Boolean) /** Determines how to enable [[plugin]] in [[context]]. */ def pluginEnable(context: Context, plugin: AutoPlugin): PluginEnable = @@ -191,7 +191,7 @@ private[sbt] object NaturesDebug // deconstruct the context val initialModel = context.enabled.toSet val initial = flatten(context.initial) - val initialNatures = natures(initial) + val initialPlugins = plugins(initial) val initialExcludes = excludes(initial) val minModel = minimalModel(plugin) @@ -212,13 +212,9 @@ private[sbt] object NaturesDebug propose: B, exclude C */ - // `plugin` will only be activated when all of these natures are activated - // Deactivating any one of these would deactivate `plugin`. - val minRequiredNatures = natures(minModel) - // `plugin` will only be activated when all of these plugins are activated // Deactivating any one of these would deactivate `plugin`. - val minRequiredPlugins = minModel.collect{ case a: AutoPlugin => a }.toSet + val minRequiredPlugins = plugins(minModel) // The presence of any one of these plugins would deactivate `plugin` val minAbsentPlugins = excludes(minModel).toSet @@ -231,21 +227,21 @@ private[sbt] object NaturesDebug PluginImpossible(plugin, context, contradictions) else { - // Natures that the user has to add to the currently selected natures in order to enable `plugin`. - val addToExistingNatures = minRequiredNatures -- initialNatures + // Plguins that the user has to add to the currently selected plugins in order to enable `plugin`. + val addToExistingPlugins = minRequiredPlugins -- initialPlugins // Plugins that are currently excluded that need to be allowed. val blockingExcludes = initialExcludes & minRequiredPlugins - // The model that results when the minimal natures are enabled and the minimal plugins are excluded. - // This can include more plugins than just `minRequiredPlugins` because the natures required for `plugin` + // The model that results when the minimal plugins are enabled and the minimal plugins are excluded. + // This can include more plugins than just `minRequiredPlugins` because the plguins required for `plugin` // might activate other plugins as well. - val modelForMin = context.compile(and(includeAll(minRequiredNatures), excludeAll(minAbsentPlugins))) + val modelForMin = context.compile(and(includeAll(minRequiredPlugins), excludeAll(minAbsentPlugins))) - val incrementalInputs = and( includeAll(minRequiredNatures ++ initialNatures), excludeAll(minAbsentPlugins ++ initialExcludes -- minRequiredPlugins)) + val incrementalInputs = and( includeAll(minRequiredPlugins ++ initialPlugins), excludeAll(minAbsentPlugins ++ initialExcludes -- minRequiredPlugins)) val incrementalModel = context.compile(incrementalInputs).toSet - // Plugins that are newly enabled as a result of selecting the natures needed for `plugin`, but aren't strictly required for `plugin`. + // Plugins that are newly enabled as a result of selecting the plugins needed for `plugin`, but aren't strictly required for `plugin`. // These could be excluded and `plugin` and the user's current plugins would still be activated. val extraPlugins = incrementalModel.toSet -- minRequiredPlugins -- initialModel @@ -254,48 +250,48 @@ private[sbt] object NaturesDebug // Determine the plugins that must be independently deactivated. // If both A and B must be deactivated, but A transitively depends on B, deactivating B will deactivate A. - // If A must be deactivated, but one if its (transitively) required natures isn't present, it won't be activated. + // If A must be deactivated, but one if its (transitively) required plugins isn't present, it won't be activated. // So, in either of these cases, A doesn't need to be considered further and won't be included in this set. - val minDeactivate = minAbsentPlugins.filter(p => Natures.satisfied(p.select, incrementalModel, natures(flatten(incrementalInputs)))) + val minDeactivate = minAbsentPlugins.filter(p => Plugins.satisfied(p.select, incrementalModel)) val deactivate = for(d <- minDeactivate.toList) yield { - // removing any one of these natures will deactivate `d`. TODO: This is not an especially efficient implementation. - val removeToDeactivate = natures(minimalModel(d)) -- minRequiredNatures + // removing any one of these plugins will deactivate `d`. TODO: This is not an especially efficient implementation. + val removeToDeactivate = plugins(minimalModel(d)) -- minRequiredPlugins val newlySelected = !initialModel(d) - // a. suggest removing a nature in removeOneToDeactivate to deactivate d + // a. suggest removing a plugin in removeOneToDeactivate to deactivate d // b. suggest excluding `d` to directly deactivate it in any case // c. note whether d was already activated (in context.enabled) or is newly selected DeactivatePlugin(d, removeToDeactivate, newlySelected) } - PluginRequirements(plugin, context, blockingExcludes, addToExistingNatures, extraPlugins, willRemove, deactivate) + PluginRequirements(plugin, context, blockingExcludes, addToExistingPlugins, extraPlugins, willRemove, deactivate) } } - private[this] def includeAll[T <: Basic](basic: Set[T]): Natures = And(basic.toList) - private[this] def excludeAll(plugins: Set[AutoPlugin]): Natures = And(plugins map (p => Exclude(p)) toList) + private[this] def includeAll[T <: Basic](basic: Set[T]): Plugins = And(basic.toList) + private[this] def excludeAll(plugins: Set[AutoPlugin]): Plugins = And(plugins map (p => Exclude(p)) toList) private[this] def excludes(bs: Seq[Basic]): Set[AutoPlugin] = bs.collect { case Exclude(b) => b }.toSet - private[this] def natures(bs: Seq[Basic]): Set[Nature] = bs.collect { case n: Nature => n }.toSet + private[this] def plugins(bs: Seq[Basic]): Set[AutoPlugin] = bs.collect { case n: AutoPlugin => n }.toSet // If there is a model that includes `plugin`, it includes at least what is returned by this method. - // This is the list of natures and plugins that must be included as well as list of plugins that must not be present. + // This is the list of plugins that must be included as well as list of plugins that must not be present. // It might not be valid, such as if there are contradictions or if there are cycles that are unsatisfiable. - // The actual model might be larger, since other plugins might be enabled by the selected natures. + // The actual model might be larger, since other plugins might be enabled by the selected plugins. private[this] def minimalModel(plugin: AutoPlugin): Seq[Basic] = Dag.topologicalSortUnchecked(plugin: Basic) { - case _: Exclude | _: Nature => Nil - case ap: AutoPlugin => Natures.flatten(ap.select) + case _: Exclude => Nil + case ap: AutoPlugin => Plugins.flatten(ap.select) } /** String representation of [[PluginEnable]], intended for end users. */ def explainPluginEnable(ps: PluginEnable): String = ps match { - case PluginRequirements(plugin, context, blockingExcludes, enablingNatures, extraEnabledPlugins, toBeRemoved, deactivate) => + case PluginRequirements(plugin, context, blockingExcludes, enablingPlugins, extraEnabledPlugins, toBeRemoved, deactivate) => def indent(str: String) = if(str.isEmpty) "" else s"\t$str" def note(str: String) = if(str.isEmpty) "" else s"Note: $str" val parts = indent(excludedError(false /* TODO */, blockingExcludes.toList)) :: - indent(required(enablingNatures.toList)) :: + indent(required(enablingPlugins.toList)) :: indent(needToDeactivate(deactivate)) :: note(willAdd(plugin, extraEnabledPlugins.toList)) :: note(willRemove(plugin, toBeRemoved.toList)) :: @@ -326,13 +322,13 @@ private[sbt] object NaturesDebug private[this] def transitiveString(transitive: Boolean) = if(transitive) "(transitive) " else "" - private[this] def required(natures: List[Nature]): String = - str(natures)(requiredNature, requiredNatures) + private[this] def required(plugins: List[AutoPlugin]): String = + str(plugins)(requiredPlugin, requiredPlugins) - private[this] def requiredNature(nature: Nature) = - s"Required nature ${nature.label} not present." - private[this] def requiredNatures(natures: List[Nature]) = - s"Required natures not present:\n\t${natures.map(_.label).mkString("\n\t")}" + private[this] def requiredPlugin(plugin: AutoPlugin) = + s"Required plugin ${plugin.label} not present." + private[this] def requiredPlugins(plugins: List[AutoPlugin]) = + s"Required plugins not present:\n\t${plugins.map(_.label).mkString("\n\t")}" private[this] def str[A](list: List[A])(f: A => String, fs: List[A] => String): String = list match { case Nil => "" @@ -367,13 +363,13 @@ private[sbt] object NaturesDebug s"Need to deactivate ${deactivateString(deactivate)}" private[this] def deactivateString(d: DeactivatePlugin): String = { - val removeNaturesString: String = + val removePluginsString: String = d.removeOneOf.toList match { case Nil => "" case x :: Nil => s" or no longer include $x" case xs => s" or remove one of ${xs.mkString(", ")}" } - s"${d.plugin.label}: directly exclude it${removeNaturesString}" + s"${d.plugin.label}: directly exclude it${removePluginsString}" } private[this] def pluginImpossible(plugin: AutoPlugin, contradictions: Set[AutoPlugin]): String = diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 647013bed..a44598e94 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -50,9 +50,9 @@ sealed trait ProjectDefinition[PR <: ProjectReference] /** Configures the sources of automatically appended settings.*/ def auto: AddSettings - /** The [[Natures]] associated with this project. - A [[Nature]] is a common label that is used by plugins to determine what settings, if any, to add to a project. */ - def natures: Natures + /** The defined [[Plugins]] associated with this project. + A [[AutoPlguin]] is a common label that is used by plugins to determine what settings, if any, to add to a project. */ + def plugins: Plugins /** The [[AutoPlugin]]s enabled for this project. This value is only available on a loaded Project. */ private[sbt] def autoPlugins: Seq[AutoPlugin] @@ -68,18 +68,18 @@ sealed trait ProjectDefinition[PR <: ProjectReference] val dep = ifNonEmpty("dependencies", dependencies) val conf = ifNonEmpty("configurations", configurations) val autos = ifNonEmpty("autoPlugins", autoPlugins.map(_.label)) - val fields = s"id $id" :: s"base: $base" :: agg ::: dep ::: conf ::: (s"natures: List($natures)" :: autos) + val fields = s"id $id" :: s"base: $base" :: agg ::: dep ::: conf ::: (s"plugins: List($plugins)" :: autos) s"Project(${fields.mkString(", ")})" } private[this] def ifNonEmpty[T](label: String, ts: Iterable[T]): List[String] = if(ts.isEmpty) Nil else s"$label: $ts" :: Nil } sealed trait Project extends ProjectDefinition[ProjectReference] { - // TODO: add parameters for natures and autoPlugins in 0.14.0 (not reasonable to do in a binary compatible way in 0.13) + // TODO: add parameters for plugins in 0.14.0 (not reasonable to do in a binary compatible way in 0.13) def copy(id: String = id, base: File = base, aggregate: => Seq[ProjectReference] = aggregate, dependencies: => Seq[ClasspathDep[ProjectReference]] = dependencies, delegates: => Seq[ProjectReference] = delegates, settings: => Seq[Setting[_]] = settings, configurations: Seq[Configuration] = configurations, auto: AddSettings = auto): Project = - unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, natures, autoPlugins) + unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, plugins, autoPlugins) def resolve(resolveRef: ProjectReference => ProjectRef): ResolvedProject = { @@ -87,7 +87,7 @@ sealed trait Project extends ProjectDefinition[ProjectReference] def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep def resolveDep(d: ClasspathDep[ProjectReference]) = ResolvedClasspathDependency(resolveRef(d.project), d.configuration) resolved(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates), - settings, configurations, auto, natures, autoPlugins) + settings, configurations, auto, plugins, autoPlugins) } def resolveBuild(resolveRef: ProjectReference => ProjectReference): Project = { @@ -95,7 +95,7 @@ sealed trait Project extends ProjectDefinition[ProjectReference] def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep def resolveDep(d: ClasspathDep[ProjectReference]) = ClasspathDependency(resolveRef(d.project), d.configuration) unresolved(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates), - settings, configurations, auto, natures, autoPlugins) + settings, configurations, auto, plugins, autoPlugins) } /** Applies the given functions to this Project. @@ -136,27 +136,27 @@ sealed trait Project extends ProjectDefinition[ProjectReference] * Any configured .sbt files are removed from this project's list.*/ def setSbtFiles(files: File*): Project = copy(auto = AddSettings.append( AddSettings.clearSbtFiles(auto), AddSettings.sbtFiles(files: _*)) ) - /** Sets the [[Nature]]s of this project. - A [[Nature]] is a common label that is used by plugins to determine what settings, if any, to add to a project. */ - def addNatures(ns: Nature*): Project = setNatures(Natures.and(natures, Natures.And(ns.toList))) + /** Sets the [[AutoPlugin]]s of this project. + A [[AutoPlugin]] is a common label that is used by plugins to determine what settings, if any, to add to a project. */ + def addPlugins(ns: AutoPlugin*): Project = setPlugins(Plugins.and(plugins, Plugins.And(ns.toList))) /** Disable the given plugins on this project. */ - def disablePlugins(plugins: AutoPlugin*): Project = - setNatures(Natures.and(natures, Natures.And(plugins.map(p => Natures.Exclude(p)).toList))) + def disablePlugins(ps: AutoPlugin*): Project = + setPlugins(Plugins.and(plugins, Plugins.And(ps.map(p => Plugins.Exclude(p)).toList))) - private[this] def setNatures(ns: Natures): Project = { - // TODO: for 0.14.0, use copy when it has the additional `natures` parameter + private[this] def setPlugins(ns: Plugins): Project = { + // TODO: for 0.14.0, use copy when it has the additional `plugins` parameter unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, ns, autoPlugins) } /** Definitively set the [[AutoPlugin]]s for this project. */ private[sbt] def setAutoPlugins(autos: Seq[AutoPlugin]): Project = { // TODO: for 0.14.0, use copy when it has the additional `autoPlugins` parameter - unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, natures, autos) + unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, plugins, autos) } } sealed trait ResolvedProject extends ProjectDefinition[ProjectRef] { - /** The [[AutoPlugin]]s enabled for this project as computed from [[natures]].*/ + /** The [[AutoPlugin]]s enabled for this project as computed from [[plugins]].*/ def autoPlugins: Seq[AutoPlugin] } @@ -192,7 +192,7 @@ object Project extends ProjectExtra private abstract class ProjectDef[PR <: ProjectReference](val id: String, val base: File, aggregate0: => Seq[PR], dependencies0: => Seq[ClasspathDep[PR]], delegates0: => Seq[PR], settings0: => Seq[Def.Setting[_]], val configurations: Seq[Configuration], val auto: AddSettings, - val natures: Natures, val autoPlugins: Seq[AutoPlugin]) extends ProjectDefinition[PR] + val plugins: Plugins, val autoPlugins: Seq[AutoPlugin]) extends ProjectDefinition[PR] { lazy val aggregate = aggregate0 lazy val dependencies = dependencies0 @@ -202,11 +202,11 @@ object Project extends ProjectExtra Dag.topologicalSort(configurations)(_.extendsConfigs) // checks for cyclic references here instead of having to do it in Scope.delegates } - // TODO: add parameter for natures in 0.14.0 + // TODO: add parameter for plugins in 0.14.0 def apply(id: String, base: File, aggregate: => Seq[ProjectReference] = Nil, dependencies: => Seq[ClasspathDep[ProjectReference]] = Nil, delegates: => Seq[ProjectReference] = Nil, settings: => Seq[Def.Setting[_]] = defaultSettings, configurations: Seq[Configuration] = Configurations.default, auto: AddSettings = AddSettings.allDefaults): Project = - unresolved(id, base, aggregate, dependencies, delegates, settings, configurations, auto, Natures.empty, Nil) + unresolved(id, base, aggregate, dependencies, delegates, settings, configurations, auto, Plugins.empty, Nil) /** 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 @@ -228,19 +228,19 @@ object Project extends ProjectExtra @deprecated("Will be removed.", "0.13.2") 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 = - resolved(id, base, aggregate, dependencies, delegates, settings, configurations, auto, Natures.empty, Nil) + resolved(id, base, aggregate, dependencies, delegates, settings, configurations, auto, Plugins.empty, Nil) private def resolved(id: String, base: File, aggregate: => Seq[ProjectRef], dependencies: => Seq[ClasspathDep[ProjectRef]], delegates: => Seq[ProjectRef], settings: Seq[Def.Setting[_]], configurations: Seq[Configuration], auto: AddSettings, - natures: Natures, autoPlugins: Seq[AutoPlugin]): ResolvedProject = - new ProjectDef[ProjectRef](id, base, aggregate, dependencies, delegates, settings, configurations, auto, natures, autoPlugins) with ResolvedProject + plugins: Plugins, autoPlugins: Seq[AutoPlugin]): ResolvedProject = + new ProjectDef[ProjectRef](id, base, aggregate, dependencies, delegates, settings, configurations, auto, plugins, autoPlugins) with ResolvedProject private def unresolved(id: String, base: File, aggregate: => Seq[ProjectReference], dependencies: => Seq[ClasspathDep[ProjectReference]], delegates: => Seq[ProjectReference], settings: => Seq[Def.Setting[_]], configurations: Seq[Configuration], auto: AddSettings, - natures: Natures, autoPlugins: Seq[AutoPlugin]): Project = + plugins: Plugins, autoPlugins: Seq[AutoPlugin]): Project = { validProjectID(id).foreach(errMsg => sys.error("Invalid project ID: " + errMsg)) - new ProjectDef[ProjectReference](id, base, aggregate, dependencies, delegates, settings, configurations, auto, natures, autoPlugins) with Project + new ProjectDef[ProjectReference](id, base, aggregate, dependencies, delegates, settings, configurations, auto, plugins, autoPlugins) with Project } def defaultSettings: Seq[Def.Setting[_]] = Defaults.defaultSettings diff --git a/sbt/src/sbt-test/project/auto-plugins/build.sbt b/sbt/src/sbt-test/project/auto-plugins/build.sbt index f48a1f0e5..57e1394c8 100644 --- a/sbt/src/sbt-test/project/auto-plugins/build.sbt +++ b/sbt/src/sbt-test/project/auto-plugins/build.sbt @@ -1,11 +1,11 @@ // excludePlugins(C) will prevent C, and thus D, from being auto-added -lazy val a = project.addNatures(A, B).disablePlugins(Q) +lazy val a = project.addPlugins(A, B).disablePlugins(Q) // without B, C is not added -lazy val b = project.addNatures(A) +lazy val b = project.addPlugins(A) // with both A and B, C is selected, which in turn selects D -lazy val c = project.addNatures(A, B) +lazy val c = project.addPlugins(A, B) // with no natures defined, nothing is auto-added lazy val d = project diff --git a/sbt/src/sbt-test/project/auto-plugins/project/Q.scala b/sbt/src/sbt-test/project/auto-plugins/project/Q.scala index e092e0fd1..c6dea7ba8 100644 --- a/sbt/src/sbt-test/project/auto-plugins/project/Q.scala +++ b/sbt/src/sbt-test/project/auto-plugins/project/Q.scala @@ -4,9 +4,12 @@ object AI extends AutoImport { - lazy val A = Nature("A") - lazy val B = Nature("B") - lazy val E = Nature("E") + trait EmptyAutoPlugin extends AutoPlugin { + def select = Plugins.empty + } + object A extends EmptyAutoPlugin + object B extends EmptyAutoPlugin + object E extends EmptyAutoPlugin lazy val q = config("q") lazy val p = config("p").extend(q) @@ -20,12 +23,12 @@ object AI extends AutoImport import AI._ object D extends AutoPlugin { - def select: Natures = E + def select: Plugins = E } object Q extends AutoPlugin { - def select: Natures = A && B + def select: Plugins = A && B override def projectConfigurations: Seq[Configuration] = p :: diff --git a/sbt/src/sbt-test/project/binary-plugin/changes/define/A.scala b/sbt/src/sbt-test/project/binary-plugin/changes/define/A.scala index c38558d4f..a9f71c928 100644 --- a/sbt/src/sbt-test/project/binary-plugin/changes/define/A.scala +++ b/sbt/src/sbt-test/project/binary-plugin/changes/define/A.scala @@ -3,7 +3,9 @@ import Keys._ object C extends AutoImport { - lazy val bN = Nature("B") + object bN extends AutoPlugin { + def select = Plugins.empty + } lazy val check = taskKey[Unit]("Checks that the AutoPlugin and Build are automatically added.") } @@ -17,5 +19,5 @@ object A extends AutoPlugin { } object B extends Build { - lazy val extra = project.addNatures(bN) + lazy val extra = project.addPlugins(bN) }