From ed96b4e1e011da6ff7a688f4ceb0301d66013805 Mon Sep 17 00:00:00 2001 From: Dream <42954461+eureka928@users.noreply.github.com> Date: Wed, 4 Mar 2026 16:22:48 -0500 Subject: [PATCH] [2.x] fix: Populate explicitArtifacts from classifier in UpdateReport (#8874) **Problem** When a dependency is declared with a classifier (e.g., classifier "linux-x86_64"), the UpdateReport > ModuleReport.module.explicitArtifacts is empty. The classifier data is available as Publication objects during Coursier resolution but is lost when SbtUpdateReport reconstructs the ModuleID. **Solution** In SbtUpdateReport.moduleReport, extract explicit artifacts from Publications with non-empty classifiers and apply them to the ModuleID used by ModuleReport. This is done on a per-report copy to avoid mutating the shared moduleId cache. Fixes #5491 --- .../lmcoursier/internal/SbtUpdateReport.scala | 19 ++++++++++++++++++- .../classifier-report/build.sbt | 17 +++++++++++++++++ .../classifier-report/test | 1 + 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 sbt-app/src/sbt-test/dependency-management/classifier-report/build.sbt create mode 100644 sbt-app/src/sbt-test/dependency-management/classifier-report/test diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala b/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala index 1dcb31f63..678bb1cc7 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala @@ -95,6 +95,18 @@ private[internal] object SbtUpdateReport { artifact((dependency.module, infoProperties(project).toMap, pub, artifact0, classLoaders)) } + val explicitArts = artifacts + .collect { + case (pub, _, _) if pub.classifier.nonEmpty => + sbt.librarymanagement + .Artifact(pub.name) + .withType(pub.`type`.value) + .withExtension(pub.ext.value) + .withClassifier(Some(pub.classifier.value)) + } + .distinct + .toVector + val publicationDate = project.info.publication.map { dt => new GregorianCalendar(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) } @@ -113,8 +125,13 @@ private[internal] object SbtUpdateReport { ) } + val modId = moduleId((dependency, project.version, infoProperties(project).toMap)) + val modIdWithArts = + if explicitArts.nonEmpty then modId.withExplicitArtifacts(explicitArts) + else modId + val rep = ModuleReport( - moduleId((dependency, project.version, infoProperties(project).toMap)), + modIdWithArts, sbtArtifacts.toVector, sbtMissingArtifacts.toVector ) diff --git a/sbt-app/src/sbt-test/dependency-management/classifier-report/build.sbt b/sbt-app/src/sbt-test/dependency-management/classifier-report/build.sbt new file mode 100644 index 000000000..56b15ea03 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/classifier-report/build.sbt @@ -0,0 +1,17 @@ +lazy val check = taskKey[Unit]("check classifier in update report") + +lazy val root = (project in file(".")).settings( + scalaVersion := "2.13.16", + libraryDependencies += "io.netty" % "netty-transport-native-epoll" % "4.1.118.Final" classifier "linux-x86_64", + check := { + val report = update.value + val modules = report.configurations.flatMap(_.modules) + val nettyModule = modules.find(_.module.name == "netty-transport-native-epoll") + .getOrElse(sys.error("netty-transport-native-epoll not found in update report")) + val explicitArts = nettyModule.module.explicitArtifacts + assert(explicitArts.nonEmpty, s"Expected non-empty explicitArtifacts, got: $explicitArts") + val classifiers = explicitArts.flatMap(_.classifier) + assert(classifiers.contains("linux-x86_64"), + s"Expected classifier 'linux-x86_64' in explicitArtifacts, got: $classifiers") + }, +) diff --git a/sbt-app/src/sbt-test/dependency-management/classifier-report/test b/sbt-app/src/sbt-test/dependency-management/classifier-report/test new file mode 100644 index 000000000..15675b169 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/classifier-report/test @@ -0,0 +1 @@ +> check