diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 1c24bda64..9cabf774a 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -3633,6 +3633,7 @@ object Classpaths { .withArtifactFilter( updateConfig0.artifactFilter.map(af => af.withInverted(!af.inverted)) ) + .withMissingOk(true) val app = appConfiguration.value val srcTypes = sourceArtifactTypes.value val docTypes = docArtifactTypes.value @@ -3718,11 +3719,14 @@ object Classpaths { val ref = thisProjectRef.value val unit = loadedBuild.value.units(ref.build).unit val converter = unit.converter - val pluginClasspath = unit.plugins.pluginData.dependencyClasspath.toVector + val pluginData = unit.plugins.pluginData + val pluginInternalCp = pluginData.internalDependencyClasspath.toVector + val pluginClasspath = pluginData.dependencyClasspath.toVector + val cp = pluginClasspath.diff(pluginInternalCp) // Exclude directories: an approximation to whether they've been published // Note: it might be a redundant legacy from sbt 0.13/1.x times where the classpath contained directories // but it's left just in case - val pluginJars = pluginClasspath.filter: x => + val pluginJars = cp.filter: x => !Files.isDirectory(converter.toPath(x.data)) val pluginIDs: Vector[ModuleID] = pluginJars.flatMap(_.get(moduleIDStr).map: str => moduleIdJsonKeyFormat.read(str)) diff --git a/main/src/main/scala/sbt/EvaluateTask.scala b/main/src/main/scala/sbt/EvaluateTask.scala index a7eb82ba8..208407585 100644 --- a/main/src/main/scala/sbt/EvaluateTask.scala +++ b/main/src/main/scala/sbt/EvaluateTask.scala @@ -142,6 +142,10 @@ object EvaluateTaskConfig { ) extends EvaluateTaskConfig } +/** + * @param internalDependencyClasspath internal classpath entries from the metabuild that are used to exclude + * them when resolving/retrieving classifiers for sbt. + */ final case class PluginData( dependencyClasspath: Def.Classpath, definitionClasspath: Def.Classpath, @@ -155,13 +159,28 @@ final case class PluginData( managedSources: Seq[File], buildTarget: Option[BuildTargetIdentifier], converter: FileConverter, + internalDependencyClasspath: Def.Classpath, ) { val classpath: Def.Classpath = definitionClasspath ++ dependencyClasspath } object PluginData { private[sbt] def apply(dependencyClasspath: Def.Classpath, converter: FileConverter): PluginData = - PluginData(dependencyClasspath, Nil, None, None, Nil, Nil, Nil, Nil, Nil, Nil, None, converter) + PluginData( + dependencyClasspath, + Nil, + None, + None, + Nil, + Nil, + Nil, + Nil, + Nil, + Nil, + None, + converter, + Nil + ) } object EvaluateTask { diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index 88da9128f..0960d853e 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -1381,6 +1381,7 @@ private[sbt] object Load { isMetaBuild :== true, pluginData := Def.uncached { val prod = (Configurations.Runtime / exportedProducts).value + val internalCp = (Configurations.Runtime / internalDependencyClasspath).value val cp = (Configurations.Runtime / fullClasspath).value val opts = (Configurations.Compile / scalacOptions).value val javaOpts = (Configurations.Compile / javacOptions).value @@ -1403,6 +1404,7 @@ private[sbt] object Load { managedSrcs, Some(buildTarget), converter, + internalCp, ) }, onLoadMessage := ("loading project definition from " + baseDirectory.value) @@ -1670,6 +1672,7 @@ final case class LoadBuildConfiguration( Nil, None, converter, + Nil ) case None => PluginData(globalPluginClasspath, converter) } diff --git a/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/build.sbt b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/build.sbt new file mode 100644 index 000000000..c0f23c1d7 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/build.sbt @@ -0,0 +1,39 @@ +lazy val root = (project in file(".")) + .settings( + // Verify that local plugins work + TaskKey[Unit]("checkLocalPlugins") := Def.uncached { + val localResult = localPluginCheck.value + val customResult = customPluginCheck.value + assert(localResult == "local-plugin-active", s"Expected local plugin to be active, got: $localResult") + assert(customResult == "custom plugin", s"Expected custom plugin to be active, got: $customResult") + }, + + // Verify that the dependencies in updateSbtClassifiers / classifiersModule do not include the local plugins but do include + // other declared dependencies + TaskKey[Unit]("checkClassifiersModule") := Def.uncached { + val mod = (updateSbtClassifiers / classifiersModule).value + val deps = mod.dependencies + val actual = deps.map(m => s"${m.organization}:${m.name}").sorted.toSet + + val expected = Set( + "org.scala-sbt:sbt", + "junit:junit", + "com.eed3si9n:sbt-buildinfo_sbt2_3", + "org.hamcrest:hamcrest-core", + "com.eed3si9n.manifesto:manifesto_3", + "org.scala-lang:scala3-library_3", + "org.scala-lang:scala-library", + "org.typelevel:cats-core_3", + "org.typelevel:cats-kernel_3" + ) + + assert( + actual == expected, + s""" + |ClassifiersModule dependencies mismatch. + |Expected: ${expected.mkString(", ")} + |Actual: ${actual.mkString(", ")} + """.stripMargin + ) + } + ) diff --git a/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/project/LocalPlugin.scala b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/project/LocalPlugin.scala new file mode 100644 index 000000000..1eee43635 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/project/LocalPlugin.scala @@ -0,0 +1,17 @@ +import sbt.* +import sbt.Keys.* + +object LocalPlugin extends AutoPlugin { + override def requires: Plugins = plugins.JvmPlugin + override def trigger: PluginTrigger = allRequirements + + object autoImport { + val localPluginCheck = taskKey[String]("A task provided by the local plugin") + } + + import autoImport.* + + override lazy val projectSettings: Seq[Setting[?]] = Seq( + localPluginCheck := "local-plugin-active" + ) +} diff --git a/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/project/build.sbt b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/project/build.sbt new file mode 100644 index 000000000..de5bb733e --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/project/build.sbt @@ -0,0 +1,16 @@ +lazy val meta = (project in file(".")) + .settings( + // Just to make the test more comprehensive and check whether the additional libraries/plugins + // are present in updateSbtClassifiers/classifiersModule. + addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1"), + libraryDependencies += "junit" % "junit" % "4.13.2" + ) + .dependsOn(customPlugin) + +lazy val customPlugin = (project in file("custom")) + .enablePlugins(SbtPlugin) + .settings( + libraryDependencies ++= Seq( + "org.typelevel" %% "cats-core" % "2.13.0", + ) + ) \ No newline at end of file diff --git a/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/project/custom/src/main/scala/CustomPlugin.scala b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/project/custom/src/main/scala/CustomPlugin.scala new file mode 100644 index 000000000..c0b64b856 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/project/custom/src/main/scala/CustomPlugin.scala @@ -0,0 +1,20 @@ +import sbt.* +import sbt.Keys.* + +object CustomPlugin extends AutoPlugin { + override def requires: Plugins = plugins.JvmPlugin + override def trigger: PluginTrigger = allRequirements + + object autoImport { + val customPluginCheck = taskKey[String]("A task provided by the custom plugin") + } + + import autoImport.* + + override lazy val projectSettings: Seq[Setting[?]] = Seq( + customPluginCheck := { + import cats.implicits.* + List("custom", " ", "plugin").combineAll + } + ) +} diff --git a/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/test b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/test new file mode 100644 index 000000000..8a6a426a0 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-local-plugin/test @@ -0,0 +1,8 @@ +# Verify the local plugins are loaded and active +> checkLocalPlugins + +# Verify the local plugins are not included in the classifiersModule dependencies +> checkClassifiersModule + +# Also verify updateSbtClassifiers itself succeeds +> updateSbtClassifiers diff --git a/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-missingok/build.sbt b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-missingok/build.sbt new file mode 100644 index 000000000..1b0629c23 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-missingok/build.sbt @@ -0,0 +1,11 @@ +lazy val root = (project in file(".")) + .settings( + // Inject a non-existent module into the classifiers module dependencies + // to simulate a scenario where classifier artifacts can't be downloaded. + // With missingOk=true, updateSbtClassifiers should still succeed. + updateSbtClassifiers / classifiersModule := { + val mod = (updateSbtClassifiers / classifiersModule).value + val fakeModule = "com.example.nonexistent" % "fake-library" % "0.0.1" + mod.withDependencies(mod.dependencies :+ fakeModule) + }, + ) diff --git a/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-missingok/project/plugins.sbt b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-missingok/project/plugins.sbt new file mode 100644 index 000000000..ddfa827f9 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-missingok/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1") diff --git a/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-missingok/test b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-missingok/test new file mode 100644 index 000000000..00ad751bf --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/i8679-update-sbt-classifiers-missingok/test @@ -0,0 +1 @@ +> updateSbtClassifiers