mirror of https://github.com/sbt/sbt.git
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.
This commit is contained in:
parent
75282195f4
commit
7f8d21c2f1
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
||||
<Project>.natures( Web && Javascript )
|
||||
<Project>.plugins( Web && Javascript )
|
||||
|
||||
will activate `MyPlugin` defined above and have its settings automatically added. If the user instead defines
|
||||
|
||||
<Project>.natures( Web && Javascript && !MyPlugin)
|
||||
<Project>.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 = "<none>"
|
||||
}
|
||||
|
||||
/** 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 =
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ::
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue