mirror of https://github.com/sbt/sbt.git
Document helper functions in BuildStructure
Also define LoadedBuildUnit#projects & BuildUnit#thisRootProject, & cleanup AttributeKey & BasicAttributeMap
This commit is contained in:
parent
b7fb0ca2a3
commit
4e89e8ace5
|
|
@ -17,11 +17,11 @@ import sbt.util.OptJsonWriter
|
|||
|
||||
/**
|
||||
* A key in an [[AttributeMap]] that constrains its associated value to be of type `T`.
|
||||
* The key is uniquely defined by its [[label]] and type `T`, represented at runtime by [[manifest]].
|
||||
* The key is uniquely defined by its `label` and type `T`, represented at runtime by `manifest`.
|
||||
*/
|
||||
sealed trait AttributeKey[T] {
|
||||
|
||||
/** The runtime evidence for `T` */
|
||||
/** The runtime evidence for `T`. */
|
||||
def manifest: Manifest[T]
|
||||
|
||||
/** The label is the identifier for the key and is camelCase by convention. */
|
||||
|
|
@ -103,10 +103,10 @@ object AttributeKey {
|
|||
rank0: Int
|
||||
)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
|
||||
new SharedAttributeKey[T] {
|
||||
require(name.headOption match {
|
||||
case Some(c) => c.isLower
|
||||
case None => false
|
||||
}, s"A named attribute key must start with a lowercase letter: $name")
|
||||
require(
|
||||
name.headOption.exists(_.isLower),
|
||||
s"A named attribute key must start with a lowercase letter: $name"
|
||||
)
|
||||
|
||||
def manifest = mf
|
||||
val label = Util.hyphenToCamel(name)
|
||||
|
|
@ -220,18 +220,13 @@ private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any])
|
|||
|
||||
def keys: Iterable[AttributeKey[_]] = backing.keys
|
||||
|
||||
def ++(o: Iterable[AttributeEntry[_]]): AttributeMap = {
|
||||
val newBacking = (backing /: o) {
|
||||
case (b, AttributeEntry(key, value)) => b.updated(key, value)
|
||||
}
|
||||
new BasicAttributeMap(newBacking)
|
||||
}
|
||||
def ++(o: Iterable[AttributeEntry[_]]): AttributeMap =
|
||||
new BasicAttributeMap(o.foldLeft(backing)((b, e) => b.updated(e.key, e.value)))
|
||||
|
||||
def ++(o: AttributeMap): AttributeMap =
|
||||
o match {
|
||||
case bam: BasicAttributeMap => new BasicAttributeMap(backing ++ bam.backing)
|
||||
case _ => o ++ this
|
||||
}
|
||||
def ++(o: AttributeMap): AttributeMap = o match {
|
||||
case bam: BasicAttributeMap => new BasicAttributeMap(backing ++ bam.backing)
|
||||
case _ => o ++ this
|
||||
}
|
||||
|
||||
def entries: Iterable[AttributeEntry[_]] =
|
||||
backing.collect {
|
||||
|
|
|
|||
|
|
@ -667,11 +667,10 @@ object BuiltinCommands {
|
|||
|
||||
def act: Command = Command.customHelp(Act.actParser, actHelp)
|
||||
|
||||
def actHelp: State => Help =
|
||||
s =>
|
||||
CommandStrings.showHelp ++ CommandStrings.printHelp ++ CommandStrings.multiTaskHelp ++ keysHelp(
|
||||
s
|
||||
)
|
||||
def actHelp: State => Help = { s =>
|
||||
CommandStrings.showHelp ++ CommandStrings.printHelp ++ CommandStrings.multiTaskHelp ++
|
||||
keysHelp(s)
|
||||
}
|
||||
|
||||
def keysHelp(s: State): Help =
|
||||
if (Project.isProjectLoaded(s))
|
||||
|
|
|
|||
|
|
@ -28,35 +28,47 @@ final class BuildStructure(
|
|||
val index: StructureIndex,
|
||||
val streams: State => Streams,
|
||||
val delegates: Scope => Seq[Scope],
|
||||
val scopeLocal: ScopeLocal
|
||||
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))
|
||||
def allProjectPairs: Seq[(ResolvedProject, ProjectRef)] =
|
||||
for {
|
||||
(build, unit) <- units.toSeq
|
||||
p: ResolvedProject <- unit.defined.values.toSeq
|
||||
} yield (p, ProjectRef(build, p.id))
|
||||
|
||||
val extra: 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)
|
||||
}
|
||||
|
||||
/** The root project for the specified build. Throws if no build or empty build. */
|
||||
val rootProject: URI => String = extra.rootProjectID
|
||||
|
||||
/** All the projects in all builds. */
|
||||
def allProjects: Seq[ResolvedProject] = eachBuild((_, p) => p)
|
||||
|
||||
/** References to all the projects in all the builds. */
|
||||
def allProjectRefs: Seq[ProjectRef] = eachBuild((b, p) => ProjectRef(b, p.id))
|
||||
|
||||
/** Both the projects and the projects reference of all the projects in all the builds. */
|
||||
def allProjectPairs: Seq[(ResolvedProject, ProjectRef)] =
|
||||
eachBuild((b, p) => p -> ProjectRef(b, p.id))
|
||||
|
||||
/** All the projects in the specified build. */
|
||||
def allProjects(build: URI): Seq[ResolvedProject] = eachProject(build, identity)
|
||||
|
||||
/** The references to all the projects in the specified build. */
|
||||
def allProjectRefs(build: URI): Seq[ProjectRef] = eachProject(build, p => ProjectRef(build, p.id))
|
||||
|
||||
/** Foreach project in each build apply the specified function. */
|
||||
private[this] def eachBuild[A](f: (URI, ResolvedProject) => A): Seq[A] =
|
||||
units.iterator.flatMap { case (build, unit) => unit.projects.map(f(build, _)) }.toIndexedSeq
|
||||
|
||||
/** Foreach project in the specified build apply the specified function. */
|
||||
private[this] def eachProject[A](build: URI, f: ResolvedProject => A): Seq[A] =
|
||||
units.get(build).iterator.flatMap(_.projects).map(f).toIndexedSeq
|
||||
|
||||
}
|
||||
|
||||
// information that is not original, but can be reconstructed from the rest of BuildStructure
|
||||
final class StructureIndex(
|
||||
val keyMap: Map[String, AttributeKey[_]],
|
||||
val taskToKey: Map[Task[_], ScopedKey[Task[_]]],
|
||||
val triggers: Triggers[Task],
|
||||
val keyIndex: KeyIndex,
|
||||
val aggregateKeyIndex: KeyIndex
|
||||
val aggregateKeyIndex: KeyIndex,
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
@ -78,13 +90,11 @@ final class LoadedBuildUnit(
|
|||
* 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 match {
|
||||
case Nil =>
|
||||
throw new java.lang.AssertionError(
|
||||
"assertion failed: No root projects defined for build unit " + unit
|
||||
)
|
||||
case Seq(root, _*) => root
|
||||
}
|
||||
val root = rootProjects.headOption.getOrElse(
|
||||
throw new java.lang.AssertionError(
|
||||
s"assertion failed: No root projects defined for build unit $unit"
|
||||
)
|
||||
)
|
||||
|
||||
/** The base directory of the build unit (not the build definition).*/
|
||||
def localBase = unit.localBase
|
||||
|
|
@ -104,6 +114,9 @@ final class LoadedBuildUnit(
|
|||
|
||||
/** The imports to use for .sbt files, `consoleProject` and other contexts that use code from the build definition. */
|
||||
def imports = BuildUtil.getImports(unit)
|
||||
|
||||
def projects: Iterable[ResolvedProject] = defined.values
|
||||
|
||||
override def toString = unit.toString
|
||||
}
|
||||
|
||||
|
|
@ -241,28 +254,37 @@ final class BuildUnit(
|
|||
|
||||
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
|
||||
units.iterator.flatMap {
|
||||
case (build, unit) => unit.projects.map(p => ProjectRef(build, p.id) -> p)
|
||||
}.toIndexedSeq
|
||||
|
||||
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[_]] }
|
||||
|
||||
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)
|
||||
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[_]]
|
||||
type Streams = sbt.std.Streams[ScopedKey[_]]
|
||||
|
||||
final val GlobalPath = "$global"
|
||||
final val BuildUnitPath = "$build"
|
||||
|
|
@ -300,11 +322,11 @@ object BuildStreams {
|
|||
show: T => String
|
||||
): String =
|
||||
axis match {
|
||||
case Zero => GlobalPath
|
||||
case This =>
|
||||
sys.error("Unresolved This reference for " + label + " in " + displayFull(scoped))
|
||||
case Zero => GlobalPath
|
||||
case This => sys.error(s"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) ::
|
||||
|
|
@ -313,10 +335,13 @@ object BuildStreams {
|
|||
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 (" ")
|
||||
a.entries.toStream
|
||||
.sortBy(_.key.label)
|
||||
.map { case AttributeEntry(key, value) => s"${key.label}=$value" }
|
||||
.mkString(" ")
|
||||
|
||||
def projectPath(
|
||||
units: Map[URI, LoadedBuildUnit],
|
||||
root: URI,
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@ final class BuildUtil[Proj](
|
|||
val configurations: Proj => Seq[ConfigKey],
|
||||
val aggregates: Relation[ProjectRef, ProjectRef]
|
||||
) {
|
||||
def rootProject(uri: URI): Proj =
|
||||
project(uri, rootProjectID(uri))
|
||||
|
||||
def thisRootProject: Proj = rootProject(root)
|
||||
|
||||
def rootProject(uri: URI): Proj = project(uri, rootProjectID(uri))
|
||||
|
||||
def resolveRef(ref: Reference): ResolvedReference =
|
||||
Scope.resolveReference(root, rootProjectID, ref)
|
||||
|
|
@ -31,14 +33,14 @@ final class BuildUtil[Proj](
|
|||
case ProjectRef(uri, id) => project(uri, id)
|
||||
case BuildRef(uri) => rootProject(uri)
|
||||
}
|
||||
|
||||
def projectRefFor(ref: ResolvedReference): ProjectRef = ref match {
|
||||
case p: ProjectRef => p
|
||||
case BuildRef(uri) => ProjectRef(uri, rootProjectID(uri))
|
||||
}
|
||||
def projectForAxis(ref: Option[ResolvedReference]): Proj = ref match {
|
||||
case Some(ref) => projectFor(ref)
|
||||
case None => rootProject(root)
|
||||
}
|
||||
|
||||
def projectForAxis(ref: Option[ResolvedReference]): Proj = ref.fold(thisRootProject)(projectFor)
|
||||
|
||||
def exactProject(refOpt: Option[Reference]): Option[Proj] = refOpt map resolveRef flatMap {
|
||||
case ProjectRef(uri, id) => Some(project(uri, id))
|
||||
case _ => None
|
||||
|
|
@ -46,7 +48,9 @@ final class BuildUtil[Proj](
|
|||
|
||||
val configurationsForAxis: Option[ResolvedReference] => Seq[String] =
|
||||
refOpt => configurations(projectForAxis(refOpt)).map(_.name)
|
||||
|
||||
}
|
||||
|
||||
object BuildUtil {
|
||||
def apply(
|
||||
root: URI,
|
||||
|
|
@ -57,14 +61,14 @@ object BuildUtil {
|
|||
val getp = (build: URI, project: String) => Load.getProject(units, build, project)
|
||||
val configs = (_: ResolvedProject).configurations.map(c => ConfigKey(c.name))
|
||||
val aggregates = aggregationRelation(units)
|
||||
new BuildUtil(keyIndex, data, root, Load getRootProject units, getp, configs, aggregates)
|
||||
new BuildUtil(keyIndex, data, root, Load.getRootProject(units), getp, configs, aggregates)
|
||||
}
|
||||
|
||||
def dependencies(units: Map[URI, LoadedBuildUnit]): BuildDependencies = {
|
||||
import scala.collection.mutable
|
||||
val agg = new mutable.HashMap[ProjectRef, Seq[ProjectRef]]
|
||||
val cp = new mutable.HashMap[ProjectRef, Seq[ClasspathDep[ProjectRef]]]
|
||||
for (lbu <- units.values; rp <- lbu.defined.values) {
|
||||
for (lbu <- units.values; rp <- lbu.projects) {
|
||||
val ref = ProjectRef(lbu.unit.uri, rp.id)
|
||||
cp(ref) = rp.dependencies
|
||||
agg(ref) = rp.aggregate
|
||||
|
|
@ -79,7 +83,7 @@ object BuildUtil {
|
|||
)(base: ResolvedProject => Seq[ProjectRef]): Seq[ResolvedProject] =
|
||||
Dag.topologicalSort(proj)(p => base(p) map getRef)
|
||||
// check for cycles
|
||||
for ((_, lbu) <- units; proj <- lbu.defined.values) {
|
||||
for ((_, lbu) <- units; proj <- lbu.projects) {
|
||||
deps(proj)(_.dependencies.map(_.project))
|
||||
deps(proj)(_.aggregate)
|
||||
}
|
||||
|
|
@ -110,7 +114,7 @@ object BuildUtil {
|
|||
val depPairs =
|
||||
for {
|
||||
(uri, unit) <- units.toIterable // don't lose this toIterable, doing so breaks actions/cross-multiproject & actions/update-state-fail
|
||||
project <- unit.defined.values
|
||||
project <- unit.projects
|
||||
ref = ProjectRef(uri, project.id)
|
||||
agg <- project.aggregate
|
||||
} yield (ref, agg)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ private[sbt] final class GroupedAutoPlugins(
|
|||
private[sbt] 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
|
||||
units.mapValues(unit => unit.projects.flatMap(_.autoPlugins).toSeq.distinct).toMap
|
||||
val all: Seq[AutoPlugin] = byBuild.values.toSeq.flatten.distinct
|
||||
new GroupedAutoPlugins(all, byBuild)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -665,8 +665,7 @@ private[sbt] object Load {
|
|||
def getProject(map: Map[URI, LoadedBuildUnit], uri: URI, id: String): ResolvedProject =
|
||||
getBuild(map, uri).defined.getOrElse(id, noProject(uri, id))
|
||||
|
||||
def getBuild[T](map: Map[URI, T], uri: URI): T =
|
||||
map.getOrElse(uri, noBuild(uri))
|
||||
def getBuild[T](map: Map[URI, T], uri: URI): T = map.getOrElse(uri, noBuild(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.")
|
||||
|
|
@ -779,7 +778,7 @@ private[sbt] object Load {
|
|||
case Right(id) => id
|
||||
case Left(msg) => sys.error(autoIDError(f, msg))
|
||||
}
|
||||
def nthParentName(f: File, i: Int): String =
|
||||
@tailrec def nthParentName(f: File, i: Int): String =
|
||||
if (f eq null) BuildDef.defaultID(localBase)
|
||||
else if (i <= 0) normalizeID(f)
|
||||
else nthParentName(f.getParentFile, i - 1)
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ private[sbt] object PluginsDebug {
|
|||
def helpBuild(uri: URI, build: LoadedBuildUnit): String = {
|
||||
val pluginStrings = for (plugin <- availableAutoPlugins(build)) yield {
|
||||
val activatedIn =
|
||||
build.defined.values.toList.filter(_.autoPlugins.contains(plugin)).map(_.id)
|
||||
build.projects.toList.filter(_.autoPlugins.contains(plugin)).map(_.id)
|
||||
val actString =
|
||||
if (activatedIn.nonEmpty) activatedIn.mkString(": enabled in ", ", ", "")
|
||||
else "" // TODO: deal with large builds
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ object Resolve {
|
|||
scope
|
||||
else {
|
||||
val (resolvedRef, proj) = scope.project match {
|
||||
case Zero | This => (None, index.rootProject(index.root))
|
||||
case Zero | This => (None, index.thisRootProject)
|
||||
case Select(ref) =>
|
||||
val r = index resolveRef ref
|
||||
(Some(r), index.projectFor(r))
|
||||
|
|
|
|||
Loading…
Reference in New Issue