From b0601b4c6ce03ac75c45679e9a5238c22c78aea3 Mon Sep 17 00:00:00 2001 From: bitloi <89318445+bitloi@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:40:00 -0500 Subject: [PATCH] [2.x] fix: Fixes subproject deps with different Scala versions (#8681) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a project depended on another project that was built with a different Scala binary version (e.g. 2.12 vs 2.13), compilation could fail with "not found: value X" because resolution was asking for the wrong artifact. This change updates how we build the `ModuleID` for inter-project dependencies in `projectDependenciesTask`: we now request the dependency’s Scala binary version (e.g. `bar_2.12`) instead of the current project’s, so the resolver can find the right artifact. We keep existing behavior for `Disabled` and `Constant` cross-version, and add a small safeguard in the default case when the dependency’s Scala version differs from the current project’s. --- main/src/main/scala/sbt/Defaults.scala | 29 +++++++++++++++++-- .../bar/src/main/scala/Bar.scala | 3 ++ .../baz/src/main/scala/Main.scala | 9 ++++++ .../build.sbt | 19 ++++++++++++ .../i4847-inter-project-variant-scala/test | 5 ++++ 5 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/bar/src/main/scala/Bar.scala create mode 100644 sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/baz/src/main/scala/Main.scala create mode 100644 sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/build.sbt create mode 100644 sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/test diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index abe96430b..ca4e2d967 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -4230,9 +4230,34 @@ object Classpaths { .withCrossVersion(CrossVersion.constant(b.prefix + depSBV)) .withConfigurations(dep.configuration) .withExplicitArtifacts(Vector.empty) - case _ if depAuto && VirtualAxis.isScala2Scala3Sandwich(sbv, depSBV) => + case b: CrossVersion.Binary if sbv != depSBV => depProjId - .withCrossVersion(CrossVersion.constant(depSBV)) + .withCrossVersion(CrossVersion.constant(b.prefix + depSBV + b.suffix)) + .withConfigurations(dep.configuration) + .withExplicitArtifacts(Vector.empty) + case f: CrossVersion.Full if sbv != depSBV => + val cross = (dep.project / scalaVersion) + .get(data) + .map(sv => CrossVersion.constant(f.prefix + sv + f.suffix)) + .getOrElse(depProjId.crossVersion) + depProjId + .withCrossVersion(cross) + .withConfigurations(dep.configuration) + .withExplicitArtifacts(Vector.empty) + // For3Use2_13/For2_13Use3 publish under compat suffix (e.g. bar_2.13 on Scala 3), + // not raw depSBV; sandwich case uses constant(depSBV) so would request wrong artifact. + case c: sbt.librarymanagement.For3Use2_13 if sbv != depSBV => + val compat = + if (depSBV == "3" || depSBV.startsWith("3.0.0")) "2.13" + else depSBV + depProjId + .withCrossVersion(CrossVersion.constant(c.prefix + compat + c.suffix)) + .withConfigurations(dep.configuration) + .withExplicitArtifacts(Vector.empty) + case c: sbt.librarymanagement.For2_13Use3 if sbv != depSBV => + val compat = if (depSBV == "2.13") "3" else depSBV + depProjId + .withCrossVersion(CrossVersion.constant(c.prefix + compat + c.suffix)) .withConfigurations(dep.configuration) .withExplicitArtifacts(Vector.empty) case _ => diff --git a/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/bar/src/main/scala/Bar.scala b/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/bar/src/main/scala/Bar.scala new file mode 100644 index 000000000..b8452f864 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/bar/src/main/scala/Bar.scala @@ -0,0 +1,3 @@ +object Bar { + def value: String = "from-bar-2.12" +} diff --git a/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/baz/src/main/scala/Main.scala b/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/baz/src/main/scala/Main.scala new file mode 100644 index 000000000..b89b1e043 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/baz/src/main/scala/Main.scala @@ -0,0 +1,9 @@ +import java.nio.file.Files +import java.nio.file.Paths + +object Main { + def main(args: Array[String]): Unit = { + val msg = Bar.value + Files.write(Paths.get("baz/output"), msg.getBytes("UTF-8")) + } +} diff --git a/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/build.sbt b/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/build.sbt new file mode 100644 index 000000000..8cb6631f8 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/build.sbt @@ -0,0 +1,19 @@ +// sbt#4847: inter-project deps with variant Scala binary versions. +// bar uses 2.12, baz uses 2.13; baz dependsOn(bar). Resolution must ask for bar_2.12, not bar_2.13. +ThisBuild / organization := "com.example" +ThisBuild / version := "0.1.0-SNAPSHOT" + +lazy val foo = project in file(".") +aggregateProjects(bar, baz) + +lazy val bar = project.settings( + scalaVersion := "2.12.18", + name := "bar", +) + +lazy val baz = project + .dependsOn(bar) + .settings( + scalaVersion := "2.13.12", + name := "baz", + ) diff --git a/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/test b/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/test new file mode 100644 index 000000000..2ead9edfd --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i4847-inter-project-variant-scala/test @@ -0,0 +1,5 @@ +# sbt#4847: baz (2.13) dependsOn bar (2.12); update must resolve bar_2.12, then baz/run must succeed. +$ delete baz/output +> baz/update +> baz/run +$ exists baz/output