[2.x] fix: Name the platform in CrossVersion(module, scalaModuleInfo) (#9620)

**Problem**
CrossVersion(module, scalaModuleInfo) is handed everything needed to name an
artifact, including ScalaModuleInfo.platform, and the name it returns is what
a caller publishes or resolves under. Nothing covers what it does with the
platform.

**Solution**
Name the artifact in full: platform suffix before cross suffix, matching the
coordinate (sbt/sbt#9117), through addPlatformSuffix, which already knows that
jvm contributes no suffix.

Generated-by: Claude Opus 5
This commit is contained in:
Albert Meltzer 2026-08-17 23:04:57 -07:00 committed by GitHub
parent 27c3f035e5
commit 8c76375eab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 48 additions and 19 deletions

View File

@ -139,9 +139,17 @@ private[librarymanagement] abstract class CrossVersionFunctions {
append(c.prefix + compat + c.suffix)
}
/** Constructs the cross-version function defined by `module` and `is`, if one is configured. */
/**
* Constructs the cross-version function defined by `module` and `is`, if one is configured.
*
* The function names the artifact in full, platform suffix before cross suffix, matching the
* coordinate (sbt/sbt#9117). On sbt 1 the platform was a prefix inside the CrossVersion itself,
* so callers rebuilding a name from it got the platform for free; sbt 2 keeps the platform in a
* field of its own, and this is where the two are put back together.
*/
def apply(module: ModuleID, is: ScalaModuleInfo): Option[String => String] =
CrossVersion(module.crossVersion, is.scalaFullVersion, is.scalaBinaryVersion)
.map(cross => name => cross(addPlatformSuffix(name, module.platformOpt, is.platform)))
/** Constructs the cross-version function defined by `module` and `is`, if one is configured. */
def apply(module: ModuleID, is: Option[ScalaModuleInfo]): Option[String => String] =

View File

@ -2,6 +2,7 @@ package sbt.librarymanagement
import sbt.internal.librarymanagement.UnitSpec
import CrossVersion.*
import sbt.librarymanagement.syntax.*
import scala.annotation.nowarn
class CrossVersionTest extends UnitSpec {
@ -413,4 +414,39 @@ class CrossVersionTest extends UnitSpec {
"artefact_3"
)
}
private def scalaInfo(platform: Option[String]) = ScalaModuleInfo(
"2.13.15",
"2.13",
Vector.empty,
true,
false,
true,
"org.scala-lang",
Vector.empty,
platform,
)
private def named(module: ModuleID, platform: Option[String]) =
CrossVersion(module, scalaInfo(platform)).map(_(module.name))
"CrossVersion(module, scalaModuleInfo)" should "name a JVM artifact with the cross suffix" in {
named("com.example" %% "foo" % "1.0", Some("jvm")) shouldBe Some("foo_2.13")
}
it should "put the platform suffix before the cross suffix" in {
named("com.example" %% "foo" % "1.0", Some("sjs1")) shouldBe Some("foo_sjs1_2.13")
}
it should "take the platform from the module over the project" in {
val m = ("com.example" %% "foo" % "1.0").platform("native0.5")
named(m, Some("sjs1")) shouldBe Some("foo_native0.5_2.13")
}
it should "add no platform suffix when there is no platform" in {
named("com.example" %% "foo" % "1.0", None) shouldBe Some("foo_2.13")
}
it should "name nothing when the module is not cross-versioned" in {
named(
("com.example" % "foo" % "1.0").withCrossVersion(CrossVersion.disabled),
Some("sjs1")
) shouldBe None
}
}

View File

@ -234,18 +234,8 @@ object IvyActions {
}
private def crossVersionMap(moduleSettings: ModuleSettings): Option[String => String] =
moduleSettings match {
case i: InlineConfiguration =>
// Platform suffix before cross suffix, matching the coordinate (sbt/sbt#9117).
CrossVersion(i.module, i.scalaModuleInfo).map { fn => (name: String) =>
fn(
CrossVersion.addPlatformSuffix(
name,
i.module.platformOpt,
i.scalaModuleInfo.flatMap(_.platform)
)
)
}
case _ => None
case i: InlineConfiguration => CrossVersion(i.module, i.scalaModuleInfo)
case _ => None
}
def mapArtifacts(
module: ModuleDescriptor,

View File

@ -61,14 +61,9 @@ private[sbt] object PomGenerator:
</project>
private def crossVersionDep(dep: ModuleID, scalaInfo: Option[ScalaModuleInfo]): ModuleID =
// Platform suffix before cross suffix, matching the coordinate (sbt/sbt#9117).
val base = dep.crossVersion match
case _: Disabled => dep.name
case _ =>
CrossVersion.addPlatformSuffix(dep.name, dep.platformOpt, scalaInfo.flatMap(_.platform))
val crossFn = CrossVersion(dep, scalaInfo)
val crossDep = crossFn match
case Some(fn) => dep.withName(fn(base)).withCrossVersion(CrossVersion.disabled)
case Some(fn) => dep.withName(fn(dep.name)).withCrossVersion(CrossVersion.disabled)
case None => dep
if crossDep.exclusions.isEmpty || scalaInfo.isEmpty then crossDep
else