mirror of https://github.com/sbt/sbt.git
So apparently scalariform was unable to parse these files.
This commit is contained in:
parent
274e0b446c
commit
b8ab638b74
|
|
@ -5,11 +5,11 @@ TODO:
|
|||
- error message when a task doesn't exist that it would be provided by plugin x, enabled by natures y,z, blocked by a, b
|
||||
*/
|
||||
|
||||
import logic.{Atom, Clause, Clauses, Formula, Literal, Logic, Negated}
|
||||
import Logic.{CyclicNegation, InitialContradictions, InitialOverlap, LogicException}
|
||||
import Def.Setting
|
||||
import Plugins._
|
||||
import annotation.tailrec
|
||||
import logic.{Atom, Clause, Clauses, Formula, Literal, Logic, Negated}
|
||||
import Logic.{CyclicNegation, InitialContradictions, InitialOverlap, LogicException}
|
||||
import Def.Setting
|
||||
import Plugins._
|
||||
import annotation.tailrec
|
||||
|
||||
/**
|
||||
An AutoPlugin defines a group of settings and the conditions where the settings are automatically added to a build (called "activation").
|
||||
|
|
@ -30,7 +30,7 @@ For example, the following will automatically add the settings in `projectSettin
|
|||
override def projectSettings = Seq(...)
|
||||
|
||||
object autoImport {
|
||||
lazy val obfuscate = taskKey[Seq[File]]("Obfuscates the source.")
|
||||
lazy val obfuscate = taskKey[Seq[File]]("Obfuscates the source.")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -50,243 +50,237 @@ 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
|
||||
{
|
||||
/** 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
|
||||
* instance will be added automatically if the `Web` and `Javascript` plugins are enabled.
|
||||
*
|
||||
* When this method returns `noTrigger`, and `requires` method returns `Web && Javascript`, this plugin
|
||||
* instance will be added only if the build user enables it, but it will automatically add both `Web` and `Javascript`. */
|
||||
def trigger: PluginTrigger = noTrigger
|
||||
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
|
||||
* instance will be added automatically if the `Web` and `Javascript` plugins are enabled.
|
||||
*
|
||||
* When this method returns `noTrigger`, and `requires` method returns `Web && Javascript`, this plugin
|
||||
* instance will be added only if the build user enables it, but it will automatically add both `Web` and `Javascript`. */
|
||||
def trigger: PluginTrigger = noTrigger
|
||||
|
||||
/** This AutoPlugin requires the plugins the [[Plugins]] matcher returned by this method. See [[trigger]].
|
||||
*/
|
||||
def requires: Plugins = empty
|
||||
/** This AutoPlugin requires the plugins the [[Plugins]] matcher returned by this method. See [[trigger]].
|
||||
*/
|
||||
def requires: Plugins = empty
|
||||
|
||||
val label: String = getClass.getName.stripSuffix("$")
|
||||
val label: String = getClass.getName.stripSuffix("$")
|
||||
|
||||
override def toString: String = label
|
||||
override def toString: String = label
|
||||
|
||||
/** The [[Configuration]]s to add to each project that activates this AutoPlugin.*/
|
||||
def projectConfigurations: Seq[Configuration] = Nil
|
||||
/** The [[Configuration]]s to add to each project that activates this AutoPlugin.*/
|
||||
def projectConfigurations: Seq[Configuration] = Nil
|
||||
|
||||
/** The [[Setting]]s to add in the scope of each project that activates this AutoPlugin. */
|
||||
def projectSettings: Seq[Setting[_]] = Nil
|
||||
/** The [[Setting]]s to add in the scope of each project that activates this AutoPlugin. */
|
||||
def projectSettings: Seq[Setting[_]] = Nil
|
||||
|
||||
/** The [[Setting]]s to add to the build scope for each project that activates this AutoPlugin.
|
||||
* The settings returned here are guaranteed to be added to a given build scope only once
|
||||
* regardless of how many projects for that build activate this AutoPlugin. */
|
||||
def buildSettings: Seq[Setting[_]] = Nil
|
||||
/** The [[Setting]]s to add to the build scope for each project that activates this AutoPlugin.
|
||||
* The settings returned here are guaranteed to be added to a given build scope only once
|
||||
* regardless of how many projects for that build activate this AutoPlugin. */
|
||||
def buildSettings: Seq[Setting[_]] = Nil
|
||||
|
||||
/** The [[Setting]]s to add to the global scope exactly once if any project activates this AutoPlugin. */
|
||||
def globalSettings: Seq[Setting[_]] = Nil
|
||||
/** The [[Setting]]s to add to the global scope exactly once if any project activates this AutoPlugin. */
|
||||
def globalSettings: Seq[Setting[_]] = Nil
|
||||
|
||||
// TODO?: def commands: Seq[Command]
|
||||
// TODO?: def commands: Seq[Command]
|
||||
|
||||
private[sbt] def unary_! : Exclude = Exclude(this)
|
||||
private[sbt] def unary_! : Exclude = Exclude(this)
|
||||
|
||||
|
||||
/** If this plugin does not have any requirements, it means it is actually a root plugin. */
|
||||
private[sbt] final def isRoot: Boolean =
|
||||
requires match {
|
||||
case Empty => true
|
||||
case _ => false
|
||||
}
|
||||
/** If this plugin does not have any requirements, it means it is actually a root plugin. */
|
||||
private[sbt] final def isRoot: Boolean =
|
||||
requires match {
|
||||
case Empty => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
/** If this plugin does not have any requirements, it means it is actually a root plugin. */
|
||||
private[sbt] final def isAlwaysEnabled: Boolean =
|
||||
isRoot && (trigger == AllRequirements)
|
||||
/** If this plugin does not have any requirements, it means it is actually a root plugin. */
|
||||
private[sbt] final def isAlwaysEnabled: Boolean =
|
||||
isRoot && (trigger == AllRequirements)
|
||||
}
|
||||
|
||||
/** 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)
|
||||
{
|
||||
/** Prepends `p` to the error message derived from `origin`. */
|
||||
def withPrefix(p: String) = new AutoPluginException(p + message, origin)
|
||||
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
|
||||
{
|
||||
def apply(msg: String): AutoPluginException = new AutoPluginException(msg, None)
|
||||
def apply(origin: LogicException): AutoPluginException = new AutoPluginException(Plugins.translateMessage(origin), Some(origin))
|
||||
object AutoPluginException {
|
||||
def apply(msg: String): AutoPluginException = new AutoPluginException(msg, None)
|
||||
def apply(origin: LogicException): AutoPluginException = new AutoPluginException(Plugins.translateMessage(origin), Some(origin))
|
||||
}
|
||||
|
||||
sealed trait PluginTrigger
|
||||
case object AllRequirements extends PluginTrigger
|
||||
case object NoTrigger extends PluginTrigger
|
||||
case object NoTrigger extends PluginTrigger
|
||||
|
||||
/** An expression that matches `AutoPlugin`s. */
|
||||
sealed trait Plugins {
|
||||
def && (o: Basic): Plugins
|
||||
def && (o: Basic): Plugins
|
||||
}
|
||||
|
||||
|
||||
sealed trait PluginsFunctions
|
||||
{
|
||||
/** [[Plugins]] instance that doesn't require any [[Plugins]]s. */
|
||||
def empty: Plugins = Plugins.Empty
|
||||
sealed trait PluginsFunctions {
|
||||
/** [[Plugins]] instance that doesn't require any [[Plugins]]s. */
|
||||
def empty: Plugins = Plugins.Empty
|
||||
|
||||
/** This plugin is activated when all required plugins are present. */
|
||||
def allRequirements: PluginTrigger = AllRequirements
|
||||
/** This plugin is activated only when it is manually activated. */
|
||||
def noTrigger: PluginTrigger = NoTrigger
|
||||
/** This plugin is activated when all required plugins are present. */
|
||||
def allRequirements: PluginTrigger = AllRequirements
|
||||
/** This plugin is activated only when it is manually activated. */
|
||||
def noTrigger: PluginTrigger = NoTrigger
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
// TODO: defined should return all the plugins
|
||||
val allReqs = (defined0 flatMap { asRequirements }).toSet
|
||||
val diff = allReqs diff defined0.toSet
|
||||
val defined = if (!diff.isEmpty) diff.toList ::: defined0
|
||||
else defined0
|
||||
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 {
|
||||
// TODO: defined should return all the plugins
|
||||
val allReqs = (defined0 flatMap { asRequirements }).toSet
|
||||
val diff = allReqs diff defined0.toSet
|
||||
val defined = if (!diff.isEmpty) diff.toList ::: defined0
|
||||
else defined0
|
||||
|
||||
val byAtom = defined map { x => (Atom(x.label), x) }
|
||||
val byAtomMap = byAtom.toMap
|
||||
if(byAtom.size != byAtomMap.size) duplicateProvidesError(byAtom)
|
||||
// Ignore clauses for plugins that does not require anything else.
|
||||
// Avoids the requirement for pure Nature strings *and* possible
|
||||
// circular dependencies in the logic.
|
||||
val allRequirementsClause = defined.filterNot(_.isRoot).flatMap(d => asRequirementsClauses(d))
|
||||
val allEnabledByClause = defined.filterNot(_.isRoot).flatMap(d => asEnabledByClauses(d))
|
||||
val byAtom = defined map { x => (Atom(x.label), x) }
|
||||
val byAtomMap = byAtom.toMap
|
||||
if(byAtom.size != byAtomMap.size) duplicateProvidesError(byAtom)
|
||||
// Ignore clauses for plugins that does not require anything else.
|
||||
// Avoids the requirement for pure Nature strings *and* possible
|
||||
// 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) => {
|
||||
(requestedPlugins, log) => {
|
||||
def explicitlyDisabled(p: AutoPlugin): Boolean = hasExclude(requestedPlugins, p)
|
||||
val alwaysEnabled: List[AutoPlugin] = defined.filter(_.isAlwaysEnabled).filterNot(explicitlyDisabled)
|
||||
val alwaysEnabled: List[AutoPlugin] = defined.filter(_.isAlwaysEnabled).filterNot(explicitlyDisabled)
|
||||
System.err.println(s"Always Enabled Plugins = ${alwaysEnabled.mkString(", ")}")
|
||||
System.err.println(s"Requested = $requestedPlugins")
|
||||
val knowlege0: Set[Atom] = ((flatten(requestedPlugins) ++ alwaysEnabled) collect {
|
||||
case x: AutoPlugin => Atom(x.label)
|
||||
}).toSet
|
||||
val clauses = Clauses((allRequirementsClause ::: allEnabledByClause) filterNot { _.head subsetOf knowlege0 })
|
||||
log.debug(s"deducing auto plugins based on known facts ${knowlege0.toString} and clauses ${clauses.toString}")
|
||||
Logic.reduce(clauses, (flattenConvert(requestedPlugins) ++ convertAll(alwaysEnabled)).toSet) match {
|
||||
case Left(problem) => throw AutoPluginException(problem)
|
||||
case Right(results) =>
|
||||
log.debug(s" :: deduced result: ${results}")
|
||||
val selectedAtoms: List[Atom] = results.ordered
|
||||
val selectedPlugins = selectedAtoms map { a =>
|
||||
byAtomMap.getOrElse(a, throw AutoPluginException(s"${a} was not found in atom map."))
|
||||
}
|
||||
val forbidden: Set[AutoPlugin] = (selectedPlugins flatMap { Plugins.asExclusions }).toSet
|
||||
val c = selectedPlugins.toSet & forbidden
|
||||
if (!c.isEmpty) {
|
||||
exlusionConflictError(requestedPlugins, selectedPlugins, c.toSeq sortBy {_.label})
|
||||
}
|
||||
val retval = topologicalSort(selectedPlugins, log)
|
||||
log.debug(s" :: sorted deduced result: ${retval.toString}")
|
||||
retval
|
||||
}
|
||||
}
|
||||
}
|
||||
private[sbt] def topologicalSort(ns: List[AutoPlugin], log: Logger): List[AutoPlugin] = {
|
||||
log.debug(s"sorting: ns: ${ns.toString}")
|
||||
@tailrec def doSort(found0: List[AutoPlugin], notFound0: List[AutoPlugin], limit0: Int): List[AutoPlugin] = {
|
||||
log.debug(s" :: sorting:: found: ${found0.toString} not found ${notFound0.toString}")
|
||||
if (limit0 < 0) throw AutoPluginException(s"Failed to sort ${ns} topologically")
|
||||
else if (notFound0.isEmpty) found0
|
||||
else {
|
||||
val (found1, notFound1) = notFound0 partition { n => asRequirements(n).toSet subsetOf found0.toSet }
|
||||
doSort(found0 ::: found1, notFound1, limit0 - 1)
|
||||
}
|
||||
}
|
||||
val (roots, nonRoots) = ns partition (_.isRoot)
|
||||
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 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)}"
|
||||
}
|
||||
private[this] def literalsString(lits: Seq[Literal]): String =
|
||||
lits map { case Atom(l) => l; case Negated(Atom(l)) => l } mkString(", ")
|
||||
val knowlege0: Set[Atom] = ((flatten(requestedPlugins) ++ alwaysEnabled) collect {
|
||||
case x: AutoPlugin => Atom(x.label)
|
||||
}).toSet
|
||||
val clauses = Clauses((allRequirementsClause ::: allEnabledByClause) filterNot { _.head subsetOf knowlege0 })
|
||||
log.debug(s"deducing auto plugins based on known facts ${knowlege0.toString} and clauses ${clauses.toString}")
|
||||
Logic.reduce(clauses, (flattenConvert(requestedPlugins) ++ convertAll(alwaysEnabled)).toSet) match {
|
||||
case Left(problem) => throw AutoPluginException(problem)
|
||||
case Right(results) =>
|
||||
log.debug(s" :: deduced result: ${results}")
|
||||
val selectedAtoms: List[Atom] = results.ordered
|
||||
val selectedPlugins = selectedAtoms map { a =>
|
||||
byAtomMap.getOrElse(a, throw AutoPluginException(s"${a} was not found in atom map."))
|
||||
}
|
||||
val forbidden: Set[AutoPlugin] = (selectedPlugins flatMap { Plugins.asExclusions }).toSet
|
||||
val c = selectedPlugins.toSet & forbidden
|
||||
if (!c.isEmpty) {
|
||||
exlusionConflictError(requestedPlugins, selectedPlugins, c.toSeq sortBy {_.label})
|
||||
}
|
||||
val retval = topologicalSort(selectedPlugins, log)
|
||||
log.debug(s" :: sorted deduced result: ${retval.toString}")
|
||||
retval
|
||||
}
|
||||
}
|
||||
}
|
||||
private[sbt] def topologicalSort(ns: List[AutoPlugin], log: Logger): List[AutoPlugin] = {
|
||||
log.debug(s"sorting: ns: ${ns.toString}")
|
||||
@tailrec def doSort(found0: List[AutoPlugin], notFound0: List[AutoPlugin], limit0: Int): List[AutoPlugin] = {
|
||||
log.debug(s" :: sorting:: found: ${found0.toString} not found ${notFound0.toString}")
|
||||
if (limit0 < 0) throw AutoPluginException(s"Failed to sort ${ns} topologically")
|
||||
else if (notFound0.isEmpty) found0
|
||||
else {
|
||||
val (found1, notFound1) = notFound0 partition { n => asRequirements(n).toSet subsetOf found0.toSet }
|
||||
doSort(found0 ::: found1, notFound1, limit0 - 1)
|
||||
}
|
||||
}
|
||||
val (roots, nonRoots) = ns partition (_.isRoot)
|
||||
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 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)}"
|
||||
}
|
||||
private[this] def literalsString(lits: Seq[Literal]): String =
|
||||
lits map { case Atom(l) => l; case Negated(Atom(l)) => l } mkString(", ")
|
||||
|
||||
private[this] def duplicateProvidesError(byAtom: Seq[(Atom, AutoPlugin)]) {
|
||||
val dupsByAtom = byAtom.groupBy(_._1).mapValues(_.map(_._2))
|
||||
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"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:
|
||||
private[this] def duplicateProvidesError(byAtom: Seq[(Atom, AutoPlugin)]) {
|
||||
val dupsByAtom = byAtom.groupBy(_._1).mapValues(_.map(_._2))
|
||||
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"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
|
||||
override def toString = "<none>"
|
||||
}
|
||||
private[sbt] final object Empty extends Plugins {
|
||||
def &&(o: Basic): Plugins = o
|
||||
override def toString = "<none>"
|
||||
}
|
||||
|
||||
/** 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(plugins: List[Basic]) extends Plugins {
|
||||
def &&(o: Basic): Plugins = And(o :: plugins)
|
||||
override def toString = plugins.mkString(" && ")
|
||||
}
|
||||
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: Plugins, del: Set[Basic]): Plugins = a match {
|
||||
case b: Basic => if(del(b)) Empty else b
|
||||
case Empty => Empty
|
||||
case And(ns) =>
|
||||
val removed = ns.filterNot(del)
|
||||
if(removed.isEmpty) Empty else And(removed)
|
||||
}
|
||||
/** 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(plugins: List[Basic]) extends Plugins {
|
||||
def &&(o: Basic): Plugins = And(o :: plugins)
|
||||
override def toString = plugins.mkString(" && ")
|
||||
}
|
||||
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: Plugins, del: Set[Basic]): Plugins = a match {
|
||||
case b: Basic => if(del(b)) Empty else b
|
||||
case Empty => Empty
|
||||
case And(ns) =>
|
||||
val removed = ns.filterNot(del)
|
||||
if(removed.isEmpty) Empty else And(removed)
|
||||
}
|
||||
|
||||
/** Defines enabled-by clauses for `ap`. */
|
||||
private[sbt] def asEnabledByClauses(ap: AutoPlugin): List[Clause] =
|
||||
// `ap` is the head and the required plugins for `ap` is the body.
|
||||
if (ap.trigger == AllRequirements) Clause( convert(ap.requires), Set(Atom(ap.label)) ) :: Nil
|
||||
else Nil
|
||||
/** Defines requirements clauses for `ap`. */
|
||||
private[sbt] def asRequirementsClauses(ap: AutoPlugin): List[Clause] =
|
||||
// required plugin is the head and `ap` is the body.
|
||||
asRequirements(ap) map { x => Clause( convert(ap), Set(Atom(x.label)) ) }
|
||||
private[sbt] def asRequirements(ap: AutoPlugin): List[AutoPlugin] = flatten(ap.requires).toList collect {
|
||||
case x: AutoPlugin => x
|
||||
}
|
||||
private[sbt] def asExclusions(ap: AutoPlugin): List[AutoPlugin] = flatten(ap.requires).toList collect {
|
||||
case Exclude(x) => x
|
||||
}
|
||||
/** Defines enabled-by clauses for `ap`. */
|
||||
private[sbt] def asEnabledByClauses(ap: AutoPlugin): List[Clause] =
|
||||
// `ap` is the head and the required plugins for `ap` is the body.
|
||||
if (ap.trigger == AllRequirements) Clause( convert(ap.requires), Set(Atom(ap.label)) ) :: Nil
|
||||
else Nil
|
||||
/** Defines requirements clauses for `ap`. */
|
||||
private[sbt] def asRequirementsClauses(ap: AutoPlugin): List[Clause] =
|
||||
// required plugin is the head and `ap` is the body.
|
||||
asRequirements(ap) map { x => Clause( convert(ap), Set(Atom(x.label)) ) }
|
||||
private[sbt] def asRequirements(ap: AutoPlugin): List[AutoPlugin] = flatten(ap.requires).toList collect {
|
||||
case x: AutoPlugin => x
|
||||
}
|
||||
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
|
||||
|
|
@ -306,46 +300,46 @@ ${listConflicts(conflicting)}""")
|
|||
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
|
||||
case Empty => Nil
|
||||
}
|
||||
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 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: Plugins): Seq[Basic] = n match {
|
||||
case And(ns) => ns
|
||||
case b: Basic => b :: Nil
|
||||
case Empty => Nil
|
||||
}
|
||||
|
||||
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 a: AutoPlugin => Atom(a.label)
|
||||
}
|
||||
private[this] def convertAll(ns: Seq[Basic]): Seq[Literal] = ns map convertBasic
|
||||
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 a: AutoPlugin => Atom(a.label)
|
||||
}
|
||||
private[this] def convertAll(ns: Seq[Basic]): Seq[Literal] = ns map convertBasic
|
||||
|
||||
/** True if the trigger clause `n` is satisifed by `model`. */
|
||||
def satisfied(n: Plugins, model: Set[AutoPlugin]): Boolean =
|
||||
flatten(n) forall {
|
||||
case Exclude(a) => !model(a)
|
||||
case ap: AutoPlugin => model(ap)
|
||||
}
|
||||
/** True if the trigger clause `n` is satisifed by `model`. */
|
||||
def satisfied(n: Plugins, model: Set[AutoPlugin]): Boolean =
|
||||
flatten(n) forall {
|
||||
case Exclude(a) => !model(a)
|
||||
case ap: AutoPlugin => model(ap)
|
||||
}
|
||||
|
||||
private[sbt] def hasAutoImportGetter(ap: AutoPlugin, loader: ClassLoader): Boolean = {
|
||||
import reflect.runtime.{universe => ru}
|
||||
import util.control.Exception.catching
|
||||
val m = ru.runtimeMirror(loader)
|
||||
val im = m.reflect(ap)
|
||||
val hasGetterOpt = catching(classOf[ScalaReflectionException]) opt {
|
||||
im.symbol.asType.toType.declaration(ru.newTermName("autoImport")) match {
|
||||
case ru.NoSymbol => false
|
||||
case sym => sym.asTerm.isGetter || sym.asTerm.isModule
|
||||
}
|
||||
}
|
||||
hasGetterOpt getOrElse false
|
||||
}
|
||||
private[sbt] def hasAutoImportGetter(ap: AutoPlugin, loader: ClassLoader): Boolean = {
|
||||
import reflect.runtime.{universe => ru}
|
||||
import util.control.Exception.catching
|
||||
val m = ru.runtimeMirror(loader)
|
||||
val im = m.reflect(ap)
|
||||
val hasGetterOpt = catching(classOf[ScalaReflectionException]) opt {
|
||||
im.symbol.asType.toType.declaration(ru.newTermName("autoImport")) match {
|
||||
case ru.NoSymbol => false
|
||||
case sym => sym.asTerm.isGetter || sym.asTerm.isModule
|
||||
}
|
||||
}
|
||||
hasGetterOpt getOrElse false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,382 +1,375 @@
|
|||
package sbt
|
||||
|
||||
import Def.Setting
|
||||
import Plugins._
|
||||
import PluginsDebug._
|
||||
import java.net.URI
|
||||
import Def.Setting
|
||||
import Plugins._
|
||||
import PluginsDebug._
|
||||
import java.net.URI
|
||||
|
||||
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 {
|
||||
case None => Set.empty
|
||||
case Some(key) => provided.reverse(key)
|
||||
}
|
||||
/** Describes alternative approaches for defining key [[keyName]] in [[context]].*/
|
||||
def toEnable(keyName: String, context: Context): List[PluginEnable] =
|
||||
providers(keyName).toList.map(plugin => pluginEnable(context, plugin))
|
||||
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 {
|
||||
case None => Set.empty
|
||||
case Some(key) => provided.reverse(key)
|
||||
}
|
||||
/** Describes alternative approaches for defining key [[keyName]] in [[context]].*/
|
||||
def toEnable(keyName: String, context: Context): List[PluginEnable] =
|
||||
providers(keyName).toList.map(plugin => pluginEnable(context, plugin))
|
||||
|
||||
/** Provides text to suggest how [[notFoundKey]] can be defined in [[context]]. */
|
||||
def debug(notFoundKey: String, context: Context): String =
|
||||
{
|
||||
val (activated, deactivated) = Util.separate(toEnable(notFoundKey, context)) {
|
||||
case pa: PluginActivated => Left(pa)
|
||||
case pd: EnableDeactivated => Right(pd)
|
||||
}
|
||||
val activePrefix = if(activated.nonEmpty) s"Some already activated plugins define $notFoundKey: ${activated.mkString(", ")}\n" else ""
|
||||
activePrefix + debugDeactivated(notFoundKey, deactivated)
|
||||
}
|
||||
private[this] def debugDeactivated(notFoundKey: String, deactivated: Seq[EnableDeactivated]): String =
|
||||
{
|
||||
val (impossible, possible) = Util.separate(deactivated) {
|
||||
case pi: PluginImpossible => Left(pi)
|
||||
case pr: PluginRequirements => Right(pr)
|
||||
}
|
||||
if(possible.nonEmpty) {
|
||||
val explained = possible.map(explainPluginEnable)
|
||||
val possibleString =
|
||||
if(explained.size > 1) explained.zipWithIndex.map{case (s,i) => s"$i. $s"}.mkString("Multiple plugins are available that can provide $notFoundKey:\n", "\n", "")
|
||||
else s"$notFoundKey is provided by an available (but not activated) plugin:\n${explained.mkString}"
|
||||
def impossiblePlugins = impossible.map(_.plugin.label).mkString(", ")
|
||||
val imPostfix = if(impossible.isEmpty) "" else s"\n\nThere are other available plugins that provide $notFoundKey, but they are impossible to add: $impossiblePlugins"
|
||||
possibleString + imPostfix
|
||||
}
|
||||
else if(impossible.isEmpty)
|
||||
s"No available plugin provides key $notFoundKey."
|
||||
else {
|
||||
val explanations = impossible.map(explainPluginEnable)
|
||||
explanations.mkString(s"Plugins are available that could provide $notFoundKey, but they are impossible to add:\n\t", "\n\t", "")
|
||||
}
|
||||
}
|
||||
/** Provides text to suggest how [[notFoundKey]] can be defined in [[context]]. */
|
||||
def debug(notFoundKey: String, context: Context): String =
|
||||
{
|
||||
val (activated, deactivated) = Util.separate(toEnable(notFoundKey, context)) {
|
||||
case pa: PluginActivated => Left(pa)
|
||||
case pd: EnableDeactivated => Right(pd)
|
||||
}
|
||||
val activePrefix = if(activated.nonEmpty) s"Some already activated plugins define $notFoundKey: ${activated.mkString(", ")}\n" else ""
|
||||
activePrefix + debugDeactivated(notFoundKey, deactivated)
|
||||
}
|
||||
private[this] def debugDeactivated(notFoundKey: String, deactivated: Seq[EnableDeactivated]): String =
|
||||
{
|
||||
val (impossible, possible) = Util.separate(deactivated) {
|
||||
case pi: PluginImpossible => Left(pi)
|
||||
case pr: PluginRequirements => Right(pr)
|
||||
}
|
||||
if(possible.nonEmpty) {
|
||||
val explained = possible.map(explainPluginEnable)
|
||||
val possibleString =
|
||||
if(explained.size > 1) explained.zipWithIndex.map{case (s,i) => s"$i. $s"}.mkString("Multiple plugins are available that can provide $notFoundKey:\n", "\n", "")
|
||||
else s"$notFoundKey is provided by an available (but not activated) plugin:\n${explained.mkString}"
|
||||
def impossiblePlugins = impossible.map(_.plugin.label).mkString(", ")
|
||||
val imPostfix = if(impossible.isEmpty) "" else s"\n\nThere are other available plugins that provide $notFoundKey, but they are impossible to add: $impossiblePlugins"
|
||||
possibleString + imPostfix
|
||||
}
|
||||
else if(impossible.isEmpty)
|
||||
s"No available plugin provides key $notFoundKey."
|
||||
else {
|
||||
val explanations = impossible.map(explainPluginEnable)
|
||||
explanations.mkString(s"Plugins are available that could provide $notFoundKey, but they are impossible to add:\n\t", "\n\t", "")
|
||||
}
|
||||
}
|
||||
|
||||
/** 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)
|
||||
private def activatedHelp(plugin: AutoPlugin): String =
|
||||
{
|
||||
val prefix = s"${plugin.label} is activated."
|
||||
val keys = provided.forward(plugin)
|
||||
val keysString = if(keys.isEmpty) "" else s"\nIt may affect these keys: ${multi(keys.toList.map(_.label))}"
|
||||
val configs = plugin.projectConfigurations
|
||||
val confsString = if(configs.isEmpty) "" else s"\nIt defines these configurations: ${multi(configs.map(_.name))}"
|
||||
prefix + keysString + confsString
|
||||
}
|
||||
private def deactivatedHelp(plugin: AutoPlugin, context: Context): String =
|
||||
{
|
||||
val prefix = s"${plugin.label} is NOT activated."
|
||||
val keys = provided.forward(plugin)
|
||||
val keysString = if(keys.isEmpty) "" else s"\nActivating it may affect these keys: ${multi(keys.toList.map(_.label))}"
|
||||
val configs = plugin.projectConfigurations
|
||||
val confsString = if(configs.isEmpty) "" else s"\nActivating it will define these configurations: ${multi(configs.map(_.name))}"
|
||||
val toActivate = explainPluginEnable(pluginEnable(context, plugin))
|
||||
s"$prefix$keysString$confsString\n$toActivate"
|
||||
}
|
||||
/** 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)
|
||||
private def activatedHelp(plugin: AutoPlugin): String =
|
||||
{
|
||||
val prefix = s"${plugin.label} is activated."
|
||||
val keys = provided.forward(plugin)
|
||||
val keysString = if(keys.isEmpty) "" else s"\nIt may affect these keys: ${multi(keys.toList.map(_.label))}"
|
||||
val configs = plugin.projectConfigurations
|
||||
val confsString = if(configs.isEmpty) "" else s"\nIt defines these configurations: ${multi(configs.map(_.name))}"
|
||||
prefix + keysString + confsString
|
||||
}
|
||||
private def deactivatedHelp(plugin: AutoPlugin, context: Context): String =
|
||||
{
|
||||
val prefix = s"${plugin.label} is NOT activated."
|
||||
val keys = provided.forward(plugin)
|
||||
val keysString = if(keys.isEmpty) "" else s"\nActivating it may affect these keys: ${multi(keys.toList.map(_.label))}"
|
||||
val configs = plugin.projectConfigurations
|
||||
val confsString = if(configs.isEmpty) "" else s"\nActivating it will define these configurations: ${multi(configs.map(_.name))}"
|
||||
val toActivate = explainPluginEnable(pluginEnable(context, plugin))
|
||||
s"$prefix$keysString$confsString\n$toActivate"
|
||||
}
|
||||
|
||||
private[this] def multi(strs: Seq[String]): String = strs.mkString(if(strs.size > 4) "\n\t" else ", ")
|
||||
private[this] def multi(strs: Seq[String]): String = strs.mkString(if(strs.size > 4) "\n\t" else ", ")
|
||||
}
|
||||
|
||||
private[sbt] object PluginsDebug
|
||||
{
|
||||
def helpAll(s: State): String =
|
||||
if(Project.isProjectLoaded(s))
|
||||
{
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
def helpBuild(uri: URI, build: LoadedBuildUnit): String =
|
||||
{
|
||||
val pluginStrings = for(plugin <- availableAutoPlugins(build)) yield {
|
||||
val activatedIn = build.defined.values.toList.filter(_.autoPlugins.contains(plugin)).map(_.id)
|
||||
val actString = if(activatedIn.nonEmpty) activatedIn.mkString(": enabled in ", ", ", "") else "" // TODO: deal with large builds
|
||||
s"\n\t${plugin.label}$actString"
|
||||
}
|
||||
s"In $uri${pluginStrings.mkString}"
|
||||
}
|
||||
val buildStrings = for((uri, build) <- structure.units) yield helpBuild(uri, build)
|
||||
buildStrings.mkString("\n")
|
||||
}
|
||||
else
|
||||
"No project is currently loaded."
|
||||
private[sbt] object PluginsDebug {
|
||||
def helpAll(s: State): String =
|
||||
if(Project.isProjectLoaded(s))
|
||||
{
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
def helpBuild(uri: URI, build: LoadedBuildUnit): String =
|
||||
{
|
||||
val pluginStrings = for(plugin <- availableAutoPlugins(build)) yield {
|
||||
val activatedIn = build.defined.values.toList.filter(_.autoPlugins.contains(plugin)).map(_.id)
|
||||
val actString = if(activatedIn.nonEmpty) activatedIn.mkString(": enabled in ", ", ", "") else "" // TODO: deal with large builds
|
||||
s"\n\t${plugin.label}$actString"
|
||||
}
|
||||
s"In $uri${pluginStrings.mkString}"
|
||||
}
|
||||
val buildStrings = for((uri, build) <- structure.units) yield helpBuild(uri, build)
|
||||
buildStrings.mkString("\n")
|
||||
}
|
||||
else "No project is currently loaded."
|
||||
|
||||
def autoPluginMap(s: State): Map[String, AutoPlugin] =
|
||||
{
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
structure.units.values.toList.flatMap(availableAutoPlugins).map(plugin => (plugin.label, plugin)).toMap
|
||||
}
|
||||
private[this] def availableAutoPlugins(build: LoadedBuildUnit): Seq[AutoPlugin] =
|
||||
build.unit.plugins.detected.autoPlugins map {_.value}
|
||||
def autoPluginMap(s: State): Map[String, AutoPlugin] =
|
||||
{
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
structure.units.values.toList.flatMap(availableAutoPlugins).map(plugin => (plugin.label, plugin)).toMap
|
||||
}
|
||||
private[this] def availableAutoPlugins(build: LoadedBuildUnit): Seq[AutoPlugin] =
|
||||
build.unit.plugins.detected.autoPlugins map {_.value}
|
||||
|
||||
def help(plugin: AutoPlugin, s: State): String =
|
||||
{
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
def definesPlugin(p: ResolvedProject): Boolean = p.autoPlugins.contains(plugin)
|
||||
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.plugins, currentProject.autoPlugins, Plugins.deducer(pluginsThisBuild), pluginsThisBuild, s.log)
|
||||
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."
|
||||
} else if(definesPlugin(currentProject))
|
||||
debug.activatedHelp(plugin)
|
||||
else {
|
||||
val thisAggregated = BuildUtil.dependencies(structure.units).aggregateTransitive.getOrElse(currentRef, Nil)
|
||||
val definedInAggregated = thisAggregated.filter(ref => definesPlugin(projectForRef(ref)))
|
||||
if(definedInAggregated.nonEmpty) {
|
||||
val projectNames = definedInAggregated.map(_.project) // TODO: usually in this build, but could technically require the build to be qualified
|
||||
s"Plugin ${plugin.label} is not activated on this project, but this project aggregates projects where it is activated:\n\t${projectNames.mkString("\n\t")}"
|
||||
} else {
|
||||
val base = debug.deactivatedHelp(plugin, context)
|
||||
val aggNote = if(thisAggregated.nonEmpty) "Note: This project aggregates other projects and this" else "Note: This"
|
||||
val common = " information is for this project only."
|
||||
val helpOther = "To see how to activate this plugin for another project, change to the project using `project <name>` and rerun this command."
|
||||
s"$base\n$aggNote$common\n$helpOther"
|
||||
}
|
||||
}
|
||||
}
|
||||
def help(plugin: AutoPlugin, s: State): String =
|
||||
{
|
||||
val extracted = Project.extract(s)
|
||||
import extracted._
|
||||
def definesPlugin(p: ResolvedProject): Boolean = p.autoPlugins.contains(plugin)
|
||||
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.plugins, currentProject.autoPlugins, Plugins.deducer(pluginsThisBuild), pluginsThisBuild, s.log)
|
||||
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."
|
||||
} else if(definesPlugin(currentProject))
|
||||
debug.activatedHelp(plugin)
|
||||
else {
|
||||
val thisAggregated = BuildUtil.dependencies(structure.units).aggregateTransitive.getOrElse(currentRef, Nil)
|
||||
val definedInAggregated = thisAggregated.filter(ref => definesPlugin(projectForRef(ref)))
|
||||
if(definedInAggregated.nonEmpty) {
|
||||
val projectNames = definedInAggregated.map(_.project) // TODO: usually in this build, but could technically require the build to be qualified
|
||||
s"Plugin ${plugin.label} is not activated on this project, but this project aggregates projects where it is activated:\n\t${projectNames.mkString("\n\t")}"
|
||||
} else {
|
||||
val base = debug.deactivatedHelp(plugin, context)
|
||||
val aggNote = if(thisAggregated.nonEmpty) "Note: This project aggregates other projects and this" else "Note: This"
|
||||
val common = " information is for this project only."
|
||||
val helpOther = "To see how to activate this plugin for another project, change to the project using `project <name>` and rerun this command."
|
||||
s"$base\n$aggNote$common\n$helpOther"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 PluginsDebug(available, nameToKey, keyR)
|
||||
}
|
||||
/** 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 PluginsDebug(available, nameToKey, keyR)
|
||||
}
|
||||
|
||||
/** The context for debugging a plugin (de)activation.
|
||||
* @param initial The initially defined [[AutoPlugin]]s.
|
||||
* @param enabled The resulting model.
|
||||
* @param deducePlugin The function used to compute the model.
|
||||
* @param available All [[AutoPlugin]]s available for consideration. */
|
||||
final case class Context(initial: Plugins, enabled: Seq[AutoPlugin], deducePlugin: (Plugins, Logger) => Seq[AutoPlugin], available: List[AutoPlugin], log: Logger)
|
||||
/** The context for debugging a plugin (de)activation.
|
||||
* @param initial The initially defined [[AutoPlugin]]s.
|
||||
* @param enabled The resulting model.
|
||||
* @param deducePlugin The function used to compute the model.
|
||||
* @param available All [[AutoPlugin]]s available for consideration. */
|
||||
final case class Context(initial: Plugins, enabled: Seq[AutoPlugin], deducePlugin: (Plugins, Logger) => Seq[AutoPlugin], available: List[AutoPlugin], log: Logger)
|
||||
|
||||
/** Describes the steps to activate a plugin in some context. */
|
||||
sealed abstract class PluginEnable
|
||||
/** Describes a [[plugin]] that is already activated in the [[context]].*/
|
||||
final case class PluginActivated(plugin: AutoPlugin, context: Context) extends PluginEnable
|
||||
sealed abstract class EnableDeactivated extends PluginEnable
|
||||
/** Describes a [[plugin]] that cannot be activated in a [[context]] due to [[contradictions]] in requirements. */
|
||||
final case class PluginImpossible(plugin: AutoPlugin, context: Context, contradictions: Set[AutoPlugin]) extends EnableDeactivated
|
||||
/** Describes the steps to activate a plugin in some context. */
|
||||
sealed abstract class PluginEnable
|
||||
/** Describes a [[plugin]] that is already activated in the [[context]].*/
|
||||
final case class PluginActivated(plugin: AutoPlugin, context: Context) extends PluginEnable
|
||||
sealed abstract class EnableDeactivated extends PluginEnable
|
||||
/** Describes a [[plugin]] that cannot be activated in a [[context]] due to [[contradictions]] in requirements. */
|
||||
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 plugins, exclusions, and ultimately activated plugins
|
||||
* @param blockingExcludes Existing exclusions that prevent [[plugin]] from being activated and must be dropped
|
||||
* @param enablingPlugins [[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 [[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 the requirements for activating [[plugin]] in [[context]].
|
||||
* @param context The base plugins, exclusions, and ultimately activated plugins
|
||||
* @param blockingExcludes Existing exclusions that prevent [[plugin]] from being activated and must be dropped
|
||||
* @param enablingPlugins [[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 [[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 [[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[AutoPlugin], newlySelected: Boolean)
|
||||
/** 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 [[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[AutoPlugin], newlySelected: Boolean)
|
||||
|
||||
/** Determines how to enable [[plugin]] in [[context]]. */
|
||||
def pluginEnable(context: Context, plugin: AutoPlugin): PluginEnable =
|
||||
if(context.enabled.contains(plugin))
|
||||
PluginActivated(plugin, context)
|
||||
else
|
||||
enableDeactivated(context, plugin)
|
||||
/** Determines how to enable [[plugin]] in [[context]]. */
|
||||
def pluginEnable(context: Context, plugin: AutoPlugin): PluginEnable =
|
||||
if(context.enabled.contains(plugin))
|
||||
PluginActivated(plugin, context)
|
||||
else
|
||||
enableDeactivated(context, plugin)
|
||||
|
||||
private[this] def enableDeactivated(context: Context, plugin: AutoPlugin): PluginEnable =
|
||||
{
|
||||
// deconstruct the context
|
||||
val initialModel = context.enabled.toSet
|
||||
val initial = flatten(context.initial)
|
||||
val initialPlugins = plugins(initial)
|
||||
val initialExcludes = excludes(initial)
|
||||
private[this] def enableDeactivated(context: Context, plugin: AutoPlugin): PluginEnable = {
|
||||
// deconstruct the context
|
||||
val initialModel = context.enabled.toSet
|
||||
val initial = flatten(context.initial)
|
||||
val initialPlugins = plugins(initial)
|
||||
val initialExcludes = excludes(initial)
|
||||
|
||||
val minModel = minimalModel(plugin)
|
||||
val minModel = minimalModel(plugin)
|
||||
|
||||
/* example 1
|
||||
A :- B, not C
|
||||
C :- D, E
|
||||
initial: B, D, E
|
||||
propose: drop D or E
|
||||
/* example 1
|
||||
A :- B, not C
|
||||
C :- D, E
|
||||
initial: B, D, E
|
||||
propose: drop D or E
|
||||
|
||||
initial: B, not A
|
||||
propose: drop 'not A'
|
||||
initial: B, not A
|
||||
propose: drop 'not A'
|
||||
|
||||
example 2
|
||||
A :- B, not C
|
||||
C :- B
|
||||
initial: <empty>
|
||||
propose: B, exclude C
|
||||
*/
|
||||
example 2
|
||||
A :- B, not C
|
||||
C :- B
|
||||
initial: <empty>
|
||||
propose: B, exclude C
|
||||
*/
|
||||
|
||||
// `plugin` will only be activated when all of these plugins are activated
|
||||
// Deactivating any one of these would deactivate `plugin`.
|
||||
val minRequiredPlugins = plugins(minModel)
|
||||
// `plugin` will only be activated when all of these plugins are activated
|
||||
// Deactivating any one of these would deactivate `plugin`.
|
||||
val minRequiredPlugins = plugins(minModel)
|
||||
|
||||
// The presence of any one of these plugins would deactivate `plugin`
|
||||
val minAbsentPlugins = excludes(minModel).toSet
|
||||
// The presence of any one of these plugins would deactivate `plugin`
|
||||
val minAbsentPlugins = excludes(minModel).toSet
|
||||
|
||||
// Plugins that must be both activated and deactivated for `plugin` to activate.
|
||||
// A non-empty list here cannot be satisfied and is an error.
|
||||
val contradictions = minAbsentPlugins & minRequiredPlugins
|
||||
// Plugins that must be both activated and deactivated for `plugin` to activate.
|
||||
// A non-empty list here cannot be satisfied and is an error.
|
||||
val contradictions = minAbsentPlugins & minRequiredPlugins
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
// Plugins that are currently excluded that need to be allowed.
|
||||
val blockingExcludes = initialExcludes & minRequiredPlugins
|
||||
// Plugins that are currently excluded that need to be allowed.
|
||||
val blockingExcludes = initialExcludes & minRequiredPlugins
|
||||
|
||||
// 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.deducePlugin(and(includeAll(minRequiredPlugins), excludeAll(minAbsentPlugins)), context.log)
|
||||
// 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.deducePlugin(and(includeAll(minRequiredPlugins), excludeAll(minAbsentPlugins)), context.log)
|
||||
|
||||
val incrementalInputs = and( includeAll(minRequiredPlugins ++ initialPlugins), excludeAll(minAbsentPlugins ++ initialExcludes -- minRequiredPlugins))
|
||||
val incrementalModel = context.deducePlugin(incrementalInputs, context.log).toSet
|
||||
val incrementalInputs = and( includeAll(minRequiredPlugins ++ initialPlugins), excludeAll(minAbsentPlugins ++ initialExcludes -- minRequiredPlugins))
|
||||
val incrementalModel = context.deducePlugin(incrementalInputs, context.log).toSet
|
||||
|
||||
// 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
|
||||
// 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
|
||||
|
||||
// Plugins that will no longer be enabled as a result of enabling `plugin`.
|
||||
val willRemove = initialModel -- incrementalModel
|
||||
// Plugins that will no longer be enabled as a result of enabling `plugin`.
|
||||
val willRemove = initialModel -- incrementalModel
|
||||
|
||||
// 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 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 => Plugins.satisfied(p.requires, incrementalModel))
|
||||
// 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 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 => Plugins.satisfied(p.requires, incrementalModel))
|
||||
|
||||
val deactivate = for(d <- minDeactivate.toList) yield {
|
||||
// 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 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)
|
||||
}
|
||||
val deactivate = for(d <- minDeactivate.toList) yield {
|
||||
// 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 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, addToExistingPlugins, extraPlugins, willRemove, deactivate)
|
||||
}
|
||||
}
|
||||
PluginRequirements(plugin, context, blockingExcludes, addToExistingPlugins, extraPlugins, willRemove, deactivate)
|
||||
}
|
||||
}
|
||||
|
||||
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 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 plugins(bs: Seq[Basic]): Set[AutoPlugin] = bs.collect { case n: AutoPlugin => n }.toSet
|
||||
private[this] def excludes(bs: Seq[Basic]): Set[AutoPlugin] = bs.collect { case Exclude(b) => b }.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 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 plugins.
|
||||
private[this] def minimalModel(plugin: AutoPlugin): Seq[Basic] = Dag.topologicalSortUnchecked(plugin: Basic) {
|
||||
case _: Exclude => Nil
|
||||
case ap: AutoPlugin => Plugins.flatten(ap.requires) :+ plugin
|
||||
}
|
||||
// If there is a model that includes `plugin`, it includes at least what is returned by this method.
|
||||
// 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 plugins.
|
||||
private[this] def minimalModel(plugin: AutoPlugin): Seq[Basic] = Dag.topologicalSortUnchecked(plugin: Basic) {
|
||||
case _: Exclude => Nil
|
||||
case ap: AutoPlugin => Plugins.flatten(ap.requires) :+ plugin
|
||||
}
|
||||
|
||||
/** String representation of [[PluginEnable]], intended for end users. */
|
||||
def explainPluginEnable(ps: PluginEnable): String =
|
||||
ps match {
|
||||
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(enablingPlugins.toList)) ::
|
||||
indent(needToDeactivate(deactivate)) ::
|
||||
note(willAdd(plugin, extraEnabledPlugins.toList)) ::
|
||||
note(willRemove(plugin, toBeRemoved.toList)) ::
|
||||
Nil
|
||||
parts.filterNot(_.isEmpty).mkString("\n")
|
||||
case PluginImpossible(plugin, context, contradictions) => pluginImpossible(plugin, contradictions)
|
||||
case PluginActivated(plugin, context) => s"Plugin ${plugin.label} already activated."
|
||||
}
|
||||
/** String representation of [[PluginEnable]], intended for end users. */
|
||||
def explainPluginEnable(ps: PluginEnable): String =
|
||||
ps match {
|
||||
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(enablingPlugins.toList)) ::
|
||||
indent(needToDeactivate(deactivate)) ::
|
||||
note(willAdd(plugin, extraEnabledPlugins.toList)) ::
|
||||
note(willRemove(plugin, toBeRemoved.toList)) ::
|
||||
Nil
|
||||
parts.filterNot(_.isEmpty).mkString("\n")
|
||||
case PluginImpossible(plugin, context, contradictions) => pluginImpossible(plugin, contradictions)
|
||||
case PluginActivated(plugin, context) => s"Plugin ${plugin.label} already activated."
|
||||
}
|
||||
|
||||
/** Provides a [[Relation]] between plugins and the keys they potentially define.
|
||||
* Because plugins can define keys in different scopes and keys can be overridden, this is not definitive.*/
|
||||
def definedKeys(available: List[AutoPlugin]): Relation[AutoPlugin, AttributeKey[_]] =
|
||||
{
|
||||
def extractDefinedKeys(ss: Seq[Setting[_]]): Seq[AttributeKey[_]] =
|
||||
ss.map(_.key.key)
|
||||
def allSettings(p: AutoPlugin): Seq[Setting[_]] = p.projectSettings ++ p.buildSettings ++ p.globalSettings
|
||||
val empty = Relation.empty[AutoPlugin, AttributeKey[_]]
|
||||
(empty /: available)( (r,p) => r + (p, extractDefinedKeys(allSettings(p))) )
|
||||
}
|
||||
/** Provides a [[Relation]] between plugins and the keys they potentially define.
|
||||
* Because plugins can define keys in different scopes and keys can be overridden, this is not definitive.*/
|
||||
def definedKeys(available: List[AutoPlugin]): Relation[AutoPlugin, AttributeKey[_]] =
|
||||
{
|
||||
def extractDefinedKeys(ss: Seq[Setting[_]]): Seq[AttributeKey[_]] =
|
||||
ss.map(_.key.key)
|
||||
def allSettings(p: AutoPlugin): Seq[Setting[_]] = p.projectSettings ++ p.buildSettings ++ p.globalSettings
|
||||
val empty = Relation.empty[AutoPlugin, AttributeKey[_]]
|
||||
(empty /: available)( (r,p) => r + (p, extractDefinedKeys(allSettings(p))) )
|
||||
}
|
||||
|
||||
private[this] def excludedError(transitive: Boolean, dependencies: List[AutoPlugin]): String =
|
||||
str(dependencies)(excludedPluginError(transitive), excludedPluginsError(transitive))
|
||||
private[this] def excludedError(transitive: Boolean, dependencies: List[AutoPlugin]): String =
|
||||
str(dependencies)(excludedPluginError(transitive), excludedPluginsError(transitive))
|
||||
|
||||
private[this] def excludedPluginError(transitive: Boolean)(dependency: AutoPlugin) =
|
||||
s"Required ${transitiveString(transitive)}dependency ${dependency.label} was excluded."
|
||||
private[this] def excludedPluginsError(transitive: Boolean)(dependencies: List[AutoPlugin]) =
|
||||
s"Required ${transitiveString(transitive)}dependencies were excluded:\n\t${labels(dependencies).mkString("\n\t")}"
|
||||
private[this] def transitiveString(transitive: Boolean) =
|
||||
if(transitive) "(transitive) " else ""
|
||||
private[this] def excludedPluginError(transitive: Boolean)(dependency: AutoPlugin) =
|
||||
s"Required ${transitiveString(transitive)}dependency ${dependency.label} was excluded."
|
||||
private[this] def excludedPluginsError(transitive: Boolean)(dependencies: List[AutoPlugin]) =
|
||||
s"Required ${transitiveString(transitive)}dependencies were excluded:\n\t${labels(dependencies).mkString("\n\t")}"
|
||||
private[this] def transitiveString(transitive: Boolean) =
|
||||
if(transitive) "(transitive) " else ""
|
||||
|
||||
private[this] def required(plugins: List[AutoPlugin]): String =
|
||||
str(plugins)(requiredPlugin, requiredPlugins)
|
||||
private[this] def required(plugins: List[AutoPlugin]): String =
|
||||
str(plugins)(requiredPlugin, requiredPlugins)
|
||||
|
||||
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 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 => ""
|
||||
case single :: Nil => f(single)
|
||||
case _ => fs(list)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
private[this] def willAdd(base: AutoPlugin, plugins: List[AutoPlugin]): String =
|
||||
str(plugins)(willAddPlugin(base), willAddPlugins(base))
|
||||
private[this] def willAdd(base: AutoPlugin, plugins: List[AutoPlugin]): String =
|
||||
str(plugins)(willAddPlugin(base), willAddPlugins(base))
|
||||
|
||||
private[this] def willAddPlugin(base: AutoPlugin)(plugin: AutoPlugin) =
|
||||
s"Enabling ${base.label} will also enable ${plugin.label}"
|
||||
private[this] def willAddPlugins(base: AutoPlugin)(plugins: List[AutoPlugin]) =
|
||||
s"Enabling ${base.label} will also enable:\n\t${labels(plugins).mkString("\n\t")}"
|
||||
private[this] def willAddPlugin(base: AutoPlugin)(plugin: AutoPlugin) =
|
||||
s"Enabling ${base.label} will also enable ${plugin.label}"
|
||||
private[this] def willAddPlugins(base: AutoPlugin)(plugins: List[AutoPlugin]) =
|
||||
s"Enabling ${base.label} will also enable:\n\t${labels(plugins).mkString("\n\t")}"
|
||||
|
||||
private[this] def willRemove(base: AutoPlugin, plugins: List[AutoPlugin]): String =
|
||||
str(plugins)(willRemovePlugin(base), willRemovePlugins(base))
|
||||
private[this] def willRemove(base: AutoPlugin, plugins: List[AutoPlugin]): String =
|
||||
str(plugins)(willRemovePlugin(base), willRemovePlugins(base))
|
||||
|
||||
private[this] def willRemovePlugin(base: AutoPlugin)(plugin: AutoPlugin) =
|
||||
s"Enabling ${base.label} will disable ${plugin.label}"
|
||||
private[this] def willRemovePlugins(base: AutoPlugin)(plugins: List[AutoPlugin]) =
|
||||
s"Enabling ${base.label} will disable:\n\t${labels(plugins).mkString("\n\t")}"
|
||||
private[this] def willRemovePlugin(base: AutoPlugin)(plugin: AutoPlugin) =
|
||||
s"Enabling ${base.label} will disable ${plugin.label}"
|
||||
private[this] def willRemovePlugins(base: AutoPlugin)(plugins: List[AutoPlugin]) =
|
||||
s"Enabling ${base.label} will disable:\n\t${labels(plugins).mkString("\n\t")}"
|
||||
|
||||
private[this] def labels(plugins: List[AutoPlugin]): List[String] =
|
||||
plugins.map(_.label)
|
||||
private[this] def labels(plugins: List[AutoPlugin]): List[String] =
|
||||
plugins.map(_.label)
|
||||
|
||||
private[this] def needToDeactivate(deactivate: List[DeactivatePlugin]): String =
|
||||
str(deactivate)(deactivate1, deactivateN)
|
||||
private[this] def deactivateN(plugins: List[DeactivatePlugin]): String =
|
||||
plugins.map(deactivateString).mkString("These plugins need to be deactivated:\n\t", "\n\t", "")
|
||||
private[this] def deactivate1(deactivate: DeactivatePlugin): String =
|
||||
s"Need to deactivate ${deactivateString(deactivate)}"
|
||||
private[this] def deactivateString(d: DeactivatePlugin): 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${removePluginsString}"
|
||||
}
|
||||
private[this] def needToDeactivate(deactivate: List[DeactivatePlugin]): String =
|
||||
str(deactivate)(deactivate1, deactivateN)
|
||||
private[this] def deactivateN(plugins: List[DeactivatePlugin]): String =
|
||||
plugins.map(deactivateString).mkString("These plugins need to be deactivated:\n\t", "\n\t", "")
|
||||
private[this] def deactivate1(deactivate: DeactivatePlugin): String =
|
||||
s"Need to deactivate ${deactivateString(deactivate)}"
|
||||
private[this] def deactivateString(d: DeactivatePlugin): 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${removePluginsString}"
|
||||
}
|
||||
|
||||
private[this] def pluginImpossible(plugin: AutoPlugin, contradictions: Set[AutoPlugin]): String =
|
||||
str(contradictions.toList)(pluginImpossible1(plugin), pluginImpossibleN(plugin))
|
||||
private[this] def pluginImpossible(plugin: AutoPlugin, contradictions: Set[AutoPlugin]): String =
|
||||
str(contradictions.toList)(pluginImpossible1(plugin), pluginImpossibleN(plugin))
|
||||
|
||||
private[this] def pluginImpossible1(plugin: AutoPlugin)(contradiction: AutoPlugin): String =
|
||||
s"There is no way to enable plugin ${plugin.label}. It (or its dependencies) requires plugin ${contradiction.label} to both be present and absent. Please report the problem to the plugin's author."
|
||||
private[this] def pluginImpossibleN(plugin: AutoPlugin)(contradictions: List[AutoPlugin]): String =
|
||||
s"There is no way to enable plugin ${plugin.label}. It (or its dependencies) requires these plugins to be both present and absent:\n\t${labels(contradictions).mkString("\n\t")}\nPlease report the problem to the plugin's author."
|
||||
private[this] def pluginImpossible1(plugin: AutoPlugin)(contradiction: AutoPlugin): String =
|
||||
s"There is no way to enable plugin ${plugin.label}. It (or its dependencies) requires plugin ${contradiction.label} to both be present and absent. Please report the problem to the plugin's author."
|
||||
private[this] def pluginImpossibleN(plugin: AutoPlugin)(contradictions: List[AutoPlugin]): String =
|
||||
s"There is no way to enable plugin ${plugin.label}. It (or its dependencies) requires these plugins to be both present and absent:\n\t${labels(contradictions).mkString("\n\t")}\nPlease report the problem to the plugin's author."
|
||||
}
|
||||
Loading…
Reference in New Issue