Main part of integrating natures into project loading.

This commit is contained in:
Mark Harrah 2014-01-14 12:38:06 -05:00
parent 322f6de655
commit b8619f4aae
13 changed files with 363 additions and 75 deletions

View File

@ -4,6 +4,9 @@ package sbt
import Def.Setting import Def.Setting
import Natures._ import Natures._
/** Marks a top-level object so that sbt will wildcard import it for .sbt files, `consoleProject`, and `set`. */
trait AutoImport
/** /**
An AutoPlugin defines a group of settings and the conditions that the settings are automatically added to a build (called "activation"). An AutoPlugin defines a group of settings and the conditions that the settings are automatically added to a build (called "activation").
The `select` method defines the conditions, The `select` method defines the conditions,
@ -59,15 +62,15 @@ abstract class AutoPlugin
def projectConfigurations: Seq[Configuration] = Nil def projectConfigurations: Seq[Configuration] = Nil
/** The [[Setting]]s to add in the scope of each project that activates this AutoPlugin. */ /** The [[Setting]]s to add in the scope of each project that activates this AutoPlugin. */
def projectSettings: Seq[Setting[_]] = Nil def projectSettings: Seq[Setting[_]] = Nil
/** The [[Setting]]s to add to the build scope for each project that activates this AutoPlugin. /** 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 * 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. */ * regardless of how many projects for that build activate this AutoPlugin. */
def buildSettings: Seq[Setting[_]] = Nil def buildSettings: Seq[Setting[_]] = Nil
/** The [[Setting]]s to add to the global scope exactly once if any project activates this AutoPlugin. */ /** The [[Setting]]s to add to the global scope exactly once if any project activates this AutoPlugin. */
def globalSettings: Seq[Setting[_]] = Nil def globalSettings: Seq[Setting[_]] = Nil
// TODO?: def commands: Seq[Command] // TODO?: def commands: Seq[Command]
} }
@ -82,19 +85,30 @@ sealed trait Natures {
final case class Nature(label: String) extends Basic { final case class Nature(label: String) extends Basic {
/** Constructs a Natures matcher that excludes this Nature. */ /** Constructs a Natures matcher that excludes this Nature. */
def unary_! : Basic = Exclude(this) def unary_! : Basic = Exclude(this)
override def toString = label
} }
object Natures object Natures
{ {
// TODO: allow multiple AutoPlugins to provide the same Nature? // TODO: allow multiple AutoPlugins to provide the same Nature?
// TODO: translate error messages // TODO: translate error messages
/** Select the AutoPlugins to include according to the user-specified natures in `requested` and all discovered AutoPlugins in `defined`.*/ def compile(defined: List[AutoPlugin]): Natures => Seq[AutoPlugin] =
def evaluate(requested: Natures, defined: List[AutoPlugin]): Seq[AutoPlugin] = if(defined.isEmpty)
{ Types.const(Nil)
val byAtom = defined.map(x => (Atom(x.provides.label), x)).toMap else
val clauses = Clauses( defined.map(d => asClause(d)) ) {
val results = Logic.reduce(clauses, flatten(requested).toSet) val byAtom = defined.map(x => (Atom(x.provides.label), x)).toMap
results.ordered.map(byAtom) val clauses = Clauses( defined.map(d => asClause(d)) )
requestedNatures => {
val results = Logic.reduce(clauses, flatten(requestedNatures).toSet)
results.ordered.flatMap(a => byAtom.get(a).toList)
}
}
def empty: Natures = Empty
private[sbt] final object Empty extends Natures {
def &&(o: Basic): Natures = o
override def toString = "<none>"
} }
/** An included or excluded Nature. TODO: better name than Basic. */ /** An included or excluded Nature. TODO: better name than Basic. */
@ -103,9 +117,16 @@ object Natures
} }
private[sbt] final case class Exclude(n: Nature) extends Basic { private[sbt] final case class Exclude(n: Nature) extends Basic {
def unary_! : Nature = n def unary_! : Nature = n
override def toString = s"!$n"
} }
private[sbt] final case class And(natures: List[Basic]) extends Natures { private[sbt] final case class And(natures: List[Basic]) extends Natures {
def &&(o: Basic): Natures = And(o :: natures) def &&(o: Basic): Natures = And(o :: natures)
override def toString = natures.mkString(", ")
}
private[sbt] def and(a: Natures, b: Natures) = b match {
case Empty => a
case And(ns) => (a /: ns)(_ && _)
case b: Basic => a && b
} }
private[sbt] def asClause(ap: AutoPlugin): Clause = private[sbt] def asClause(ap: AutoPlugin): Clause =
@ -114,11 +135,13 @@ object Natures
private[this] def flatten(n: Natures): Seq[Literal] = n match { private[this] def flatten(n: Natures): Seq[Literal] = n match {
case And(ns) => convertAll(ns) case And(ns) => convertAll(ns)
case b: Basic => convertBasic(b) :: Nil case b: Basic => convertBasic(b) :: Nil
case Empty => Nil
} }
private[this] def convert(n: Natures): Formula = n match { private[this] def convert(n: Natures): Formula = n match {
case And(ns) => convertAll(ns).reduce[Formula](_ && _) case And(ns) => convertAll(ns).reduce[Formula](_ && _)
case b: Basic => convertBasic(b) case b: Basic => convertBasic(b)
case Empty => Formula.True
} }
private[this] def convertBasic(b: Basic): Literal = b match { private[this] def convertBasic(b: Basic): Literal = b match {
case Exclude(n) => !convertBasic(n) case Exclude(n) => !convertBasic(n)

View File

@ -18,6 +18,7 @@ trait Build
* If None, the root project is the first project in the build's root directory or just the first project if none are in the root directory.*/ * If None, the root project is the first project in the build's root directory or just the first project if none are in the root directory.*/
def rootProject: Option[Project] = None def rootProject: Option[Project] = None
} }
// TODO 0.14.0: decide if Plugin should be deprecated in favor of AutoPlugin
trait Plugin trait Plugin
{ {
@deprecated("Override projectSettings or buildSettings instead.", "0.12.0") @deprecated("Override projectSettings or buildSettings instead.", "0.12.0")

View File

@ -41,11 +41,35 @@ final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, Resolv
override def toString = unit.toString override def toString = unit.toString
} }
// TODO: figure out how to deprecate and drop buildNames
final class LoadedDefinitions(val base: File, val target: Seq[File], val loader: ClassLoader, val builds: Seq[Build], val projects: Seq[Project], val buildNames: Seq[String]) final class LoadedDefinitions(val base: File, val target: Seq[File], val loader: ClassLoader, val builds: Seq[Build], val projects: Seq[Project], val buildNames: Seq[String])
final class LoadedPlugins(val base: File, val pluginData: PluginData, val loader: ClassLoader, val plugins: Seq[Plugin], val pluginNames: Seq[String])
final class DetectedModules[T](val modules: Seq[(String, T)]) {
def names: Seq[String] = modules.map(_._1)
def values: Seq[T] = modules.map(_._2)
}
final class DetectedPlugins(val plugins: DetectedModules[Plugin], val autoImports: DetectedModules[AutoImport], val autoPlugins: DetectedModules[AutoPlugin], val builds: DetectedModules[Build])
{ {
lazy val imports: Seq[String] = BuildUtil.getImports(plugins.names ++ builds.names ++ autoImports.names)
lazy val compileNatures: Natures => Seq[AutoPlugin] = Natures.compile(autoPlugins.values.toList)
}
final class LoadedPlugins(val base: File, val pluginData: PluginData, val loader: ClassLoader, val detected: DetectedPlugins)
{
/*
// TODO: uncomment before COMMIT for compatibility
@deprecated("Use the primary constructor.", "0.13.2")
def this(base: File, pluginData: PluginData, loader: ClassLoader, plugins: Seq[Plugin], pluginNames: Seq[String]) =
this(base, pluginData, loader, DetectedPlugins(DetectedModules(pluginNames zip plugins), DetectedModules(Nil), DetectedModules(Nil), DetectedModules(Nil)))
@deprecated("Use detected.plugins.values.", "0.13.2")
val plugins = detected.plugins.values
@deprecated("Use detected.plugins.names.", "0.13.2")
val pluginNames = detected.plugins.names
*/
def fullClasspath: Seq[Attributed[File]] = pluginData.classpath def fullClasspath: Seq[Attributed[File]] = pluginData.classpath
def classpath = data(fullClasspath) def classpath = data(fullClasspath)
} }
final class BuildUnit(val uri: URI, val localBase: File, val definitions: LoadedDefinitions, val plugins: LoadedPlugins) final class BuildUnit(val uri: URI, val localBase: File, val definitions: LoadedDefinitions, val plugins: LoadedPlugins)
{ {
@ -57,6 +81,7 @@ final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit])
BuildUtil.checkCycles(units) BuildUtil.checkCycles(units)
def allProjectRefs: Seq[(ProjectRef, ResolvedProject)] = for( (uri, unit) <- units.toSeq; (id, proj) <- unit.defined ) yield ProjectRef(uri, id) -> proj def allProjectRefs: Seq[(ProjectRef, ResolvedProject)] = for( (uri, unit) <- units.toSeq; (id, proj) <- unit.defined ) yield ProjectRef(uri, id) -> proj
def extra(data: Settings[Scope])(keyIndex: KeyIndex): BuildUtil[ResolvedProject] = BuildUtil(root, units, keyIndex, data) def extra(data: Settings[Scope])(keyIndex: KeyIndex): BuildUtil[ResolvedProject] = BuildUtil(root, units, keyIndex, data)
private[sbt] def autos = GroupedAutoPlugins(units)
} }
final class PartBuild(val root: URI, val units: Map[URI, PartBuildUnit]) final class PartBuild(val root: URI, val units: Map[URI, PartBuildUnit])
sealed trait BuildUnitBase { def rootProjects: Seq[String]; def buildSettings: Seq[Setting[_]] } sealed trait BuildUnitBase { def rootProjects: Seq[String]; def buildSettings: Seq[Setting[_]] }

