mirror of https://github.com/sbt/sbt.git
Main part of integrating natures into project loading.
This commit is contained in:
parent
322f6de655
commit
b8619f4aae
|
|
@ -4,6 +4,9 @@ package sbt
|
|||
import Def.Setting
|
||||
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").
|
||||
The `select` method defines the conditions,
|
||||
|
|
@ -59,15 +62,15 @@ abstract class 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
|
||||
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
|
||||
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
|
||||
def globalSettings: Seq[Setting[_]] = Nil
|
||||
|
||||
// TODO?: def commands: Seq[Command]
|
||||
}
|
||||
|
|
@ -82,19 +85,30 @@ sealed trait Natures {
|
|||
final case class Nature(label: String) extends Basic {
|
||||
/** Constructs a Natures matcher that excludes this Nature. */
|
||||
def unary_! : Basic = Exclude(this)
|
||||
override def toString = label
|
||||
}
|
||||
|
||||
object Natures
|
||||
{
|
||||
// TODO: allow multiple AutoPlugins to provide the same Nature?
|
||||
// TODO: translate error messages
|
||||
/** Select the AutoPlugins to include according to the user-specified natures in `requested` and all discovered AutoPlugins in `defined`.*/
|
||||
def evaluate(requested: Natures, defined: List[AutoPlugin]): Seq[AutoPlugin] =
|
||||
{
|
||||
val byAtom = defined.map(x => (Atom(x.provides.label), x)).toMap
|
||||
val clauses = Clauses( defined.map(d => asClause(d)) )
|
||||
val results = Logic.reduce(clauses, flatten(requested).toSet)
|
||||
results.ordered.map(byAtom)
|
||||
def compile(defined: List[AutoPlugin]): Natures => Seq[AutoPlugin] =
|
||||
if(defined.isEmpty)
|
||||
Types.const(Nil)
|
||||
else
|
||||
{
|
||||
val byAtom = defined.map(x => (Atom(x.provides.label), x)).toMap
|
||||
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. */
|
||||
|
|
@ -103,9 +117,16 @@ object Natures
|
|||
}
|
||||
private[sbt] final case class Exclude(n: Nature) extends Basic {
|
||||
def unary_! : Nature = n
|
||||
override def toString = s"!$n"
|
||||
}
|
||||
private[sbt] final case class And(natures: List[Basic]) extends Natures {
|
||||
def &&(o: Basic): Natures = And(o :: natures)
|
||||
override def toString = natures.mkString(", ")
|
||||
}
|
||||
private[sbt] 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 =
|
||||
|
|
@ -114,11 +135,13 @@ object Natures
|
|||
private[this] def flatten(n: Natures): Seq[Literal] = n match {
|
||||
case And(ns) => convertAll(ns)
|
||||
case b: Basic => convertBasic(b) :: Nil
|
||||
case Empty => Nil
|
||||
}
|
||||
|
||||
private[this] def convert(n: Natures): 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)
|
||||
|
|
|
|||
|
|
@ -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.*/
|
||||
def rootProject: Option[Project] = None
|
||||
}
|
||||
// TODO 0.14.0: decide if Plugin should be deprecated in favor of AutoPlugin
|
||||
trait Plugin
|
||||
{
|
||||
@deprecated("Override projectSettings or buildSettings instead.", "0.12.0")
|
||||
|
|
|
|||
|
|
@ -41,11 +41,35 @@ final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, Resolv
|
|||
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 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 classpath = data(fullClasspath)
|
||||
|
||||
}
|
||||
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)
|
||||
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)
|
||||
private[sbt] def autos = GroupedAutoPlugins(units)
|
||||
}
|
||||
final class PartBuild(val root: URI, val units: Map[URI, PartBuildUnit])
|
||||
sealed trait BuildUnitBase { def rootProjects: Seq[String]; def buildSettings: Seq[Setting[_]] }
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ final class BuildUtil[Proj](
|
|||
case _ => None
|
||||
}
|
||||
|
||||
val configurationsForAxis: Option[ResolvedReference] => Seq[String] =
|
||||
val configurationsForAxis: Option[ResolvedReference] => Seq[String] =
|
||||
refOpt => configurations(projectForAxis(refOpt)).map(_.name)
|
||||
}
|
||||
object BuildUtil
|
||||
|
|
@ -60,8 +60,14 @@ object BuildUtil
|
|||
}
|
||||
}
|
||||
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 importAllRoot(values: Seq[String]): Seq[String] = importAll(values map rootedName)
|
||||
def rootedName(s: String): String = if(s contains '.') "_root_." + s else s
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -180,7 +180,7 @@ object Load
|
|||
val keys = Index.allKeys(settings)
|
||||
val attributeKeys = Index.attributeKeys(data) ++ keys.map(_.key)
|
||||
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 aggIndex = KeyIndex.aggregate(scopedKeys, extra(keyIndex), projectsMap)
|
||||
new sbt.StructureIndex(Index.stringToKeyMap(attributeKeys), Index.taskToKeyMap(data), Index.triggers(data), keyIndex, aggIndex)
|
||||
|
|
@ -201,10 +201,10 @@ object Load
|
|||
{
|
||||
((loadedBuild in GlobalScope :== loaded) +:
|
||||
transformProjectOnly(loaded.root, rootProject, injectSettings.global)) ++
|
||||
inScope(GlobalScope)( pluginGlobalSettings(loaded) ) ++
|
||||
inScope(GlobalScope)( pluginGlobalSettings(loaded) ++ loaded.autos.globalSettings ) ++
|
||||
loaded.units.toSeq.flatMap { case (uri, build) =>
|
||||
val plugins = build.unit.plugins.plugins
|
||||
val pluginBuildSettings = plugins.flatMap(_.buildSettings)
|
||||
val plugins = build.unit.plugins.detected.plugins.values
|
||||
val pluginBuildSettings = plugins.flatMap(_.buildSettings) ++ loaded.autos.buildSettings(uri)
|
||||
val pluginNotThis = plugins.flatMap(_.settings) filterNot isProjectThis
|
||||
val projectSettings = build.defined flatMap { case (id, project) =>
|
||||
val ref = ProjectRef(uri, id)
|
||||
|
|
@ -220,9 +220,10 @@ object Load
|
|||
buildSettings ++ projectSettings
|
||||
}
|
||||
}
|
||||
@deprecated("Does not account for AutoPlugins and will be made private.", "0.13.2")
|
||||
def pluginGlobalSettings(loaded: sbt.LoadedBuild): Seq[Setting[_]] =
|
||||
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")
|
||||
|
|
@ -368,10 +369,11 @@ object Load
|
|||
def resolveProjects(loaded: sbt.PartBuild): sbt.LoadedBuild =
|
||||
{
|
||||
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)
|
||||
(uri, resolveProjects(uri, unit, rootProject))
|
||||
})
|
||||
}
|
||||
new sbt.LoadedBuild(loaded.root, units)
|
||||
}
|
||||
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 =
|
||||
map.getOrElse(uri, noBuild(uri))
|
||||
|
||||
def emptyBuild(uri: URI) = sys.error("No root project defined for build unit '" + uri + "'")
|
||||
def noBuild(uri: URI) = sys.error("Build unit '" + uri + "' not defined.")
|
||||
def noProject(uri: URI, id: String) = sys.error("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 emptyBuild(uri: URI) = sys.error(s"No root project defined for build unit '$uri'")
|
||||
def noBuild(uri: URI) = sys.error(s"Build unit '$uri' not defined.")
|
||||
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(s"No configuration '$conf' defined in project '$id' in '$uri'")
|
||||
|
||||
def loadUnit(uri: URI, localBase: File, s: State, config: sbt.LoadBuildConfiguration): sbt.BuildUnit =
|
||||
{
|
||||
|
|
@ -410,15 +412,13 @@ object Load
|
|||
val defDir = projectStandard(normBase)
|
||||
|
||||
val plugs = plugins(defDir, s, config.copy(pluginManagement = config.pluginManagement.forPlugin))
|
||||
val defNames = analyzed(plugs.fullClasspath) flatMap findDefinitions
|
||||
val defsScala = if(defNames.isEmpty) Nil else loadDefinitions(plugs.loader, defNames)
|
||||
val imports = BuildUtil.getImports(plugs.pluginNames, defNames)
|
||||
val defsScala = plugs.detected.builds.values
|
||||
|
||||
lazy val eval = mkEval(plugs.classpath, defDir, plugs.pluginData.scalacOptions)
|
||||
val initialProjects = defsScala.flatMap(b => projectsFromBuild(b, normBase))
|
||||
|
||||
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 hasRoot = loadedProjectsRaw.exists(_.base == normBase) || defsScala.exists(_.rootProject.isDefined)
|
||||
val (loadedProjects, defaultBuildIfNone) =
|
||||
|
|
@ -434,7 +434,7 @@ object Load
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -460,16 +460,19 @@ object Load
|
|||
private[this] def projectsFromBuild(b: Build, base: File): Seq[Project] =
|
||||
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 =
|
||||
loadSettings(auto, base, imports, plugins, eval, injectSettings, memoSettings)
|
||||
def loadSbtFiles(auto: AddSettings, base: File, autoPlugins: Seq[AutoPlugin]): LoadedSbtFile =
|
||||
loadSettings(auto, base, plugins, eval, injectSettings, memoSettings, autoPlugins)
|
||||
def loadForProjects = newProjects map { project =>
|
||||
val loadedSbtFiles = loadSbtFiles(project.auto, project.base)
|
||||
val transformed = project.copy(settings = (project.settings: Seq[Setting[_]]) ++ loadedSbtFiles.settings)
|
||||
val autoPlugins = plugins.detected.compileNatures(project.natures)
|
||||
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)
|
||||
}
|
||||
def defaultLoad = loadSbtFiles(AddSettings.defaultSbtFiles, buildBase).projects
|
||||
def defaultLoad = loadSbtFiles(AddSettings.defaultSbtFiles, buildBase, Nil).projects
|
||||
val (nextProjects, loadedProjects) =
|
||||
if(newProjects.isEmpty) // load the .sbt files in the root directory to look for Projects
|
||||
(defaultLoad, acc)
|
||||
|
|
@ -481,10 +484,10 @@ object Load
|
|||
if(nextProjects.isEmpty)
|
||||
loadedProjects
|
||||
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)
|
||||
def settings(ss: Seq[Setting[_]]) = new LoadedSbtFile(ss, Nil, Nil)
|
||||
|
|
@ -499,14 +502,20 @@ object Load
|
|||
lf
|
||||
}
|
||||
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}
|
||||
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 {
|
||||
case User => settings(injectSettings.projectLoaded(loader))
|
||||
case sf: SbtFiles => loadSettings( sf.files.map(f => IO.resolve(projectBase, f)))
|
||||
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) ) }
|
||||
}
|
||||
expand(auto)
|
||||
|
|
@ -599,27 +608,48 @@ object Load
|
|||
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] =
|
||||
defs map { definition => loadDefinition(loader, definition) }
|
||||
|
||||
@deprecated("Use ModuleUtilities.getCheckedObject[Build].", "0.13.2")
|
||||
def loadDefinition(loader: ClassLoader, definition: String): Build =
|
||||
ModuleUtilities.getObject(definition, loader).asInstanceOf[Build]
|
||||
*/
|
||||
|
||||
def loadPlugins(dir: File, data: PluginData, loader: ClassLoader): sbt.LoadedPlugins =
|
||||
{
|
||||
val (pluginNames, plugins) = if(data.classpath.isEmpty) (Nil, Nil) else {
|
||||
val names = getPluginNames(data.classpath, loader)
|
||||
val loaded =
|
||||
try loadPlugins(loader, names)
|
||||
catch {
|
||||
case e: ExceptionInInitializerError =>
|
||||
val cause = e.getCause
|
||||
if(cause eq null) throw e else throw cause
|
||||
case e: LinkageError => incompatiblePlugins(data, e)
|
||||
}
|
||||
(names, loaded)
|
||||
}
|
||||
new sbt.LoadedPlugins(dir, data, loader, plugins, pluginNames)
|
||||
// TODO: binary detection for builds, autoImports, autoPlugins
|
||||
import AutoBinaryResource._
|
||||
val plugins = detectModules[Plugin](data, loader, Plugins)
|
||||
val builds = detectModules[Build](data, loader, Builds)
|
||||
val autoImports = detectModules[AutoImport](data, loader, AutoImports)
|
||||
val autoPlugins = detectModules[AutoPlugin](data, loader, AutoPlugins)
|
||||
val detected = new DetectedPlugins(plugins, autoImports, autoPlugins, builds)
|
||||
new sbt.LoadedPlugins(dir, data, loader, detected)
|
||||
}
|
||||
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 =
|
||||
{
|
||||
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")
|
||||
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] =
|
||||
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._
|
||||
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)
|
||||
}
|
||||
}
|
||||
def onClasspath(classpath: Seq[File])(url: URL): Boolean =
|
||||
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] =
|
||||
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 =
|
||||
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 findDefinitions(analysis: inc.Analysis): Seq[String] = discover(analysis, "sbt.Build")
|
||||
def discover(analysis: inc.Analysis, subclasses: String*): Seq[String] =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -125,7 +125,8 @@ object BuiltinCommands
|
|||
|
||||
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: ", ", ", "")
|
||||
}
|
||||
def aboutScala(s: State, e: Extracted): String =
|
||||
|
|
|
|||
|
|
@ -50,33 +50,52 @@ sealed trait ProjectDefinition[PR <: ProjectReference]
|
|||
/** Configures the sources of automatically appended settings.*/
|
||||
def auto: AddSettings
|
||||
|
||||
/** The [[Natures]] associated with this project.
|
||||
A [[Nature]] is a common label that is used by plugins to determine what settings, if any, to add to a project. */
|
||||
def natures: Natures
|
||||
|
||||
/** The [[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 equals(o: Any) = o match {
|
||||
case p: ProjectDefinition[_] => p.getClass == this.getClass && p.id == id && p.base == base
|
||||
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]
|
||||
{
|
||||
// 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,
|
||||
delegates: => Seq[ProjectReference] = delegates, settings: => Seq[Setting[_]] = settings, configurations: Seq[Configuration] = configurations,
|
||||
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 resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef
|
||||
def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep
|
||||
def resolveDep(d: ClasspathDep[ProjectReference]) = ResolvedClasspathDependency(resolveRef(d.project), d.configuration)
|
||||
resolved(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates), settings, configurations, auto)
|
||||
resolved(id, base, aggregate = resolveRefs(aggregate), dependencies = resolveDeps(dependencies), delegates = resolveRefs(delegates),
|
||||
settings, configurations, auto, natures, autoPlugins)
|
||||
}
|
||||
def resolveBuild(resolveRef: ProjectReference => ProjectReference): Project =
|
||||
{
|
||||
def resolveRefs(prs: Seq[ProjectReference]) = prs map resolveRef
|
||||
def resolveDeps(ds: Seq[ClasspathDep[ProjectReference]]) = ds map resolveDep
|
||||
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.
|
||||
|
|
@ -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.
|
||||
* Any configured .sbt files are removed from this project's list.*/
|
||||
def setSbtFiles(files: File*): Project = copy(auto = AddSettings.append( AddSettings.clearSbtFiles(auto), AddSettings.sbtFiles(files: _*)) )
|
||||
|
||||
/** Sets the [[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] }
|
||||
final case class ResolvedClasspathDependency(project: ProjectRef, configuration: Option[String]) extends ClasspathDep[ProjectRef]
|
||||
|
|
@ -150,23 +185,22 @@ object Project extends ProjectExtra
|
|||
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]],
|
||||
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 dependencies = dependencies0
|
||||
lazy val delegates = delegates0
|
||||
lazy val settings = settings0
|
||||
|
||||
|
||||
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,
|
||||
delegates: => Seq[ProjectReference] = Nil, settings: => Seq[Def.Setting[_]] = defaultSettings, configurations: Seq[Configuration] = Configurations.default,
|
||||
auto: AddSettings = AddSettings.allDefaults): Project =
|
||||
{
|
||||
validProjectID(id).foreach(errMsg => sys.error("Invalid project ID: " + errMsg))
|
||||
new ProjectDef[ProjectReference](id, base, aggregate, dependencies, delegates, settings, configurations, auto) with Project
|
||||
}
|
||||
unresolved(id, base, aggregate, dependencies, delegates, settings, configurations, auto, Natures.empty, Nil)
|
||||
|
||||
/** Returns None if `id` is a valid Project ID or Some containing the parser error message if it is not.*/
|
||||
def validProjectID(id: String): Option[String] = DefaultParsers.parse(id, DefaultParsers.ID).left.toOption
|
||||
|
|
@ -185,9 +219,23 @@ object Project extends ProjectExtra
|
|||
* This is a best effort implementation, since valid characters are not documented or consistent.*/
|
||||
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],
|
||||
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
|
||||
|
||||
|
|
@ -307,7 +355,7 @@ object Project extends ProjectExtra
|
|||
def details(structure: BuildStructure, actual: Boolean, scope: Scope, key: AttributeKey[_])(implicit display: Show[ScopedKey[_]]): String =
|
||||
{
|
||||
val scoped = ScopedKey(scope,key)
|
||||
|
||||
|
||||
val data = scopedKeyData(structure, scope, key) map {_.description} getOrElse {"No entry for key."}
|
||||
val description = key.description match { case Some(desc) => "Description:\n\t" + desc + "\n"; case None => "" }
|
||||
|
||||
|
|
@ -413,7 +461,7 @@ object Project extends ProjectExtra
|
|||
import DefaultParsers._
|
||||
|
||||
val loadActionParser = token(Space ~> ("plugins" ^^^ Plugins | "return" ^^^ Return)) ?? Current
|
||||
|
||||
|
||||
val ProjectReturn = AttributeKey[List[File]]("project-return", "Maintains a stack of builds visited using reload.")
|
||||
def projectReturn(s: State): List[File] = getOrNil(s, ProjectReturn)
|
||||
def inPluginProject(s: State): Boolean = projectReturn(s).toList.length > 1
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ object Sbt extends Build
|
|||
lazy val datatypeSub = baseProject(utilPath /"datatype", "Datatype Generator") dependsOn(ioSub)
|
||||
// cross versioning
|
||||
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)
|
||||
|
||||
/* **** Intermediate-level Modules **** */
|
||||
|
|
|
|||
|
|
@ -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'")
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
> check
|
||||
|
|
@ -6,7 +6,7 @@ package sbt
|
|||
object ModuleUtilities
|
||||
{
|
||||
/** Reflectively loads and returns the companion object for top-level class `className` from `loader`.
|
||||
* The class name should not include the `$` that scalac appends to the underlying jvm class for
|
||||
* The class name should not include the `$` that scalac appends to the underlying jvm class for
|
||||
* a companion object. */
|
||||
def getObject(className: String, loader: ClassLoader): AnyRef =
|
||||
{
|
||||
|
|
@ -14,4 +14,10 @@ object ModuleUtilities
|
|||
val singletonField = obj.getField("MODULE$")
|
||||
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)))
|
||||
}
|
||||
Loading…
Reference in New Issue