mirror of
https://github.com/sbt/sbt.git
synced 2026-09-07 02:55:23 +02:00
[2.x] fix: Fixes ThisBuild-scoped bare settings (#9674)
**Problem** ThisBuild scoped baresettings are treated as a common setting, which results in duplicate appends etc. **Solution** Only treat This-project and ThisProject scoped setting as a common setting.
This commit is contained in:
@@ -15,7 +15,7 @@ import sbt.Keys.*
|
||||
import sbt.Project.inScope
|
||||
import sbt.ProjectExtra.{ prefixConfigs, setProject, showLoadingKey, structure }
|
||||
import sbt.Scope.GlobalScope
|
||||
import sbt.ScopeAxis.{ Select, Zero }
|
||||
import sbt.ScopeAxis.{ Select, This, Zero }
|
||||
import sbt.SlashSyntax0.*
|
||||
import sbt.internal.BuildStreams.*
|
||||
import sbt.internal.inc.classpath.ClasspathUtil
|
||||
@@ -1069,6 +1069,11 @@ private[sbt] object Load {
|
||||
log = log,
|
||||
)
|
||||
|
||||
// Excludes settings already scoped explicitly (ThisBuild, Global); those apply once (#9668).
|
||||
def thisProjectScoped(settings: Seq[Setting[?]]): Seq[Setting[?]] =
|
||||
settings.filter: s =>
|
||||
s.key.scope.project == This || s.key.scope.project == Select(ThisProject)
|
||||
|
||||
// load all relevant configuration files (.sbt, as .scala already exists at this point)
|
||||
def discover(base: File): DiscoveredProjects = {
|
||||
val auto =
|
||||
@@ -1132,7 +1137,9 @@ private[sbt] object Load {
|
||||
val newAcc = acc :+ finalRoot
|
||||
val newGenerated = generated ++ generatedConfigClassFiles
|
||||
// only root-level settings are build-wide; a submodule's own settings must not leak to siblings (#9517).
|
||||
val cs = if isRootPath(p.base, buildBase) then finalRoot.commonSettings else commonSettings
|
||||
val cs =
|
||||
if isRootPath(p.base, buildBase) then thisProjectScoped(finalRoot.commonSettings)
|
||||
else commonSettings
|
||||
loadTransitive1(newProjects, newAcc, newGenerated, cs)
|
||||
}
|
||||
|
||||
@@ -1168,7 +1175,9 @@ private[sbt] object Load {
|
||||
acc = acc,
|
||||
generated = Nil,
|
||||
commonSettings0 = commonSettings
|
||||
++ expandCommonSettingsPerBase1(buildBase.getCanonicalFile()),
|
||||
++ thisProjectScoped(
|
||||
expandCommonSettingsPerBase1(buildBase.getCanonicalFile())
|
||||
),
|
||||
)
|
||||
val existingIds = otherProjects.projects.map(_.id)
|
||||
val refs = existingIds.map(id => ProjectRef(buildUri, id))
|
||||
@@ -1181,7 +1190,12 @@ private[sbt] object Load {
|
||||
val newAcc = finalRoot +: (acc ++ otherProjects.projects)
|
||||
val newGenerated =
|
||||
generated ++ otherProjects.generatedConfigClassFiles ++ generatedConfigClassFiles
|
||||
loadTransitive1(newProjects, newAcc, newGenerated, finalRoot.commonSettings)
|
||||
loadTransitive1(
|
||||
newProjects,
|
||||
newAcc,
|
||||
newGenerated,
|
||||
thisProjectScoped(finalRoot.commonSettings)
|
||||
)
|
||||
case Nil =>
|
||||
val projectIds = acc.map(_.id).mkString("(", ", ", ")")
|
||||
log.debug(s"[Loading] Done in $buildBase, returning: $projectIds")
|
||||
|
||||
@@ -5,6 +5,16 @@ scalaVersion := scala212
|
||||
val o = "com.example"
|
||||
organization := o
|
||||
|
||||
lazy val aa = settingKey[Seq[String]]("")
|
||||
Global / aa := Seq("initial-value")
|
||||
// explicitly ThisBuild-scoped settings must apply exactly once, not once per project (#9668)
|
||||
ThisBuild / aa += "added-value"
|
||||
|
||||
lazy val bb = settingKey[Seq[String]]("")
|
||||
Global / bb := Seq("initial-value")
|
||||
// ThisProject means "current project", so it must still apply once per project, unlike ThisBuild
|
||||
ThisProject / bb += "added-value"
|
||||
|
||||
lazy val foo = project
|
||||
lazy val bar = project
|
||||
.settings(
|
||||
@@ -24,5 +34,17 @@ check := {
|
||||
assert((bar / organization).value == "com.example.bar", s"unexpected bar / organization = {(bar / organization).value}")
|
||||
// Test that baz/build.sbt bare settings get loaded
|
||||
assert((baz / organization).value == "com.example.baz", s"unexpected baz/organization")
|
||||
|
||||
// Test that ThisBuild / aa += is applied exactly once, not once per project (#9668)
|
||||
val expectedAa = Seq("initial-value", "added-value")
|
||||
assert((foo / aa).value == expectedAa, s"(foo / aa).value: ${(foo / aa).value}")
|
||||
assert((bar / aa).value == expectedAa, s"(bar / aa).value: ${(bar / aa).value}")
|
||||
assert((baz / aa).value == expectedAa, s"(baz / aa).value: ${(baz / aa).value}")
|
||||
|
||||
// Test that ThisProject / bb += still applies once per project (#9668)
|
||||
val expectedBb = Seq("initial-value", "added-value")
|
||||
assert((foo / bb).value == expectedBb, s"(foo / bb).value: ${(foo / bb).value}")
|
||||
assert((bar / bb).value == expectedBb, s"(bar / bb).value: ${(bar / bb).value}")
|
||||
assert((baz / bb).value == expectedBb, s"(baz / bb).value: ${(baz / bb).value}")
|
||||
}
|
||||
check / aggregate := false
|
||||
|
||||
@@ -6,6 +6,16 @@ scalaVersion := scala212
|
||||
val o = "com.example"
|
||||
organization := o
|
||||
|
||||
lazy val aa = settingKey[Seq[String]]("")
|
||||
Global / aa := Seq("initial-value")
|
||||
// explicitly ThisBuild-scoped settings must apply exactly once, not once per project (#9668)
|
||||
ThisBuild / aa += "added-value"
|
||||
|
||||
lazy val bb = settingKey[Seq[String]]("")
|
||||
Global / bb := Seq("initial-value")
|
||||
// ThisProject means "current project", so it must still apply once per project, unlike ThisBuild
|
||||
ThisProject / bb += "added-value"
|
||||
|
||||
lazy val root = rootProject
|
||||
.autoAggregate
|
||||
|
||||
@@ -34,5 +44,21 @@ LocalRootProject / check := {
|
||||
assert((baz / organization).value == "com.example.baz")
|
||||
// Test that baz/build.sbt settings don't leak onto qux, processed right after it (#9517)
|
||||
assert((qux / organization).value == o, s"(qux / organization).value: ${(qux / organization).value}")
|
||||
|
||||
// Test that ThisBuild / aa += is applied exactly once, not once per project (#9668)
|
||||
val expectedAa = Seq("initial-value", "added-value")
|
||||
assert((root / aa).value == expectedAa, s"(root / aa).value: ${(root / aa).value}")
|
||||
assert((foo / aa).value == expectedAa, s"(foo / aa).value: ${(foo / aa).value}")
|
||||
assert((bar / aa).value == expectedAa, s"(bar / aa).value: ${(bar / aa).value}")
|
||||
assert((baz / aa).value == expectedAa, s"(baz / aa).value: ${(baz / aa).value}")
|
||||
assert((qux / aa).value == expectedAa, s"(qux / aa).value: ${(qux / aa).value}")
|
||||
|
||||
// Test that ThisProject / bb += still applies once per project (#9668)
|
||||
val expectedBb = Seq("initial-value", "added-value")
|
||||
assert((root / bb).value == expectedBb, s"(root / bb).value: ${(root / bb).value}")
|
||||
assert((foo / bb).value == expectedBb, s"(foo / bb).value: ${(foo / bb).value}")
|
||||
assert((bar / bb).value == expectedBb, s"(bar / bb).value: ${(bar / bb).value}")
|
||||
assert((baz / bb).value == expectedBb, s"(baz / bb).value: ${(baz / bb).value}")
|
||||
assert((qux / bb).value == expectedBb, s"(qux / bb).value: ${(qux / bb).value}")
|
||||
}
|
||||
check / aggregate := false
|
||||
|
||||
Reference in New Issue
Block a user