API documentation and comments related to natures

This commit is contained in:
Mark Harrah 2014-01-24 14:19:18 -05:00
parent b8619f4aae
commit 30658f98bb
4 changed files with 79 additions and 7 deletions

View File

@ -16,7 +16,7 @@ The `select` method defines the conditions,
Steps for plugin authors:
1. Determine the natures that, when present (or absent), activate the AutoPlugin.
2. Determine the settings/configurations to automatically inject when activated.
3. Define a new, unique identifying [[Nature]] (which is a wrapper around a String ID).
3. Define a new, unique identifying [[Nature]], which is a wrapper around a String ID.
For example, the following will automatically add the settings in `projectSettings`
to a project that has both the `Web` and `Javascript` natures enabled. It will itself
@ -26,7 +26,7 @@ For example, the following will automatically add the settings in `projectSettin
object MyPlugin extends AutoPlugin {
def select = Web && Javascript
def provides = MyStuff
def projectSettings = Seq(...)
override def projectSettings = Seq(...)
}
Steps for users:
@ -92,6 +92,8 @@ object Natures
{
// TODO: allow multiple AutoPlugins to provide the same Nature?
// TODO: translate error messages
/** Given the available auto plugins `defined`, returns a function that selects [[AutoPlugin]]s for the provided [[Nature]]s.
* The [[AutoPlugin]]s are topologically sorted so that a selected [[AutoPlugin]] comes before its selecting [[AutoPlugin]].*/
def compile(defined: List[AutoPlugin]): Natures => Seq[AutoPlugin] =
if(defined.isEmpty)
Types.const(Nil)
@ -101,10 +103,13 @@ object Natures
val clauses = Clauses( defined.map(d => asClause(d)) )
requestedNatures => {
val results = Logic.reduce(clauses, flatten(requestedNatures).toSet)
// results includes the originally requested (positive) atoms,
// which won't have a corresponding AutoPlugin to map back to
results.ordered.flatMap(a => byAtom.get(a).toList)
}
}
/** [[Natures]] instance that doesn't require any [[Nature]]s. */
def empty: Natures = Empty
private[sbt] final object Empty extends Natures {
def &&(o: Basic): Natures = o
@ -129,6 +134,7 @@ object Natures
case b: Basic => a && b
}
/** Defines a clause for `ap` such that the [[Nature]] provided by `ap` is the head and the selector for `ap` is the body. */
private[sbt] def asClause(ap: AutoPlugin): Clause =
Clause( convert(ap.select), Set(Atom(ap.provides.label)) )

View File

@ -30,30 +30,86 @@ final class StructureIndex(
val keyIndex: KeyIndex,
val aggregateKeyIndex: KeyIndex
)
/** A resolved build unit. (`ResolvedBuildUnit` would be a better name to distinguish it from the loaded, but unresolved `BuildUnit`.)
* @param unit The loaded, but unresolved [[BuildUnit]] this was resolved from.
* @param defined The definitive map from project IDs to resolved projects.
* These projects have had [[Reference]]s resolved and [[AutoPlugin]]s evaluated.
* @param rootProjects The list of project IDs for the projects considered roots of this build.
* The first root project is used as the default in several situations where a project is not otherwise selected.
*/
final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, ResolvedProject], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase
{
assert(!rootProjects.isEmpty, "No root projects defined for build unit " + unit)
/** The project to use as the default when one is not otherwise selected.
* [[LocalRootProject]] resolves to this from within the same build.*/
val root = rootProjects.head
/** The base directory of the build unit (not the build definition).*/
def localBase = unit.localBase
/** The classpath to use when compiling against this build unit's publicly visible code.
* It includes build definition and plugin classes, but not classes for .sbt file statements and expressions. */
def classpath: Seq[File] = unit.definitions.target ++ unit.plugins.classpath
/** The class loader to use for this build unit's publicly visible code.
* It includes build definition and plugin classes, but not classes for .sbt file statements and expressions. */
def loader = unit.definitions.loader
/** The imports to use for .sbt files, `consoleProject` and other contexts that use code from the build definition. */
def imports = BuildUtil.getImports(unit)
override def toString = unit.toString
}
// TODO: figure out how to deprecate and drop buildNames
/** The built and loaded build definition, including loaded but unresolved [[Project]]s, for a build unit (for a single URI).
*
* @param base The base directory of the build definition, typically `<build base>/project/`.
* @param loader The ClassLoader containing all classes and plugins for the build definition project.
* Note that this does not include classes for .sbt files.
* @param builds The list of [[Build]]s for the build unit.
* In addition to auto-discovered [[Build]]s, this includes any auto-generated default [[Build]]s.
* @param projects The list of all [[Project]]s from all [[Build]]s.
* These projects have not yet been resolved, but they have had auto-plugins applied.
* In particular, each [[Project]]'s `autoPlugins` field is populated according to their configured `natures`
* and their `settings` and `configurations` updated as appropriate.
* @param buildNames No longer used and will be deprecated once feasible.
*/
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 DetectedModules[T](val modules: Seq[(String, T)]) {
/** Auto-detected top-level modules (as in `object X`) of type `T` paired with their source names. */
final class DetectedModules[T](val modules: Seq[(String, T)])
{
/** The source names of the modules. This is "X" in `object X`, as opposed to the implementation class name "X$".
* The names are returned in a stable order such that `names zip values` pairs a name with the actual module. */
def names: Seq[String] = modules.map(_._1)
/** The singleton value of the module.
* The values are returned in a stable order such that `names zip values` pairs a name with the actual module. */
def values: Seq[T] = modules.map(_._2)
}
/** Auto-discovered modules for the build definition project. These include modules defined in build definition sources
* as well as modules in binary dependencies.
*
* @param builds The [[Build]]s detected in the build definition. This does not include the default [[Build]] that sbt creates if none is defined.
*/
final class DetectedPlugins(val plugins: DetectedModules[Plugin], val autoImports: DetectedModules[AutoImport], val autoPlugins: DetectedModules[AutoPlugin], val builds: DetectedModules[Build])
{
/** Sequence of import expressions for the build definition. This includes the names of the [[Plugin]], [[Build]], and [[AutoImport]] modules, but not the [[AutoPlugin]] modules. */
lazy val imports: Seq[String] = BuildUtil.getImports(plugins.names ++ builds.names ++ autoImports.names)
/** A function to select the right [[AutoPlugin]]s from [[autoPlugins]] given the defined [[Natures]] for a [[Project]]. */
lazy val compileNatures: Natures => Seq[AutoPlugin] = Natures.compile(autoPlugins.values.toList)
}
/** The built and loaded build definition project.
* @param base The base directory for the build definition project (not the base of the project itself).
* @param pluginData Evaluated tasks/settings from the build definition for later use.
* This is necessary because the build definition project is discarded.
* @param loader The class loader for the build definition project, notably excluding classes used for .sbt files.
* @param detected Auto-detected modules in the build definition.
*/
final class LoadedPlugins(val base: File, val pluginData: PluginData, val loader: ClassLoader, val detected: DetectedPlugins)
{
/*
@ -71,6 +127,11 @@ final class LoadedPlugins(val base: File, val pluginData: PluginData, val loader
def classpath = data(fullClasspath)
}
/** The loaded, but unresolved build unit.
* @param uri The uniquely identifying URI for the build.
* @param localBase The working location of the build on the filesystem.
* For local URIs, this is the same as `uri`, but for remote URIs, this is the local copy or workspace allocated for the build.
*/
final class BuildUnit(val uri: URI, val localBase: File, val definitions: LoadedDefinitions, val plugins: LoadedPlugins)
{
override def toString = if(uri.getScheme == "file") localBase.toString else (uri + " (locally: " + localBase +")")

View File

@ -3,13 +3,13 @@ package sbt
import Def.Setting
import java.net.URI
final class GroupedAutoPlugins(val all: Seq[AutoPlugin], val byBuild: Map[URI, Seq[AutoPlugin]])
private[sbt] 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] object GroupedAutoPlugins
{
private[sbt] def apply(units: Map[URI, LoadedBuildUnit]): GroupedAutoPlugins =
{

View File

@ -469,6 +469,7 @@ object Load
val autoConfigs = autoPlugins.flatMap(_.projectConfigurations)
val loadedSbtFiles = loadSbtFiles(project.auto, project.base, autoPlugins)
val newSettings = (project.settings: Seq[Setting[_]]) ++ loadedSbtFiles.settings
// add the automatically selected settings, record the selected AutoPlugins, and register the automatically selected configurations
val transformed = project.copy(settings = newSettings).setAutoPlugins(autoPlugins).overrideConfigs(autoConfigs : _*)
(transformed, loadedSbtFiles.projects)
}
@ -621,6 +622,9 @@ object Load
*/
def loadPlugins(dir: File, data: PluginData, loader: ClassLoader): sbt.LoadedPlugins =
new sbt.LoadedPlugins(dir, data, loader, autoDetect(data, loader))
private[this] def autoDetect(data: PluginData, loader: ClassLoader): DetectedPlugins =
{
// TODO: binary detection for builds, autoImports, autoPlugins
import AutoBinaryResource._
@ -628,8 +632,7 @@ object Load
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)
new DetectedPlugins(plugins, autoImports, autoPlugins, builds)
}
private[this] def detectModules[T](data: PluginData, loader: ClassLoader, resourceName: String)(implicit mf: reflect.ClassManifest[T]): DetectedModules[T] =
{
@ -677,6 +680,8 @@ TODO: UNCOMMENT BEFORE COMMIT
binaryPlugins(classpath, loader, AutoBinaryResource.Plugins)
*/
/** Relative paths of resources that list top-level modules that are available.
* Normally, the classes for those modules will be in the same classpath entry as the resource. */
object AutoBinaryResource {
final val AutoPlugins = "sbt/sbt.autoplugins"
final val Plugins = "sbt/sbt.plugins"