mirror of https://github.com/sbt/sbt.git
Added conflict report and unit tests
This commit is contained in:
parent
f43daecee3
commit
5b1c33dd6e
|
|
@ -64,6 +64,8 @@ abstract class AutoPlugin extends Plugins.Basic with PluginsFunctions
|
|||
|
||||
val label: String = getClass.getName.stripSuffix("$")
|
||||
|
||||
override def toString: String = label
|
||||
|
||||
/** The [[Configuration]]s to add to each project that activates this AutoPlugin.*/
|
||||
def projectConfigurations: Seq[Configuration] = Nil
|
||||
|
||||
|
|
@ -169,8 +171,7 @@ object Plugins extends PluginsFunctions
|
|||
val forbidden: Set[AutoPlugin] = (selectedPlugins flatMap { Plugins.asExclusions }).toSet
|
||||
val c = selectedPlugins.toSet & forbidden
|
||||
if (!c.isEmpty) {
|
||||
val listString = (c map {_.label}).mkString(", ")
|
||||
throw AutoPluginException(s"Contradiction in selected plugins. These plguins were both included and excluded: ${listString}")
|
||||
exlusionConflictError(requestedPlugins, selectedPlugins, c.toSeq sortBy {_.label})
|
||||
}
|
||||
val retval = topologicalSort(selectedPlugins, log)
|
||||
log.debug(s" :: sorted deduced result: ${retval.toString}")
|
||||
|
|
@ -193,7 +194,7 @@ object Plugins extends PluginsFunctions
|
|||
doSort(roots, nonRoots, ns.size * ns.size + 1)
|
||||
}
|
||||
private[sbt] def translateMessage(e: LogicException) = e match {
|
||||
case ic: InitialContradictions => s"Contradiction in selected plugins. These plguins were both included and excluded: ${literalsString(ic.literals.toSeq)}"
|
||||
case ic: InitialContradictions => s"Contradiction in selected plugins. These plugins 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)}"
|
||||
}
|
||||
|
|
@ -208,6 +209,29 @@ object Plugins extends PluginsFunctions
|
|||
val message = s"Plugin$ns provided by multiple AutoPlugins:$nl${dupStrings.mkString(nl)}"
|
||||
throw AutoPluginException(message)
|
||||
}
|
||||
private[this] def exlusionConflictError(requested: Plugins, selected: Seq[AutoPlugin], conflicting: Seq[AutoPlugin]) {
|
||||
def listConflicts(ns: Seq[AutoPlugin]) = (ns map { c =>
|
||||
val reasons = (if (flatten(requested) contains c) List("requested")
|
||||
else Nil) ++
|
||||
(if (c.requires != empty && c.trigger == allRequirements) List(s"enabled by ${c.requires.toString}")
|
||||
else Nil) ++
|
||||
{
|
||||
val reqs = selected filter { x => asRequirements(x) contains c }
|
||||
if (!reqs.isEmpty) List(s"""required by ${reqs.mkString(", ")}""")
|
||||
else Nil
|
||||
} ++
|
||||
{
|
||||
val exs = selected filter { x => asExclusions(x) contains c }
|
||||
if (!exs.isEmpty) List(s"""excluded by ${exs.mkString(", ")}""")
|
||||
else Nil
|
||||
}
|
||||
s""" - conflict: ${c.label} is ${reasons.mkString("; ")}"""
|
||||
}).mkString("\n")
|
||||
throw AutoPluginException(s"""Contradiction in enabled plugins:
|
||||
- requested: ${requested.toString}
|
||||
- enabled: ${selected.mkString(", ")}
|
||||
${listConflicts(conflicting)}""")
|
||||
}
|
||||
|
||||
private[sbt] final object Empty extends Plugins {
|
||||
def &&(o: Basic): Plugins = o
|
||||
|
|
@ -225,7 +249,7 @@ object Plugins extends PluginsFunctions
|
|||
}
|
||||
private[sbt] final case class And(plugins: List[Basic]) extends Plugins {
|
||||
def &&(o: Basic): Plugins = And(o :: plugins)
|
||||
override def toString = plugins.mkString(", ")
|
||||
override def toString = plugins.mkString(" && ")
|
||||
}
|
||||
private[sbt] def and(a: Plugins, b: Plugins) = b match {
|
||||
case Empty => a
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import org.specs2._
|
||||
import mutable.Specification
|
||||
|
||||
object PluginsTest extends Specification
|
||||
{
|
||||
import AI._
|
||||
|
||||
"Auto plugin" should {
|
||||
"enable plugins with trigger=allRequirements AND requirements met" in {
|
||||
deducePlugin(A && B, log) must contain(Q)
|
||||
}
|
||||
"enable transive plugins with trigger=allRequirements AND requirements met" in {
|
||||
deducePlugin(A && B, log) must contain(R)
|
||||
}
|
||||
"order enable plugins after required plugins" in {
|
||||
val ns = deducePlugin(A && B, log)
|
||||
( (ns indexOf Q) must beGreaterThan(ns indexOf A) ) and
|
||||
( (ns indexOf Q) must beGreaterThan(ns indexOf B) ) and
|
||||
( (ns indexOf R) must beGreaterThan(ns indexOf A) ) and
|
||||
( (ns indexOf R) must beGreaterThan(ns indexOf B) ) and
|
||||
( (ns indexOf R) must beGreaterThan(ns indexOf Q) )
|
||||
}
|
||||
"not enable plugins with trigger=allRequirements but conflicting requirements" in {
|
||||
deducePlugin(A && B, log) must not contain(S)
|
||||
}
|
||||
"enable plugins that are required by the requested plugins" in {
|
||||
val ns = deducePlugin(Q, log)
|
||||
(ns must contain(A)) and
|
||||
(ns must contain(B))
|
||||
}
|
||||
"throw an AutoPluginException on conflicting requirements" in {
|
||||
deducePlugin(S, log) must throwAn[AutoPluginException](message = """Contradiction in enabled plugins:
|
||||
- requested: sbt.AI\$S
|
||||
- enabled: sbt.AI\$S, sbt.AI\$Q, sbt.AI\$R, sbt.AI\$B, sbt.AI\$A
|
||||
- conflict: sbt.AI\$R is enabled by sbt.AI\$Q; excluded by sbt.AI\$S""")
|
||||
}
|
||||
"generates a detailed report on conflicting requirements" in {
|
||||
deducePlugin(T && U, log) must throwAn[AutoPluginException](message = """Contradiction in enabled plugins:
|
||||
- requested: sbt.AI\$T && sbt.AI\$U
|
||||
- enabled: sbt.AI\$U, sbt.AI\$T, sbt.AI\$A, sbt.AI\$Q, sbt.AI\$R, sbt.AI\$B
|
||||
- conflict: sbt.AI\$Q is enabled by sbt.AI\$A && sbt.AI\$B; required by sbt.AI\$T, sbt.AI\$R; excluded by sbt.AI\$U
|
||||
- conflict: sbt.AI\$R is enabled by sbt.AI\$Q; excluded by sbt.AI\$T""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AI
|
||||
{
|
||||
lazy val allPlugins: List[AutoPlugin] = List(A, B, Q, R, S, T, U)
|
||||
lazy val deducePlugin = Plugins.deducer(allPlugins)
|
||||
lazy val log = Logger.Null
|
||||
|
||||
trait EmptyAutoPlugin extends AutoPlugin {
|
||||
def requires = empty
|
||||
def trigger = noTrigger
|
||||
}
|
||||
object A extends EmptyAutoPlugin
|
||||
object B extends EmptyAutoPlugin
|
||||
|
||||
object Q extends AutoPlugin
|
||||
{
|
||||
def requires: Plugins = A && B
|
||||
def trigger = allRequirements
|
||||
}
|
||||
|
||||
object R extends AutoPlugin
|
||||
{
|
||||
def requires = Q
|
||||
def trigger = allRequirements
|
||||
}
|
||||
|
||||
object S extends AutoPlugin
|
||||
{
|
||||
def requires = Q && !R
|
||||
def trigger = allRequirements
|
||||
}
|
||||
|
||||
// This is an opt-in plugin with a requirement
|
||||
// Unless explicitly loaded by the build user, this will not be activated.
|
||||
object T extends AutoPlugin
|
||||
{
|
||||
def requires = Q && !R
|
||||
def trigger = noTrigger
|
||||
}
|
||||
|
||||
// This is an opt-in plugin with a requirement
|
||||
// Unless explicitly loaded by the build user, this will not be activated.
|
||||
object U extends AutoPlugin
|
||||
{
|
||||
def requires = A && !Q
|
||||
def trigger = noTrigger
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue