mirror of https://github.com/sbt/sbt.git
[2.x] fix: extraProjects with auto-root aggregate breaks key aggregation (#8690)
When using the aggregated key parser, a key is now valid if it exists in `data` for that scope **or** it's an aggregate key and every key it aggregates to exists in `data`. So `(root, scripted)` is accepted when the root aggregates a project that defines `scripted`, and running `scripted` at root runs it on that project as before.
This commit is contained in:
parent
b460bb871e
commit
2f27b5cecd
|
|
@ -63,7 +63,7 @@ object Act {
|
|||
keyMap: Map[String, AttributeKey[?]],
|
||||
data: Def.Settings
|
||||
): Parser[ScopedKey[Any]] =
|
||||
scopedKeySelected(index, current, defaultConfigs, keyMap, data, askProject = true)
|
||||
scopedKeySelected(index, current, defaultConfigs, keyMap, data, askProject = true, None)
|
||||
.map(_.key.asInstanceOf[ScopedKey[Any]])
|
||||
|
||||
// the index should be an aggregated index for proper tab completion
|
||||
|
|
@ -80,6 +80,7 @@ object Act {
|
|||
structure.index.keyMap,
|
||||
structure.data,
|
||||
askProject = true,
|
||||
structure = Some(structure),
|
||||
)
|
||||
)
|
||||
yield Aggregation.aggregate(
|
||||
|
|
@ -102,6 +103,7 @@ object Act {
|
|||
structure.index.keyMap,
|
||||
structure.data,
|
||||
askProject = optQuery.isEmpty,
|
||||
structure = Some(structure),
|
||||
)
|
||||
yield Aggregation
|
||||
.aggregate(selected.key, selected.mask, structure.extra)
|
||||
|
|
@ -117,10 +119,11 @@ object Act {
|
|||
keyMap: Map[String, AttributeKey[?]],
|
||||
data: Def.Settings,
|
||||
askProject: Boolean,
|
||||
structure: Option[BuildStructure],
|
||||
): Parser[ParsedKey] =
|
||||
scopedKeyFull(index, current, defaultConfigs, keyMap, askProject = askProject).flatMap {
|
||||
choices =>
|
||||
select(choices, data)(using showRelativeKey2(current))
|
||||
select(choices, data, structure)(using showRelativeKey2(current))
|
||||
}
|
||||
|
||||
def scopedKeyFull(
|
||||
|
|
@ -197,16 +200,39 @@ object Act {
|
|||
key
|
||||
)
|
||||
|
||||
def select(allKeys: Seq[Parser[ParsedKey]], data: Def.Settings)(using
|
||||
show: Show[ScopedKey[?]]
|
||||
): Parser[ParsedKey] =
|
||||
def select(
|
||||
allKeys: Seq[Parser[ParsedKey]],
|
||||
data: Def.Settings
|
||||
)(using show: Show[ScopedKey[?]]): Parser[ParsedKey] =
|
||||
select(allKeys, data, None)
|
||||
|
||||
def select(
|
||||
allKeys: Seq[Parser[ParsedKey]],
|
||||
data: Def.Settings,
|
||||
structure: Option[BuildStructure]
|
||||
)(using show: Show[ScopedKey[?]]): Parser[ParsedKey] =
|
||||
seq(allKeys) flatMap { ss =>
|
||||
val default: Parser[ParsedKey] = ss.headOption match
|
||||
case None => noValidKeys
|
||||
case Some(x) => success(x)
|
||||
selectFromValid(ss filter isValid(data), default)
|
||||
val validFilter = structure.fold(isValid(data))(isValidForAggregate(data, _))
|
||||
selectFromValid(ss filter validFilter, default)
|
||||
}
|
||||
|
||||
private def isValidForAggregate(
|
||||
data: Def.Settings,
|
||||
structure: BuildStructure
|
||||
)(parsed: ParsedKey): Boolean =
|
||||
if data.contains(parsed.key) then true
|
||||
else
|
||||
val aggregated =
|
||||
Aggregation.aggregate(
|
||||
parsed.key.asInstanceOf[ScopedKey[Any]],
|
||||
parsed.mask,
|
||||
structure.extra
|
||||
)
|
||||
aggregated.nonEmpty && aggregated.exists(data.contains)
|
||||
|
||||
def selectFromValid(ss: Seq[ParsedKey], default: Parser[ParsedKey])(using
|
||||
show: Show[ScopedKey[?]]
|
||||
): Parser[ParsedKey] =
|
||||
|
|
@ -230,7 +256,7 @@ object Act {
|
|||
if (zeros.nonEmpty) zeros else selects
|
||||
}
|
||||
|
||||
def noValidKeys = failure("No such key.")
|
||||
def noValidKeys: Parser[ParsedKey] = failure("No such key.")
|
||||
|
||||
def showAmbiguous(keys: Seq[ScopedKey[?]])(using show: Show[ScopedKey[?]]): String =
|
||||
keys.take(3).map(x => show.show(x)).mkString("", ", ", if (keys.size > 3) ", ..." else "")
|
||||
|
|
|
|||
|
|
@ -867,7 +867,7 @@ private[sbt] object Load {
|
|||
defaultProjects.generatedConfigClassFiles ++ loadedProjectsRaw.generatedConfigClassFiles
|
||||
)
|
||||
}
|
||||
val loadedProjects = processAutoAggregate(loadedProjects0, uri)
|
||||
val loadedProjects = processAutoAggregate(loadedProjects0, uri, normBase, !hasRoot)
|
||||
timed("Load.loadUnit: cleanEvalClasses", log) {
|
||||
cleanEvalClasses(defDir, keepClassFiles)
|
||||
}
|
||||
|
|
@ -889,9 +889,29 @@ private[sbt] object Load {
|
|||
new BuildUnit(uri, normBase, loadedDefs, plugs, converter)
|
||||
}
|
||||
|
||||
private def processAutoAggregate(inProjects: Seq[Project], uri: URI): Seq[Project] =
|
||||
inProjects.map: proj =>
|
||||
proj
|
||||
private def processAutoAggregate(
|
||||
inProjects: Seq[Project],
|
||||
uri: URI,
|
||||
buildBase: File,
|
||||
hasAutoRoot: Boolean
|
||||
): Seq[Project] =
|
||||
if !hasAutoRoot then inProjects
|
||||
else
|
||||
val allProjectIds = inProjects.map(_.id).toSet
|
||||
inProjects.map: proj =>
|
||||
val isAutoRoot = isRootPath(proj.base, buildBase) && proj.aggregate.nonEmpty
|
||||
if isAutoRoot then
|
||||
val currentAggregateIds = proj.aggregate
|
||||
.flatMap:
|
||||
case ref: ProjectRef => Some(ref.project)
|
||||
case _ => None
|
||||
.toSet
|
||||
val missingIds = allProjectIds - proj.id -- currentAggregateIds
|
||||
if missingIds.nonEmpty then
|
||||
val missingRefs = missingIds.toSeq.map(id => ProjectRef(uri, id))
|
||||
proj.aggregate((proj.aggregate ++ missingRefs)*)
|
||||
else proj
|
||||
else proj
|
||||
|
||||
private def autoID(
|
||||
localBase: File,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2023, Scala center
|
||||
* Copyright 2011 - 2022, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
val check = taskKey[Unit]("Repro for #4947: task at root when extraProjects creates auto root")
|
||||
|
||||
val a = project
|
||||
val p = project
|
||||
.settings(
|
||||
name := "p",
|
||||
check := ()
|
||||
)
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2023, Scala center
|
||||
* Copyright 2011 - 2022, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
import sbt._, Keys._
|
||||
|
||||
object ExtraPlugin extends AutoPlugin {
|
||||
override def trigger = allRequirements
|
||||
override def extraProjects: Seq[Project] =
|
||||
Seq(Project("z", file("z")).settings(name := "z"))
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
> check
|
||||
Loading…
Reference in New Issue