View File

@ -60,8 +60,14 @@ object BuildUtil
} }
} }
def baseImports: Seq[String] = "import sbt._, Keys._" :: Nil def baseImports: Seq[String] = "import sbt._, Keys._" :: Nil
def getImports(unit: BuildUnit): Seq[String] = getImports(unit.plugins.pluginNames, unit.definitions.buildNames)
def getImports(pluginNames: Seq[String], buildNames: Seq[String]): Seq[String] = baseImports ++ importAllRoot(pluginNames ++ buildNames) def getImports(unit: BuildUnit): Seq[String] = unit.plugins.detected.imports
@deprecated("Use getImports(Seq[String]).", "0.13.2")
def getImports(pluginNames: Seq[String], buildNames: Seq[String]): Seq[String] = getImports(pluginNames ++ buildNames)
def getImports(names: Seq[String]): Seq[String] = baseImports ++ importAllRoot(names)
def importAll(values: Seq[String]): Seq[String] = if(values.isEmpty) Nil else values.map( _ + "._" ).mkString("import ", ", ", "") :: Nil def importAll(values: Seq[String]): Seq[String] = if(values.isEmpty) Nil else values.map( _ + "._" ).mkString("import ", ", ", "") :: Nil
def importAllRoot(values: Seq[String]): Seq[String] = importAll(values map rootedName) def importAllRoot(values: Seq[String]): Seq[String] = importAll(values map rootedName)
def rootedName(s: String): String = if(s contains '.') "_root_." + s else s def rootedName(s: String): String = if(s contains '.') "_root_." + s else s

View File

