package sbt
import java.io.File
import java.net.URI
import Def.{ displayFull, ScopedKey, ScopeLocal, Setting }
import Attributed.data
import BuildPaths.outputDirectory
import Scope.GlobalScope
import BuildStreams.Streams
import Path._
final class BuildStructure(val units: Map[URI, LoadedBuildUnit], val root: URI, val settings: Seq[Setting[_]], val data: Settings[Scope], val index: StructureIndex, val streams: State => Streams, val delegates: Scope => Seq[Scope], val scopeLocal: ScopeLocal) {
val rootProject: URI => String = Load getRootProject units
def allProjects: Seq[ResolvedProject] = units.values.flatMap(_.defined.values).toSeq
def allProjects(build: URI): Seq[ResolvedProject] = units.get(build).toList.flatMap(_.defined.values)
def allProjectRefs: Seq[ProjectRef] = units.toSeq flatMap { case (build, unit) => refs(build, unit.defined.values.toSeq) }
def allProjectRefs(build: URI): Seq[ProjectRef] = refs(build, allProjects(build))
val : BuildUtil[ResolvedProject] = BuildUtil(root, units, index.keyIndex, data)
private[this] def refs(build: URI, projects: Seq[ResolvedProject]): Seq[ProjectRef] = projects.map { p => ProjectRef(build, p.id) }
}
final class StructureIndex(
val keyMap: Map[String, AttributeKey[_]],
val taskToKey: Map[Task[_], ScopedKey[Task[_]]],
val triggers: Triggers[Task],
val keyIndex: KeyIndex,
val aggregateKeyIndex: KeyIndex)
final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, ResolvedProject], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase {
assert(rootProjects.nonEmpty, "No root projects defined for build unit " + unit)
val root = rootProjects.head
def localBase = unit.localBase
def classpath: Seq[File] = unit.definitions.target ++ unit.plugins.classpath ++ unit.definitions.dslDefinitions.classpath
def loader = unit.definitions.dslDefinitions.classloader(unit.definitions.loader)
def imports = BuildUtil.getImports(unit)
override def toString = unit.toString
}
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],
val dslDefinitions: DefinedSbtValues) {
def this(base: File,
target: Seq[File],
loader: ClassLoader,
builds: Seq[Build],
projects: Seq[Project],
buildNames: Seq[String]) = this(base, target, loader, builds, projects, buildNames, DefinedSbtValues.empty)
}
final class DetectedModules[T](val modules: Seq[(String, T)]) {
def names: Seq[String] = modules.map(_._1)
def values: Seq[T] = modules.map(_._2)
}
case class DetectedAutoPlugin(name: String, value: AutoPlugin, hasAutoImport: Boolean)
final class DetectedPlugins(val plugins: DetectedModules[Plugin], val autoPlugins: Seq[DetectedAutoPlugin], val builds: DetectedModules[Build]) {
lazy val imports: Seq[String] = BuildUtil.getImports(plugins.names ++ builds.names) ++
BuildUtil.importAllRoot(autoImports(autoPluginAutoImports)) ++
BuildUtil.importAll(autoImports(topLevelAutoPluginAutoImports)) ++
BuildUtil.importNamesRoot(autoPlugins.map(_.name).filter(nonTopLevelPlugin))
private[this] lazy val (autoPluginAutoImports, topLevelAutoPluginAutoImports) =
autoPlugins.flatMap {
case DetectedAutoPlugin(name, ap, hasAutoImport) =>
if (hasAutoImport) Some(name)
else None
}.partition(nonTopLevelPlugin)
@deprecated("Use deducePluginsFromProject", "0.13.8")
lazy val deducePlugins: (Plugins, Logger) => Seq[AutoPlugin] = Plugins.deducer(autoPlugins.toList map { _.value })
def deducePluginsFromProject(p: Project, log: Logger): Seq[AutoPlugin] =
{
val ps0 = p.plugins
val allDetected = autoPlugins.toList map { _.value }
val detected = p match {
case _: GeneratedRootProject => allDetected filterNot { _ == sbt.plugins.IvyPlugin }
case _ => allDetected
}
Plugins.deducer(detected)(ps0, log)
}
private[this] def autoImports(pluginNames: Seq[String]) = pluginNames.map(_ + ".autoImport")
private[this] def nonTopLevelPlugin(name: String) = name.contains('.')
}
final class LoadedPlugins(val base: File, val pluginData: PluginData, val loader: ClassLoader, val detected: DetectedPlugins) {
@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,
new DetectedPlugins(new DetectedModules(pluginNames zip plugins), Nil, new DetectedModules(Nil))
)
@deprecated("Use detected.plugins.values.", "0.13.2")
val plugins: Seq[Plugin] = detected.plugins.values
@deprecated("Use detected.plugins.names.", "0.13.2")
val pluginNames: Seq[String] = 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) {
override def toString = if (uri.getScheme == "file") localBase.toString else (uri + " (locally: " + localBase + ")")
}
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 (: Settings[Scope])(: 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[_]] }
final class PartBuildUnit(val unit: BuildUnit, val defined: Map[String, Project], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase {
def resolve(f: Project => ResolvedProject): LoadedBuildUnit = new LoadedBuildUnit(unit, defined mapValues f toMap, rootProjects, buildSettings)
def resolveRefs(f: ProjectReference => ProjectRef): LoadedBuildUnit = resolve(_ resolve f)
}
object BuildStreams {
type Streams = std.Streams[ScopedKey[_]]
final val GlobalPath = "$global"
final val BuildUnitPath = "$build"
final val StreamsDirectory = "streams"
def mkStreams(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope]): State => Streams = s =>
s get Keys.stateStreams getOrElse std.Streams(path(units, root, data), displayFull, LogManager.construct(data, s))
def path(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope])(scoped: ScopedKey[_]): File =
resolvePath(projectPath(units, root, scoped, data), nonProjectPath(scoped))
def resolvePath(base: File, components: Seq[String]): File =
(base /: components)((b, p) => new File(b, p))
def pathComponent[T](axis: ScopeAxis[T], scoped: ScopedKey[_], label: String)(show: T => String): String =
axis match {
case Global => GlobalPath
case This => sys.error("Unresolved This reference for " + label + " in " + displayFull(scoped))
case Select(t) => show(t)
}
def nonProjectPath[T](scoped: ScopedKey[T]): Seq[String] =
{
val scope = scoped.scope
pathComponent(scope.config, scoped, "config")(_.name) ::
pathComponent(scope.task, scoped, "task")(_.label) ::
pathComponent(scope.extra, scoped, "extra")(showAMap) ::
scoped.key.label ::
Nil
}
def showAMap(a: AttributeMap): String =
a.entries.toSeq.sortBy(_.key.label).map { case AttributeEntry(key, value) => key.label + "=" + value.toString } mkString (" ")
def projectPath(units: Map[URI, LoadedBuildUnit], root: URI, scoped: ScopedKey[_], data: Settings[Scope]): File =
scoped.scope.project match {
case Global => refTarget(GlobalScope, units(root).localBase, data) / GlobalPath
case Select(br @ BuildRef(uri)) => refTarget(br, units(uri).localBase, data) / BuildUnitPath
case Select(pr @ ProjectRef(uri, id)) => refTarget(pr, units(uri).defined(id).base, data)
case Select(pr) => sys.error("Unresolved project reference (" + pr + ") in " + displayFull(scoped))
case This => sys.error("Unresolved project reference (This) in " + displayFull(scoped))
}
def refTarget(ref: ResolvedReference, fallbackBase: File, data: Settings[Scope]): File =
refTarget(GlobalScope.copy(project = Select(ref)), fallbackBase, data)
def refTarget(scope: Scope, fallbackBase: File, data: Settings[Scope]): File =
(Keys.target in scope get data getOrElse outputDirectory(fallbackBase).asFile) / StreamsDirectory
}