[2.0.x] fix: Fix build.sbt file loading in subdirs (#9165) (#9168)

**Problem**
Projects defined in subdirectory build.sbt files are
not correctly resolved to the base directory relative to the file.

**Solution**
Call resolveBase.
This commit is contained in:
eugene yokota 2026-05-03 00:09:27 -04:00 committed by GitHub
parent 7088ad9884
commit 604d2ce129
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 42 additions and 11 deletions

View File

@ -172,8 +172,7 @@ private[sbt] object EvaluateConfigurations {
val compositeProjects = definitions
.values(loader)
.collect { case p: CompositeProject => p }
// todo: resolveBase?
CompositeProject.expand(compositeProjects) // .map(resolveBase(file.getParentFile, _))
CompositeProject.expand(compositeProjects)
}
val loadedDslEntries = dslEntries.map(_.result.apply(loader))
val settings = loadedDslEntries.collect { case DslEntry.ProjectSettings(s) => s }.flatten

View File

@ -16,15 +16,15 @@ import java.nio.file.Path
* Represents the exported contents of a .sbt file. Currently, that includes the list of settings,
* the values of Project vals, and the import statements for all defined vals/defs.
*/
private[sbt] final class LoadedSbtFile(
val settings: Seq[Setting[?]],
val projects: Seq[Project],
val importedDefs: Seq[String],
val manipulations: Seq[Project => Project],
private[sbt] final case class LoadedSbtFile(
settings: Seq[Setting[?]],
projects: Seq[Project],
importedDefs: Seq[String],
manipulations: Seq[Project => Project],
// TODO - we may want to expose a simpler interface on top of here for the set command,
// rather than what we have now...
val definitions: DefinedSbtValues,
val generatedFiles: Seq[Path]
definitions: DefinedSbtValues,
generatedFiles: Seq[Path]
) {
// We still use merge for now. We track originating sbt file in an alternative manner.
def merge(o: LoadedSbtFile): LoadedSbtFile =

View File

@ -30,7 +30,7 @@ import sbt.io.{ GlobFilter, IO }
import sbt.librarymanagement.{ Configuration, Configurations, IvyPaths, Resolver, ScalaArtifacts }
import sbt.nio.Settings
import sbt.util.{ Logger, Show }
import xsbti.{ FileConverter, HashedVirtualFileRef, VirtualFile }
import xsbti.{ FileConverter, HashedVirtualFileRef, PathBasedFile, VirtualFile }
import xsbti.compile.{ ClasspathOptionsUtil, Compilers }
import java.io.File
import java.net.URI
@ -1327,13 +1327,19 @@ private[sbt] object Load {
// TODO - We should import vals defined in other sbt files here, if we wish to
// share. For now, build.sbt files have their own unique namespace.
def loadSettingsFile(src: VirtualFile): LoadedSbtFile =
EvaluateConfigurations.evaluateSbtFile(
val evaluated = EvaluateConfigurations.evaluateSbtFile(
eval(),
src,
IO.readStream(src.input()).linesIterator.toList,
loadedPlugins.detected.imports,
0
)(loader)
evaluated.copy(
projects = evaluated.projects.map: p =>
src match
case file: PathBasedFile => resolveBase(file.toPath.toFile.getParentFile)(p)
case _ => p
)
// How to merge SbtFiles we read into one thing
def merge(ls: Seq[LoadedSbtFile]): LoadedSbtFile = ls.foldLeft(LoadedSbtFile.empty) {
_.merge(_)

View File

@ -0,0 +1,10 @@
lazy val foo = project
@transient
lazy val check = taskKey[Unit]("")
scalaVersion := "3.8.3"
LocalRootProject / check := {
assert((foo / Compile / scalacOptions).value == List("-Xmacro-settings:a:a"),
s"${(foo / Compile / scalacOptions).value}")
}

View File

@ -0,0 +1,3 @@
package example
class A

View File

@ -0,0 +1,3 @@
package example
class A("

View File

@ -0,0 +1,4 @@
lazy val foo = rootProject
.settings(
Compile / scalacOptions += "-Xmacro-settings:a:a"
)

View File

@ -0,0 +1,6 @@
> check
# check that it fails to compile a bad source
-> compile
$ copy-file changes/Good.scala foo/A.scala
> compile