@ -0,0 +1,20 @@
package sbt
import Def.Setting
import java.net.URI
final class GroupedAutoPlugins(val all: Seq[AutoPlugin], val byBuild: Map[URI, Seq[AutoPlugin]])
{
def globalSettings: Seq[Setting[_]] = all.flatMap(_.globalSettings)
def buildSettings(uri: URI): Seq[Setting[_]] = byBuild.getOrElse(uri, Nil).flatMap(_.buildSettings)
}
object GroupedAutoPlugins
{
private[sbt] def apply(units: Map[URI, LoadedBuildUnit]): GroupedAutoPlugins =
{
val byBuild: Map[URI, Seq[AutoPlugin]] = units.mapValues(unit => unit.defined.values.flatMap(_.autoPlugins).toSeq.distinct).toMap
val all: Seq[AutoPlugin] = byBuild.values.toSeq.flatten.distinct
new GroupedAutoPlugins(all, byBuild)
}
}

View File

@ -180,7 +180,7 @@ object Load
val keys = Index.allKeys(settings) val keys = Index.allKeys(settings)
val attributeKeys = Index.attributeKeys(data) ++ keys.map(_.key) val attributeKeys = Index.attributeKeys(data) ++ keys.map(_.key)
val scopedKeys = keys ++ data.allKeys( (s,k) => ScopedKey(s,k)) val scopedKeys = keys ++ data.allKeys( (s,k) => ScopedKey(s,k))
val projectsMap = projects.mapValues(_.defined.keySet) val projectsMap = projects.mapValues(_.defined.keySet).toMap
val keyIndex = KeyIndex(scopedKeys, projectsMap) val keyIndex = KeyIndex(scopedKeys, projectsMap)
val aggIndex = KeyIndex.aggregate(scopedKeys, extra(keyIndex), projectsMap) val aggIndex = KeyIndex.aggregate(scopedKeys, extra(keyIndex), projectsMap)
new sbt.StructureIndex(Index.stringToKeyMap(attributeKeys), Index.taskToKeyMap(data), Index.triggers(data), keyIndex, aggIndex) new sbt.StructureIndex(Index.stringToKeyMap(attributeKeys), Index.taskToKeyMap(data), Index.triggers(data), keyIndex, aggIndex)
@ -201,10 +201,10 @@ object Load
{ {
((loadedBuild in GlobalScope :== loaded) +: ((loadedBuild in GlobalScope :== loaded) +:
transformProjectOnly(loaded.root, rootProject, injectSettings.global)) ++ transformProjectOnly(loaded.root, rootProject, injectSettings.global)) ++
inScope(GlobalScope)( pluginGlobalSettings(loaded) ) ++ inScope(GlobalScope)( pluginGlobalSettings(loaded) ++ loaded.autos.globalSettings ) ++
loaded.units.toSeq.flatMap { case (uri, build) => loaded.units.toSeq.flatMap { case (uri, build) =>
val plugins = build.unit.plugins.plugins val plugins = build.unit.plugins.detected.plugins.values
val pluginBuildSettings = plugins.flatMap(_.buildSettings) val pluginBuildSettings = plugins.flatMap(_.buildSettings) ++ loaded.autos.buildSettings(uri)
val pluginNotThis = plugins.flatMap(_.settings) filterNot isProjectThis val pluginNotThis = plugins.flatMap(_.settings) filterNot isProjectThis
val projectSettings = build.defined flatMap { case (id, project) => val projectSettings = build.defined flatMap { case (id, project) =>
val ref = ProjectRef(uri, id) val ref = ProjectRef(uri, id)
@ -220,9 +220,10 @@ object Load
buildSettings ++ projectSettings buildSettings ++ projectSettings
} }
} }
@deprecated("Does not account for AutoPlugins and will be made private.", "0.13.2")
def pluginGlobalSettings(loaded: sbt.LoadedBuild): Seq[Setting[_]] = def pluginGlobalSettings(loaded: sbt.LoadedBuild): Seq[Setting[_]] =
loaded.units.toSeq flatMap { case (_, build) => loaded.units.toSeq flatMap { case (_, build) =>
build.unit.plugins.plugins flatMap { _.globalSettings } build.unit.plugins.detected.plugins.values flatMap { _.globalSettings }
} }
@deprecated("No longer used.", "0.13.0") @deprecated("No longer used.", "0.13.0")
@ -368,10 +369,11 @@ object Load
def resolveProjects(loaded: sbt.PartBuild): sbt.LoadedBuild = def resolveProjects(loaded: sbt.PartBuild): sbt.LoadedBuild =
{ {
val rootProject = getRootProject(loaded.units) val rootProject = getRootProject(loaded.units)
new sbt.LoadedBuild(loaded.root, loaded.units map { case (uri, unit) => val units = loaded.units map { case (uri, unit) =>
IO.assertAbsolute(uri) IO.assertAbsolute(uri)
(uri, resolveProjects(uri, unit, rootProject)) (uri, resolveProjects(uri, unit, rootProject))
}) }
new sbt.LoadedBuild(loaded.root, units)
} }
def resolveProjects(uri: URI, unit: sbt.PartBuildUnit, rootProject: URI => String): sbt.LoadedBuildUnit = def resolveProjects(uri: URI, unit: sbt.PartBuildUnit, rootProject: URI => String): sbt.LoadedBuildUnit =
{ {
@ -399,10 +401,10 @@ object Load
def getBuild[T](map: Map[URI, T], uri: URI): T = def getBuild[T](map: Map[URI, T], uri: URI): T =
map.getOrElse(uri, noBuild(uri)) map.getOrElse(uri, noBuild(uri))
def emptyBuild(uri: URI) = sys.error("No root project defined for build unit '" + uri + "'") def emptyBuild(uri: URI) = sys.error(s"No root project defined for build unit '$uri'")
def noBuild(uri: URI) = sys.error("Build unit '" + uri + "' not defined.") def noBuild(uri: URI) = sys.error(s"Build unit '$uri' not defined.")
def noProject(uri: URI, id: String) = sys.error("No project '" + id + "' defined in '" + uri + "'.") def noProject(uri: URI, id: String) = sys.error(s"No project '$id' defined in '$uri'.")
def noConfiguration(uri: URI, id: String, conf: String) = sys.error("No configuration '" + conf + "' defined in project '" + id + "' in '" + uri +"'") def noConfiguration(uri: URI, id: String, conf: String) = sys.error(s"No configuration '$conf' defined in project '$id' in '$uri'")
def loadUnit(uri: URI, localBase: File, s: State, config: sbt.LoadBuildConfiguration): sbt.BuildUnit = def loadUnit(uri: URI, localBase: File, s: State, config: sbt.LoadBuildConfiguration): sbt.BuildUnit =
{ {
@ -410,15 +412,13 @@ object Load
val defDir = projectStandard(normBase) val defDir = projectStandard(normBase)
val plugs = plugins(defDir, s, config.copy(pluginManagement = config.pluginManagement.forPlugin)) val plugs = plugins(defDir, s, config.copy(pluginManagement = config.pluginManagement.forPlugin))
val defNames = analyzed(plugs.fullClasspath) flatMap findDefinitions val defsScala = plugs.detected.builds.values
val defsScala = if(defNames.isEmpty) Nil else loadDefinitions(plugs.loader, defNames)
val imports = BuildUtil.getImports(plugs.pluginNames, defNames)
lazy val eval = mkEval(plugs.classpath, defDir, plugs.pluginData.scalacOptions) lazy val eval = mkEval(plugs.classpath, defDir, plugs.pluginData.scalacOptions)
val initialProjects = defsScala.flatMap(b => projectsFromBuild(b, normBase)) val initialProjects = defsScala.flatMap(b => projectsFromBuild(b, normBase))
val memoSettings = new mutable.HashMap[File, LoadedSbtFile] val memoSettings = new mutable.HashMap[File, LoadedSbtFile]
def loadProjects(ps: Seq[Project]) = loadTransitive(ps, normBase, imports, plugs, () => eval, config.injectSettings, Nil, memoSettings) def loadProjects(ps: Seq[Project]) = loadTransitive(ps, normBase, plugs, () => eval, config.injectSettings, Nil, memoSettings)
val loadedProjectsRaw = loadProjects(initialProjects) val loadedProjectsRaw = loadProjects(initialProjects)
val hasRoot = loadedProjectsRaw.exists(_.base == normBase) || defsScala.exists(_.rootProject.isDefined) val hasRoot = loadedProjectsRaw.exists(_.base == normBase) || defsScala.exists(_.rootProject.isDefined)
val (loadedProjects, defaultBuildIfNone) = val (loadedProjects, defaultBuildIfNone) =
@ -434,7 +434,7 @@ object Load
} }
val defs = if(defsScala.isEmpty) defaultBuildIfNone :: Nil else defsScala val defs = if(defsScala.isEmpty) defaultBuildIfNone :: Nil else defsScala
val loadedDefs = new sbt.LoadedDefinitions(defDir, Nil, plugs.loader, defs, loadedProjects, defNames) val loadedDefs = new sbt.LoadedDefinitions(defDir, Nil, plugs.loader, defs, loadedProjects, plugs.detected.builds.names)
new sbt.BuildUnit(uri, normBase, loadedDefs, plugs) new sbt.BuildUnit(uri, normBase, loadedDefs, plugs)
} }
@ -460,16 +460,19 @@ object Load
private[this] def projectsFromBuild(b: Build, base: File): Seq[Project] = private[this] def projectsFromBuild(b: Build, base: File): Seq[Project] =
b.projectDefinitions(base).map(resolveBase(base)) b.projectDefinitions(base).map(resolveBase(base))
private[this] def loadTransitive(newProjects: Seq[Project], buildBase: File, imports: Seq[String], plugins: sbt.LoadedPlugins, eval: () => Eval, injectSettings: InjectSettings, acc: Seq[Project], memoSettings: mutable.Map[File, LoadedSbtFile]): Seq[Project] = private[this] def loadTransitive(newProjects: Seq[Project], buildBase: File, plugins: sbt.LoadedPlugins, eval: () => Eval, injectSettings: InjectSettings, acc: Seq[Project], memoSettings: mutable.Map[File, LoadedSbtFile]): Seq[Project] =
{ {
def loadSbtFiles(auto: AddSettings, base: File): LoadedSbtFile = def loadSbtFiles(auto: AddSettings, base: File, autoPlugins: Seq[AutoPlugin]): LoadedSbtFile =
loadSettings(auto, base, imports, plugins, eval, injectSettings, memoSettings) loadSettings(auto, base, plugins, eval, injectSettings, memoSettings, autoPlugins)
def loadForProjects = newProjects map { project => def loadForProjects = newProjects map { project =>
val loadedSbtFiles = loadSbtFiles(project.auto, project.base) val autoPlugins = plugins.detected.compileNatures(project.natures)
val transformed = project.copy(settings = (project.settings: Seq[Setting[_]]) ++ loadedSbtFiles.settings) val autoConfigs = autoPlugins.flatMap(_.projectConfigurations)
val loadedSbtFiles = loadSbtFiles(project.auto, project.base, autoPlugins)
val newSettings = (project.settings: Seq[Setting[_]]) ++ loadedSbtFiles.settings
val transformed = project.copy(settings = newSettings).setAutoPlugins(autoPlugins).overrideConfigs(autoConfigs : _*)
(transformed, loadedSbtFiles.projects) (transformed, loadedSbtFiles.projects)
} }
def defaultLoad = loadSbtFiles(AddSettings.defaultSbtFiles, buildBase).projects def defaultLoad = loadSbtFiles(AddSettings.defaultSbtFiles, buildBase, Nil).projects
val (nextProjects, loadedProjects) = val (nextProjects, loadedProjects) =
if(newProjects.isEmpty) // load the .sbt files in the root directory to look for Projects if(newProjects.isEmpty) // load the .sbt files in the root directory to look for Projects
(defaultLoad, acc) (defaultLoad, acc)
@ -481,10 +484,10 @@ object Load
if(nextProjects.isEmpty) if(nextProjects.isEmpty)
loadedProjects loadedProjects
else else
loadTransitive(nextProjects, buildBase, imports, plugins, eval, injectSettings, loadedProjects, memoSettings) loadTransitive(nextProjects, buildBase, plugins, eval, injectSettings, loadedProjects, memoSettings)
} }
private[this] def loadSettings(auto: AddSettings, projectBase: File, buildImports: Seq[String], loadedPlugins: sbt.LoadedPlugins, eval: ()=>Eval, injectSettings: InjectSettings, memoSettings: mutable.Map[File, LoadedSbtFile]): LoadedSbtFile = private[this] def loadSettings(auto: AddSettings, projectBase: File, loadedPlugins: sbt.LoadedPlugins, eval: ()=>Eval, injectSettings: InjectSettings, memoSettings: mutable.Map[File, LoadedSbtFile], autoPlugins: Seq[AutoPlugin]): LoadedSbtFile =
{ {
lazy val defaultSbtFiles = configurationSources(projectBase) lazy val defaultSbtFiles = configurationSources(projectBase)
def settings(ss: Seq[Setting[_]]) = new LoadedSbtFile(ss, Nil, Nil) def settings(ss: Seq[Setting[_]]) = new LoadedSbtFile(ss, Nil, Nil)
@ -499,14 +502,20 @@ object Load
lf lf
} }
def loadSettingsFile(src: File): LoadedSbtFile = def loadSettingsFile(src: File): LoadedSbtFile =
EvaluateConfigurations.evaluateSbtFile(eval(), src, IO.readLines(src), buildImports, 0)(loader) EvaluateConfigurations.evaluateSbtFile(eval(), src, IO.readLines(src), loadedPlugins.detected.imports, 0)(loader)
import AddSettings.{User,SbtFiles,DefaultSbtFiles,Plugins,Sequence} import AddSettings.{User,SbtFiles,DefaultSbtFiles,Plugins,Sequence}
def pluginSettings(f: Plugins) = {
val included = loadedPlugins.detected.plugins.values.filter(f.include) // don't apply the filter to AutoPlugins, only Plugins
val oldStyle = included.flatMap(p => p.settings.filter(isProjectThis) ++ p.projectSettings)
val autoStyle = autoPlugins.flatMap(_.projectSettings)
oldStyle ++ autoStyle
}
def expand(auto: AddSettings): LoadedSbtFile = auto match { def expand(auto: AddSettings): LoadedSbtFile = auto match {
case User => settings(injectSettings.projectLoaded(loader)) case User => settings(injectSettings.projectLoaded(loader))
case sf: SbtFiles => loadSettings( sf.files.map(f => IO.resolve(projectBase, f))) case sf: SbtFiles => loadSettings( sf.files.map(f => IO.resolve(projectBase, f)))
case sf: DefaultSbtFiles => loadSettings( defaultSbtFiles.filter(sf.include)) case sf: DefaultSbtFiles => loadSettings( defaultSbtFiles.filter(sf.include))
case f: Plugins => settings(loadedPlugins.plugins.filter(f.include).flatMap(p => p.settings.filter(isProjectThis) ++ p.projectSettings)) case p: Plugins => settings(pluginSettings(p))
case q: Sequence => (LoadedSbtFile.empty /: q.sequence) { (b,add) => b.merge( expand(add) ) } case q: Sequence => (LoadedSbtFile.empty /: q.sequence) { (b,add) => b.merge( expand(add) ) }
} }
expand(auto) expand(auto)
@ -599,27 +608,48 @@ object Load
config.evalPluginDef(pluginDef, pluginState) config.evalPluginDef(pluginDef, pluginState)
} }
/*
// TODO: UNCOMMENT BEFORE COMMIT
@deprecated("Use ModuleUtilities.getCheckedObjects[Build].", "0.13.2")
def loadDefinitions(loader: ClassLoader, defs: Seq[String]): Seq[Build] = def loadDefinitions(loader: ClassLoader, defs: Seq[String]): Seq[Build] =
defs map { definition => loadDefinition(loader, definition) } defs map { definition => loadDefinition(loader, definition) }
@deprecated("Use ModuleUtilities.getCheckedObject[Build].", "0.13.2")
def loadDefinition(loader: ClassLoader, definition: String): Build = def loadDefinition(loader: ClassLoader, definition: String): Build =
ModuleUtilities.getObject(definition, loader).asInstanceOf[Build] ModuleUtilities.getObject(definition, loader).asInstanceOf[Build]
*/
def loadPlugins(dir: File, data: PluginData, loader: ClassLoader): sbt.LoadedPlugins = def loadPlugins(dir: File, data: PluginData, loader: ClassLoader): sbt.LoadedPlugins =
{ {
val (pluginNames, plugins) = if(data.classpath.isEmpty) (Nil, Nil) else { // TODO: binary detection for builds, autoImports, autoPlugins
val names = getPluginNames(data.classpath, loader) import AutoBinaryResource._
val loaded = val plugins = detectModules[Plugin](data, loader, Plugins)
try loadPlugins(loader, names) val builds = detectModules[Build](data, loader, Builds)
catch { val autoImports = detectModules[AutoImport](data, loader, AutoImports)
case e: ExceptionInInitializerError => val autoPlugins = detectModules[AutoPlugin](data, loader, AutoPlugins)
val cause = e.getCause val detected = new DetectedPlugins(plugins, autoImports, autoPlugins, builds)
if(cause eq null) throw e else throw cause new sbt.LoadedPlugins(dir, data, loader, detected)
case e: LinkageError => incompatiblePlugins(data, e)
}
(names, loaded)
}
new sbt.LoadedPlugins(dir, data, loader, plugins, pluginNames)
} }
private[this] def detectModules[T](data: PluginData, loader: ClassLoader, resourceName: String)(implicit mf: reflect.ClassManifest[T]): DetectedModules[T] =
{
val classpath = data.classpath
val namesAndValues = if(classpath.isEmpty) Nil else {
val names = discoverModuleNames(classpath, loader, resourceName, mf.erasure.getName)
loadModules[T](data, names, loader)
}
new DetectedModules(namesAndValues)
}
private[this] def loadModules[T: ClassManifest](data: PluginData, names: Seq[String], loader: ClassLoader): Seq[(String,T)] =
try ModuleUtilities.getCheckedObjects[T](names, loader)
catch {
case e: ExceptionInInitializerError =>
val cause = e.getCause
if(cause eq null) throw e else throw cause
case e: LinkageError => incompatiblePlugins(data, e)
}
private[this] def incompatiblePlugins(data: PluginData, t: LinkageError): Nothing = private[this] def incompatiblePlugins(data: PluginData, t: LinkageError): Nothing =
{ {
val evicted = data.report.toList.flatMap(_.configurations.flatMap(_.evicted)) val evicted = data.report.toList.flatMap(_.configurations.flatMap(_.evicted))
@ -629,26 +659,54 @@ object Load
val msgExtra = if(evictedStrings.isEmpty) "" else "\nNote that conflicts were resolved for some dependencies:\n\t" + evictedStrings.mkString("\n\t") val msgExtra = if(evictedStrings.isEmpty) "" else "\nNote that conflicts were resolved for some dependencies:\n\t" + evictedStrings.mkString("\n\t")
throw new IncompatiblePluginsException(msgBase + msgExtra, t) throw new IncompatiblePluginsException(msgBase + msgExtra, t)
} }
def getPluginNames(classpath: Seq[Attributed[File]], loader: ClassLoader): Seq[String] =
( binaryPlugins(data(classpath), loader) ++ (analyzed(classpath) flatMap findPlugins) ).distinct
def discoverModuleNames(classpath: Seq[Attributed[File]], loader: ClassLoader, resourceName: String, moduleTypes: String*): Seq[String] =
(
binaryPlugins(data(classpath), loader, resourceName) ++
(analyzed(classpath) flatMap (a => discover(a, moduleTypes : _*)))
).distinct
@deprecated("Replaced by the more general discoverModuleNames and will be made private.", "0.13.2")
def getPluginNames(classpath: Seq[Attributed[File]], loader: ClassLoader): Seq[String] =
discoverModuleNames(classpath, loader, AutoBinaryResource.Plugins, classOf[Plugin].getName)
/*
TODO: UNCOMMENT BEFORE COMMIT
@deprecated("Explicitly specify the resource name.", "0.13.2")
def binaryPlugins(classpath: Seq[File], loader: ClassLoader): Seq[String] = def binaryPlugins(classpath: Seq[File], loader: ClassLoader): Seq[String] =
binaryPlugins(classpath, loader, AutoBinaryResource.Plugins)
*/
object AutoBinaryResource {
final val AutoPlugins = "sbt/sbt.autoplugins"
final val Plugins = "sbt/sbt.plugins"
final val Builds = "sbt/sbt.builds"
final val AutoImports = "sbt/sbt.autoimports"
}
def binaryPlugins(classpath: Seq[File], loader: ClassLoader, resourceName: String): Seq[String] =
{ {
import collection.JavaConversions._ import collection.JavaConversions._
loader.getResources("sbt/sbt.plugins").toSeq.filter(onClasspath(classpath)) flatMap { u => loader.getResources(resourceName).toSeq.filter(onClasspath(classpath)) flatMap { u =>
IO.readLinesURL(u).map( _.trim).filter(!_.isEmpty) IO.readLinesURL(u).map( _.trim).filter(!_.isEmpty)
} }
} }
def onClasspath(classpath: Seq[File])(url: URL): Boolean = def onClasspath(classpath: Seq[File])(url: URL): Boolean =
IO.urlAsFile(url) exists (classpath.contains _) IO.urlAsFile(url) exists (classpath.contains _)
/*
// TODO: UNCOMMENT BEFORE COMMIT
@deprecated("Use ModuleUtilities.getCheckedObjects[Plugin].", "0.13.2")
def loadPlugins(loader: ClassLoader, pluginNames: Seq[String]): Seq[Plugin] = def loadPlugins(loader: ClassLoader, pluginNames: Seq[String]): Seq[Plugin] =
pluginNames.map(pluginName => loadPlugin(pluginName, loader)) ModuleUtilities.getCheckedObjects[Plugin](loader, pluginNames)
@deprecated("Use ModuleUtilities.getCheckedObject[Plugin].", "0.13.2")
def loadPlugin(pluginName: String, loader: ClassLoader): Plugin = def loadPlugin(pluginName: String, loader: ClassLoader): Plugin =
ModuleUtilities.getObject(pluginName, loader).asInstanceOf[Plugin] ModuleUtilities.getCheckedObject[Plugin](pluginName, loader)
@deprecated("No longer used.", "0.13.2")
def findPlugins(analysis: inc.Analysis): Seq[String] = discover(analysis, "sbt.Plugin") def findPlugins(analysis: inc.Analysis): Seq[String] = discover(analysis, "sbt.Plugin")
*/
def findDefinitions(analysis: inc.Analysis): Seq[String] = discover(analysis, "sbt.Build") def findDefinitions(analysis: inc.Analysis): Seq[String] = discover(analysis, "sbt.Build")
def discover(analysis: inc.Analysis, subclasses: String*): Seq[String] = def discover(analysis: inc.Analysis, subclasses: String*): Seq[String] =
{ {

View File

@ -125,7 +125,8 @@ object BuiltinCommands
def aboutPlugins(e: Extracted): String = def aboutPlugins(e: Extracted): String =
{ {
val allPluginNames = e.structure.units.values.flatMap(_.unit.plugins.pluginNames).toSeq.distinct def list(b: BuildUnit) = b.plugins.detected.autoPlugins.values.map(_.provides) ++ b.plugins.detected.plugins.names
val allPluginNames = e.structure.units.values.flatMap(u => list(u.unit)).toSeq.distinct
if(allPluginNames.isEmpty) "" else allPluginNames.mkString("Available Plugins: ", ", ", "") if(allPluginNames.isEmpty) "" else allPluginNames.mkString("Available Plugins: ", ", ", "")
} }
def aboutScala(s: State, e: Extracted): String = def aboutScala(s: State, e: Extracted): String =

View File

@ -50,33 +50,52 @@ sealed trait ProjectDefinition[PR <: ProjectReference]
/** Configures the sources of automatically appended settings.*/ /** Configures the sources of automatically appended settings.*/
def auto: AddSettings 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 [[AutoPlugin]]s enabled for this project. This value is only available on a loaded Project. */
private[sbt] def autoPlugins: Seq[AutoPlugin]
override final def hashCode: Int = id.hashCode ^ base.hashCode ^ getClass.hashCode override final def hashCode: Int = id.hashCode ^ base.hashCode ^ getClass.hashCode
override final def equals(o: Any) = o match { override final def equals(o: Any) = o match {
case p: ProjectDefinition[_] => p.getClass == this.getClass && p.id == id && p.base == base case p: ProjectDefinition[_] => p.getClass == this.getClass && p.id == id && p.base == base
case _ => false case _ => false
} }
override def toString = "Project(id: " + id + ", base: " + base + ", aggregate: " + aggregate + ", dependencies: " + dependencies + ", configurations: " + configurations + ")" override def toString =
{
val agg = ifNonEmpty("aggregate", aggregate)
val dep = ifNonEmpty("dependencies", dependencies)
val conf = ifNonEmpty("configurations", configurations)
val autos = ifNonEmpty("autoPlugins", autoPlugins.map(_.provides))
val fields = s"id $id" :: s"base: $base" :: agg ::: dep ::: conf ::: (s"natures: List($natures)" :: 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] 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)
def copy(id: String = id, base: File = base, aggregate: => Seq[ProjectReference] = aggregate, dependencies: => Seq[ClasspathDep[ProjectReference]] = dependencies, 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, delegates: => Seq[ProjectReference] = delegates, settings: => Seq[Setting[_]] = settings, configurations: Seq[Configuration] = configurations,
auto: AddSettings = auto): Project = auto: AddSettings = auto): Project =
Project(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto) unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, natures, autoPlugins)
def resolve(resolveRef: ProjectReference => ProjectRef): ResolvedProject = def resolve(resolveRef: ProjectReference => ProjectRef): ResolvedProject =
{ {
def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef
def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep
def resolveDep(d: ClasspathDep[ProjectReference]) = ResolvedClasspathDependency(resolveRef(d.project), d.configuration) 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) resolved(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates),
settings, configurations, auto, natures, autoPlugins)
} }
def resolveBuild(resolveRef: ProjectReference => ProjectReference): Project = def resolveBuild(resolveRef: ProjectReference => ProjectReference): Project =
{ {
def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef
def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep
def resolveDep(d: ClasspathDep[ProjectReference]) = ClasspathDependency(resolveRef(d.project), d.configuration) def resolveDep(d: ClasspathDep[ProjectReference]) = ClasspathDependency(resolveRef(d.project), d.configuration)
apply(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates), settings, configurations, auto) unresolved(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates),
settings, configurations, auto, natures, autoPlugins)
} }
/** Applies the given functions to this Project. /** Applies the given functions to this Project.
@ -116,8 +135,24 @@ sealed trait Project extends ProjectDefinition[ProjectReference]
/** Sets the list of .sbt files to parse for settings to be appended to this project's settings. /** Sets the list of .sbt files to parse for settings to be appended to this project's settings.
* Any configured .sbt files are removed from this project's list.*/ * 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: _*)) ) def setSbtFiles(files: File*): Project = copy(auto = AddSettings.append( AddSettings.clearSbtFiles(auto), AddSettings.sbtFiles(files: _*)) )
/** Sets the [[Natures]] 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: Natures): Project = {
// TODO: for 0.14.0, use copy when it has the additional `natures` parameter
unresolved(id, base, aggregate = aggregate, dependencies = dependencies, delegates = delegates, settings, configurations, auto, Natures.and(natures, 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)
}
}
sealed trait ResolvedProject extends ProjectDefinition[ProjectRef] {
/** The [[AutoPlugin]]s enabled for this project as computed from [[natures]].*/
def autoPlugins: Seq[AutoPlugin]
} }
sealed trait ResolvedProject extends ProjectDefinition[ProjectRef]
sealed trait ClasspathDep[PR <: ProjectReference] { def project: PR; def configuration: Option[String] } sealed trait ClasspathDep[PR <: ProjectReference] { def project: PR; def configuration: Option[String] }
final case class ResolvedClasspathDependency(project: ProjectRef, configuration: Option[String]) extends ClasspathDep[ProjectRef] final case class ResolvedClasspathDependency(project: ProjectRef, configuration: Option[String]) extends ClasspathDep[ProjectRef]
@ -150,7 +185,8 @@ object Project extends ProjectExtra
Def.showRelativeKey( ProjectRef(loaded.root, loaded.units(loaded.root).rootProjects.head), loaded.allProjectRefs.size > 1, keyNameColor) Def.showRelativeKey( ProjectRef(loaded.root, loaded.units(loaded.root).rootProjects.head), loaded.allProjectRefs.size > 1, keyNameColor)
private abstract class ProjectDef[PR <: ProjectReference](val id: String, val base: File, aggregate0: => Seq[PR], dependencies0: => Seq[ClasspathDep[PR]], 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) extends ProjectDefinition[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]
{ {
lazy val aggregate = aggregate0 lazy val aggregate = aggregate0
lazy val dependencies = dependencies0 lazy val dependencies = dependencies0
@ -160,13 +196,11 @@ object Project extends ProjectExtra
Dag.topologicalSort(configurations)(_.extendsConfigs) // checks for cyclic references here instead of having to do it in Scope.delegates 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
def apply(id: String, base: File, aggregate: => Seq[ProjectReference] = Nil, dependencies: => Seq[ClasspathDep[ProjectReference]] = Nil, 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, delegates: => Seq[ProjectReference] = Nil, settings: => Seq[Def.Setting[_]] = defaultSettings, configurations: Seq[Configuration] = Configurations.default,
auto: AddSettings = AddSettings.allDefaults): Project = auto: AddSettings = AddSettings.allDefaults): Project =
{ unresolved(id, base, aggregate, dependencies, delegates, settings, configurations, auto, Natures.empty, Nil)
validProjectID(id).foreach(errMsg => sys.error("Invalid project ID: " + errMsg))
new ProjectDef[ProjectReference](id, base, aggregate, dependencies, delegates, settings, configurations, auto) with Project
}
/** Returns None if `id` is a valid Project ID or Some containing the parser error message if it is not.*/ /** 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 def validProjectID(id: String): Option[String] = DefaultParsers.parse(id, DefaultParsers.ID).left.toOption
@ -185,9 +219,23 @@ object Project extends ProjectExtra
* This is a best effort implementation, since valid characters are not documented or consistent.*/ * This is a best effort implementation, since valid characters are not documented or consistent.*/
def normalizeModuleID(id: String): String = normalizeBase(id) def normalizeModuleID(id: String): String = normalizeBase(id)
@deprecated("Will be removed.", "0.13.2")
def resolved(id: String, base: File, aggregate: => Seq[ProjectRef], dependencies: => Seq[ResolvedClasspathDependency], delegates: => Seq[ProjectRef], 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 = settings: Seq[Def.Setting[_]], configurations: Seq[Configuration], auto: AddSettings): ResolvedProject =
new ProjectDef[ProjectRef](id, base, aggregate, dependencies, delegates, settings, configurations, auto) with ResolvedProject resolved(id, base, aggregate, dependencies, delegates, settings, configurations, auto, Natures.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
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 =
{
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
}
def defaultSettings: Seq[Def.Setting[_]] = Defaults.defaultSettings def defaultSettings: Seq[Def.Setting[_]] = Defaults.defaultSettings

View File

@ -73,7 +73,7 @@ object Sbt extends Build
lazy val datatypeSub = baseProject(utilPath /"datatype", "Datatype Generator") dependsOn(ioSub) lazy val datatypeSub = baseProject(utilPath /"datatype", "Datatype Generator") dependsOn(ioSub)
// cross versioning // cross versioning
lazy val crossSub = baseProject(utilPath / "cross", "Cross") settings(inConfig(Compile)(Transform.crossGenSettings): _*) lazy val crossSub = baseProject(utilPath / "cross", "Cross") settings(inConfig(Compile)(Transform.crossGenSettings): _*)
// A monotonic logic that includes restricted negation as failure // A logic with restricted negation as failure for a unique, stable model
lazy val logicSub = baseProject(utilPath / "logic", "Logic").dependsOn(collectionSub, relationSub) lazy val logicSub = baseProject(utilPath / "logic", "Logic").dependsOn(collectionSub, relationSub)
/* **** Intermediate-level Modules **** */ /* **** Intermediate-level Modules **** */

View File

@ -0,0 +1,34 @@
// !C will exclude C, and thus D, from being auto-added
lazy val a = project.addNatures(A && B && !C)
// without B, C is not added
lazy val b = project.addNatures(A)
// with both A and B, C is selected, which in turn selects D
lazy val c = project.addNatures(A && B)
// with no natures defined, nothing is auto-added
lazy val d = project
check := {
val ddel = (del in d).?.value // should be None
same(ddel, None, "del in d")
val bdel = (del in b).?.value // should be None
same(bdel, None, "del in b")
val adel = (del in a).?.value // should be None
same(adel, None, "del in a")
//
val buildValue = (demo in ThisBuild).value
same(buildValue, "build 0", "demo in ThisBuild")
val globalValue = (demo in Global).value
same(globalValue, "global 0", "demo in Global")
val projValue = (demo in c).value
same(projValue, "project c Q R", "demo in c")
val qValue = (del in c in q).value
same(qValue, " Q R", "del in c in q")
}
def same[T](actual: T, expected: T, label: String) {
assert(actual == expected, s"Expected '$expected' for `$label`, got '$actual'")
}

View File

@ -0,0 +1,65 @@
import sbt._
import sbt.Keys.{name, resolvedScoped}
import java.util.concurrent.atomic.{AtomicInteger => AInt}
object AI extends AutoImport
{
lazy val A = Nature("A")
lazy val B = Nature("B")
lazy val C = Nature("C")
lazy val D = Nature("D")
lazy val E = Nature("E")
lazy val q = config("q")
lazy val p = config("p").extend(q)
lazy val demo = settingKey[String]("A demo setting.")
lazy val del = settingKey[String]("Another demo setting.")
lazy val check = settingKey[Unit]("Verifies settings are as they should be.")
}
import AI._
object Q extends AutoPlugin
{
def select: Natures = A && B
def provides = C
override def projectConfigurations: Seq[Configuration] =
p ::
q ::
Nil
override def projectSettings: Seq[Setting[_]] =
(demo := s"project ${name.value}") ::
(del in q := " Q") ::
Nil
override def buildSettings: Seq[Setting[_]] =
(demo := s"build ${buildCount.getAndIncrement}") ::
Nil
override def globalSettings: Seq[Setting[_]] =
(demo := s"global ${globalCount.getAndIncrement}") ::
Nil
// used to ensure the build-level and global settings are only added once
private[this] val buildCount = new AInt(0)
private[this] val globalCount = new AInt(0)
}
object R extends AutoPlugin
{
def select = C && !D
def provides = E
override def projectSettings = Seq(
// tests proper ordering: R requires C, so C settings should come first
del in q += " R",
// tests that configurations are properly registered, enabling delegation from p to q
demo += (del in p).value
)
}

View File

@ -0,0 +1 @@
> check

View File

@ -14,4 +14,10 @@ object ModuleUtilities
val singletonField = obj.getField("MODULE$") val singletonField = obj.getField("MODULE$")
singletonField.get(null) singletonField.get(null)
} }
def getCheckedObject[T](className: String, loader: ClassLoader)(implicit mf: reflect.ClassManifest[T]): T =
mf.erasure.cast(getObject(className, loader)).asInstanceOf[T]
def getCheckedObjects[T](classNames: Seq[String], loader: ClassLoader)(implicit mf: reflect.ClassManifest[T]): Seq[(String,T)] =
classNames.map(name => (name, getCheckedObject(name, loader)))
} }