Merge pull request #1516 from sbt/wip/fix-1455

Allow root plugins to be disabled.
This commit is contained in:
eugene yokota 2014-08-12 11:12:18 -04:00
commit 3d59b18aef
4 changed files with 585 additions and 569 deletions

View File

@ -50,8 +50,7 @@ will activate `MyPlugin` defined above and have its settings automatically added
then the `MyPlugin` settings (and anything that activates only when `MyPlugin` is activated) will not be added.
*/
abstract class AutoPlugin extends Plugins.Basic with PluginsFunctions
{
abstract class AutoPlugin extends Plugins.Basic with PluginsFunctions {
/** Determines whether this AutoPlugin will be activated for this project when the `requires` clause is satisfied.
*
* When this method returns `allRequirements`, and `requires` method returns `Web && Javascript`, this plugin
@ -102,13 +101,11 @@ abstract class AutoPlugin extends Plugins.Basic with PluginsFunctions
/** An error that occurs when auto-plugins aren't configured properly.
* It translates the error from the underlying logic system to be targeted at end users. */
final class AutoPluginException private(val message: String, val origin: Option[LogicException]) extends RuntimeException(message)
{
final class AutoPluginException private(val message: String, val origin: Option[LogicException]) extends RuntimeException(message) {
/** Prepends `p` to the error message derived from `origin`. */
def withPrefix(p: String) = new AutoPluginException(p + message, origin)
}
object AutoPluginException
{
object AutoPluginException {
def apply(msg: String): AutoPluginException = new AutoPluginException(msg, None)
def apply(origin: LogicException): AutoPluginException = new AutoPluginException(Plugins.translateMessage(origin), Some(origin))
}
@ -123,8 +120,7 @@ sealed trait Plugins {
}
sealed trait PluginsFunctions
{
sealed trait PluginsFunctions {
/** [[Plugins]] instance that doesn't require any [[Plugins]]s. */
def empty: Plugins = Plugins.Empty
@ -134,14 +130,12 @@ sealed trait PluginsFunctions
def noTrigger: PluginTrigger = NoTrigger
}
object Plugins extends PluginsFunctions
{
object Plugins extends PluginsFunctions {
/** 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 required [[AutoPlugin]] comes before its requiring [[AutoPlugin]].*/
def deducer(defined0: List[AutoPlugin]): (Plugins, Logger) => Seq[AutoPlugin] =
if(defined0.isEmpty) (_, _) => Nil
else
{
else {
// TODO: defined should return all the plugins
val allReqs = (defined0 flatMap { asRequirements }).toSet
val diff = allReqs diff defined0.toSet
@ -156,8 +150,11 @@ object Plugins extends PluginsFunctions
// circular dependencies in the logic.
val allRequirementsClause = defined.filterNot(_.isRoot).flatMap(d => asRequirementsClauses(d))
val allEnabledByClause = defined.filterNot(_.isRoot).flatMap(d => asEnabledByClauses(d))
// Note: Here is where the function begins. We're given a list of plugins now.
(requestedPlugins, log) => {
val alwaysEnabled: List[AutoPlugin] = defined.filter(_.isAlwaysEnabled)
def explicitlyDisabled(p: AutoPlugin): Boolean = hasExclude(requestedPlugins, p)
val alwaysEnabled: List[AutoPlugin] = defined.filter(_.isAlwaysEnabled).filterNot(explicitlyDisabled)
val knowlege0: Set[Atom] = ((flatten(requestedPlugins) ++ alwaysEnabled) collect {
case x: AutoPlugin => Atom(x.label)
}).toSet
@ -282,6 +279,25 @@ ${listConflicts(conflicting)}""")
private[sbt] def asExclusions(ap: AutoPlugin): List[AutoPlugin] = flatten(ap.requires).toList collect {
case Exclude(x) => x
}
// TODO - This doesn't handle nested AND boolean logic...
private[sbt] def hasExclude(n: Plugins, p: AutoPlugin): Boolean = n match {
case `p` => false
case Exclude(`p`) => true
// TODO - This is stupidly advanced. We do a nested check through possible and-ed
// lists of plugins exclusions to see if the plugin ever winds up in an excluded=true case.
// This would handle things like !!p or !(p && z)
case Exclude(n) => hasInclude(n, p)
case And(ns) => ns.forall(n => hasExclude(n, p))
case b: Basic => false
case Empty => false
}
private[sbt] def hasInclude(n: Plugins, p: AutoPlugin): Boolean = n match {
case `p` => true
case Exclude(n) => hasExclude(n, p)
case And(ns) => ns.forall(n => hasInclude(n, p))
case b: Basic => false
case Empty => false
}
private[this] def flattenConvert(n: Plugins): Seq[Literal] = n match {
case And(ns) => convertAll(ns)
case b: Basic => convertBasic(b) :: Nil

View File

@ -5,8 +5,7 @@ package sbt
import PluginsDebug._
import java.net.URI
private[sbt] class PluginsDebug(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. */
def providers(keyName: String): Set[AutoPlugin] = nameToKey.get(keyName) match {
@ -52,10 +51,8 @@ private[sbt] class PluginsDebug(val available: List[AutoPlugin], val nameToKey:
/** Text that suggests how to activate [[plugin]] in [[context]] if possible and if it is not already activated.*/
def help(plugin: AutoPlugin, context: Context): String =
if(context.enabled.contains(plugin))
activatedHelp(plugin)
else
deactivatedHelp(plugin, context)
if (context.enabled.contains(plugin)) activatedHelp(plugin)
else deactivatedHelp(plugin, context)
private def activatedHelp(plugin: AutoPlugin): String =
{
val prefix = s"${plugin.label} is activated."
@ -79,8 +76,7 @@ private[sbt] class PluginsDebug(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 PluginsDebug
{
private[sbt] object PluginsDebug {
def helpAll(s: State): String =
if(Project.isProjectLoaded(s))
{
@ -98,8 +94,7 @@ private[sbt] object PluginsDebug
val buildStrings = for((uri, build) <- structure.units) yield helpBuild(uri, build)
buildStrings.mkString("\n")
}
else
"No project is currently loaded."
else "No project is currently loaded."
def autoPluginMap(s: State): Map[String, AutoPlugin] =
{
@ -186,8 +181,7 @@ private[sbt] object PluginsDebug
else
enableDeactivated(context, plugin)
private[this] def enableDeactivated(context: Context, plugin: AutoPlugin): PluginEnable =
{
private[this] def enableDeactivated(context: Context, plugin: AutoPlugin): PluginEnable = {
// deconstruct the context
val initialModel = context.enabled.toSet
val initial = flatten(context.initial)
@ -223,10 +217,8 @@ private[sbt] object PluginsDebug
// A non-empty list here cannot be satisfied and is an error.
val contradictions = minAbsentPlugins & minRequiredPlugins
if(contradictions.nonEmpty)
PluginImpossible(plugin, context, contradictions)
else
{
if(contradictions.nonEmpty) PluginImpossible(plugin, context, contradictions)
else {
// Plguins that the user has to add to the currently selected plugins in order to enable `plugin`.
val addToExistingPlugins = minRequiredPlugins -- initialPlugins
@ -330,7 +322,8 @@ private[sbt] object PluginsDebug
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 {
private[this] def str[A](list: List[A])(f: A => String, fs: List[A] => String): String =
list match {
case Nil => ""
case single :: Nil => f(single)
case _ => fs(list)

View File

@ -44,6 +44,7 @@
[1488]: https://github.com/sbt/sbt/pull/1488
[1489]: https://github.com/sbt/sbt/pull/1489
[1494]: https://github.com/sbt/sbt/pull/1494
[1516]: https://github.com/sbt/sbt/pull/1516
[@dansanduleac]: https://github.com/dansanduleac
[@2m]: https://github.com/2m
@ -87,6 +88,7 @@
- Allows keys defined inside `build.sbt` to be used from sbt shell. [#1059][1059]/[#1456][1456]
- Updates internal Ivy instance to cache the results of dependency exclusion rules. [#1476][1476] by [@eed3si9n][@eed3si9n]
- Adds `Resolver.jcenterRepo` and `Resolver.bintrayRepo(owner, repo)` to add Bintray easier. [#1405][1405] by [@evgeny-goldin][@evgeny-goldin]
- AutoPlugins with no requirements enabled by allRequirements can now be disable dby the user. [#1516][1516] by [@jsuereth]
### Bug fixes
@ -103,6 +105,7 @@
- Fixes `Scope.parseScopedKey`. [#1384][1384] by [@eed3si9n][@eed3si9n]
- Fixes `build.sbt` errors causing `ArrayIndexOutOfBoundsException` due to invalid source in position. [#1181][1181] by [@eed3si9n][@eed3si9n]
### Maven Central Repository defaults to HTTPS
Thanks to Sonatype, HTTPS access to Maven Central Repository is available to public. This is now enabled by default, but if HTTP is required for some reason the following system properties can be used:

View File

@ -25,6 +25,10 @@ lazy val projH = project.enablePlugins(TopB)
lazy val projI = project.enablePlugins(TopC)
// Tests that we can disable an auto-enabled root plugin
lazy val disableAutoNoRequirePlugin = project.disablePlugins(OrgPlugin)
disablePlugins(plugins.IvyPlugin)
check := {