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[?]],
|
keyMap: Map[String, AttributeKey[?]],
|
||||||
data: Def.Settings
|
data: Def.Settings
|
||||||
): Parser[ScopedKey[Any]] =
|
): 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]])
|
.map(_.key.asInstanceOf[ScopedKey[Any]])
|
||||||
|
|
||||||
// the index should be an aggregated index for proper tab completion
|
// the index should be an aggregated index for proper tab completion
|
||||||
|
|
@ -80,6 +80,7 @@ object Act {
|
||||||
structure.index.keyMap,
|
structure.index.keyMap,
|
||||||
structure.data,
|
structure.data,
|
||||||
askProject = true,
|
askProject = true,
|
||||||
|
structure = Some(structure),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
yield Aggregation.aggregate(
|
yield Aggregation.aggregate(
|
||||||
|
|
@ -102,6 +103,7 @@ object Act {
|
||||||
structure.index.keyMap,
|
structure.index.keyMap,
|
||||||
structure.data,
|
structure.data,
|
||||||
askProject = optQuery.isEmpty,
|
askProject = optQuery.isEmpty,
|
||||||
|
structure = Some(structure),
|
||||||
)
|
)
|
||||||
yield Aggregation
|
yield Aggregation
|
||||||
.aggregate(selected.key, selected.mask, structure.extra)
|
.aggregate(selected.key, selected.mask, structure.extra)
|
||||||
|
|
@ -117,10 +119,11 @@ object Act {
|
||||||
keyMap: Map[String, AttributeKey[?]],
|
keyMap: Map[String, AttributeKey[?]],
|
||||||
data: Def.Settings,
|
data: Def.Settings,
|
||||||
askProject: Boolean,
|
askProject: Boolean,
|
||||||
|
structure: Option[BuildStructure],
|
||||||
): Parser[ParsedKey] =
|
): Parser[ParsedKey] =
|
||||||
scopedKeyFull(index, current, defaultConfigs, keyMap, askProject = askProject).flatMap {
|
scopedKeyFull(index, current, defaultConfigs, keyMap, askProject = askProject).flatMap {
|
||||||
choices =>
|
choices =>
|
||||||
select(choices, data)(using showRelativeKey2(current))
|
select(choices, data, structure)(using showRelativeKey2(current))
|
||||||
}
|
}
|
||||||
|
|
||||||
def scopedKeyFull(
|
def scopedKeyFull(
|
||||||
|
|
@ -197,16 +200,39 @@ object Act {
|
||||||
key
|
key
|
||||||
)
|
)
|
||||||
|
|
||||||
def select(allKeys: Seq[Parser[ParsedKey]], data: Def.Settings)(using
|
def select(
|
||||||
show: Show[ScopedKey[?]]
|
allKeys: Seq[Parser[ParsedKey]],
|
||||||
): 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 =>
|
seq(allKeys) flatMap { ss =>
|
||||||
val default: Parser[ParsedKey] = ss.headOption match
|
val default: Parser[ParsedKey] = ss.headOption match
|
||||||
case None => noValidKeys
|
case None => noValidKeys
|
||||||
case Some(x) => success(x)
|
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
|
def selectFromValid(ss: Seq[ParsedKey], default: Parser[ParsedKey])(using
|
||||||
show: Show[ScopedKey[?]]
|
show: Show[ScopedKey[?]]
|
||||||
): Parser[ParsedKey] =
|
): Parser[ParsedKey] =
|
||||||
|
|
@ -230,7 +256,7 @@ object Act {
|
||||||
if (zeros.nonEmpty) zeros else selects
|
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 =
|
def showAmbiguous(keys: Seq[ScopedKey[?]])(using show: Show[ScopedKey[?]]): String =
|
||||||
keys.take(3).map(x => show.show(x)).mkString("", ", ", if (keys.size > 3) ", ..." else "")
|
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
|
defaultProjects.generatedConfigClassFiles ++ loadedProjectsRaw.generatedConfigClassFiles
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val loadedProjects = processAutoAggregate(loadedProjects0, uri)
|
val loadedProjects = processAutoAggregate(loadedProjects0, uri, normBase, !hasRoot)
|
||||||
timed("Load.loadUnit: cleanEvalClasses", log) {
|
timed("Load.loadUnit: cleanEvalClasses", log) {
|
||||||
cleanEvalClasses(defDir, keepClassFiles)
|
cleanEvalClasses(defDir, keepClassFiles)
|
||||||
}
|
}
|
||||||
|
|
@ -889,9 +889,29 @@ private[sbt] object Load {
|
||||||
new BuildUnit(uri, normBase, loadedDefs, plugs, converter)
|
new BuildUnit(uri, normBase, loadedDefs, plugs, converter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def processAutoAggregate(inProjects: Seq[Project], uri: URI): Seq[Project] =
|
private def processAutoAggregate(
|
||||||
inProjects.map: proj =>
|
inProjects: Seq[Project],
|
||||||
proj
|
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(
|
private def autoID(
|
||||||
localBase: File,
|
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