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 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)
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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[_]] }
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 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] =
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 **** */
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue