From e6666e4b1014ecc42f2a92298fb614c7eef781d7 Mon Sep 17 00:00:00 2001 From: eugene yokota Date: Sat, 30 May 2026 20:16:58 -0400 Subject: [PATCH] [2.0.x] Fix duplicate autoplugins packageBin mappings (#9255) (#9273) Deduplicate executable jar package entries. In the issue, the duplicate was something like: (resource_managed/.../sbt/sbt.autoplugins, "sbt/sbt.autoplugins") (classes/.../sbt/sbt.autoplugins, "sbt/sbt.autoplugins") This is because copyResources copied autoplugin metadata to the classes directory. Solution: when composing the packageBinMappings, differentiate between artifacts that come from class directory and from outside of it. If there is a duplicate, prefer resource that comes from outside of it. Co-authored-by: Anatolii Kmetiuk --- main/src/main/scala/sbt/Defaults.scala | 12 ++++++++++-- .../duplicate-autoplugins-packagebin/build.sbt | 4 ++++ .../src/main/scala/example/ScalacOptionsPlugin.scala | 5 +++++ .../plugins/duplicate-autoplugins-packagebin/test | 3 +++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/build.sbt create mode 100644 sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/scalac-options/src/main/scala/example/ScalacOptionsPlugin.scala create mode 100644 sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/test diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 888d42cdb..1273a3337 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1722,10 +1722,18 @@ object Defaults extends BuildCommon { def packageBinMappings: Initialize[Task[Seq[(HashedVirtualFileRef, String)]]] = Def.task { val converter = fileConverter.value + val classDirPath = classDirectory.value.toPath.toAbsolutePath.normalize val xs = products.value - xs + val allMappings = xs .flatMap(Path.allSubpaths) - .withFilter(_._1.isFile()) + .filter(_._1.isFile()) + val resourcePaths = allMappings.collect { + case (p, path) if !p.toPath.toAbsolutePath.normalize.startsWith(classDirPath) => path + }.toSet + allMappings + .filterNot { (p, path) => + resourcePaths(path) && p.toPath.toAbsolutePath.normalize.startsWith(classDirPath) + } .map { (p, path) => val vf = converter.toVirtualFile(p.toPath()) (vf: HashedVirtualFileRef) -> path diff --git a/sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/build.sbt b/sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/build.sbt new file mode 100644 index 000000000..40734b4c1 --- /dev/null +++ b/sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/build.sbt @@ -0,0 +1,4 @@ +ThisBuild / organization := "com.example" +ThisBuild / version := "0.1.0-SNAPSHOT" + +val `scalac-options` = project.enablePlugins(SbtPlugin) diff --git a/sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/scalac-options/src/main/scala/example/ScalacOptionsPlugin.scala b/sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/scalac-options/src/main/scala/example/ScalacOptionsPlugin.scala new file mode 100644 index 000000000..3d8f9eae2 --- /dev/null +++ b/sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/scalac-options/src/main/scala/example/ScalacOptionsPlugin.scala @@ -0,0 +1,5 @@ +package example + +import sbt.* + +object ScalacOptionsPlugin extends AutoPlugin diff --git a/sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/test b/sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/test new file mode 100644 index 000000000..e00f9cbb0 --- /dev/null +++ b/sbt-app/src/sbt-test/plugins/duplicate-autoplugins-packagebin/test @@ -0,0 +1,3 @@ +> scalac-options / Compile / packageBin +> scalac-options / Compile / copyResources +> scalac-options / Compile / packageBin