From 4e89e8ace5dc8d6aa65a63de16e18a44adf7f376 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 1 Jun 2019 10:32:35 +0100 Subject: [PATCH] Document helper functions in BuildStructure Also define LoadedBuildUnit#projects & BuildUnit#thisRootProject, & cleanup AttributeKey & BasicAttributeMap --- .../scala/sbt/internal/util/Attributes.scala | 29 +++--- main/src/main/scala/sbt/Main.scala | 9 +- .../scala/sbt/internal/BuildStructure.scala | 95 ++++++++++++------- .../main/scala/sbt/internal/BuildUtil.scala | 24 +++-- .../sbt/internal/GroupedAutoPlugins.scala | 2 +- main/src/main/scala/sbt/internal/Load.scala | 5 +- .../scala/sbt/internal/PluginsDebug.scala | 2 +- .../src/main/scala/sbt/internal/Resolve.scala | 2 +- 8 files changed, 95 insertions(+), 73 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala index e1322bfa8..931c8592e 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala @@ -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 { diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index a1a54f095..518cf6f4a 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -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)) diff --git a/main/src/main/scala/sbt/internal/BuildStructure.scala b/main/src/main/scala/sbt/internal/BuildStructure.scala index 309e72844..a77686022 100644 --- a/main/src/main/scala/sbt/internal/BuildStructure.scala +++ b/main/src/main/scala/sbt/internal/BuildStructure.scala @@ -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, diff --git a/main/src/main/scala/sbt/internal/BuildUtil.scala b/main/src/main/scala/sbt/internal/BuildUtil.scala index eb424a682..78611a963 100644 --- a/main/src/main/scala/sbt/internal/BuildUtil.scala +++ b/main/src/main/scala/sbt/internal/BuildUtil.scala @@ -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) diff --git a/main/src/main/scala/sbt/internal/GroupedAutoPlugins.scala b/main/src/main/scala/sbt/internal/GroupedAutoPlugins.scala index 735dd651f..af67e69bd 100644 --- a/main/src/main/scala/sbt/internal/GroupedAutoPlugins.scala +++ b/main/src/main/scala/sbt/internal/GroupedAutoPlugins.scala @@ -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) } diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index 76741d579..b5f9f70e0 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -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) diff --git a/main/src/main/scala/sbt/internal/PluginsDebug.scala b/main/src/main/scala/sbt/internal/PluginsDebug.scala index a6e3770da..4b293e3c9 100644 --- a/main/src/main/scala/sbt/internal/PluginsDebug.scala +++ b/main/src/main/scala/sbt/internal/PluginsDebug.scala @@ -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 diff --git a/main/src/main/scala/sbt/internal/Resolve.scala b/main/src/main/scala/sbt/internal/Resolve.scala index 211c0f9dd..8e92e1b78 100644 --- a/main/src/main/scala/sbt/internal/Resolve.scala +++ b/main/src/main/scala/sbt/internal/Resolve.scala @@ -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))