From b3d2733fb1a7594969d6b85d3ef24254b855fe4b Mon Sep 17 00:00:00 2001 From: Dream <42954461+eureka928@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:53:39 -0400 Subject: [PATCH] [2.x] fix: Remove projectDescriptors key that leaks Ivy types (#8894) **Problem** The projectDescriptors key in sbt.Keys leaked Ivy classes (ModuleRevisionId, ModuleDescriptor) through its API surface. No external plugins use this key. **Solution** Remove projectDescriptors entirely. IvyDependencyPlugin now uses its internal depMap method directly for projectResolverTask and coursierExtraProjectsTask, eliminating unnecessary Any casts. The descriptors field is also removed from GlobalPluginData. Fixes sbt#8865 --- build.sbt | 13 ++++++++++++ main/src/main/scala/sbt/Defaults.scala | 1 - main/src/main/scala/sbt/Keys.scala | 1 - .../sbt/coursierint/CoursierInputsTasks.scala | 2 +- .../scala/sbt/internal/GlobalPlugin.scala | 4 ---- .../sbt/plugins/IvyDependencyPlugin.scala | 20 ++++--------------- 6 files changed, 18 insertions(+), 23 deletions(-) diff --git a/build.sbt b/build.sbt index e59dd0485..cc8a2cd75 100644 --- a/build.sbt +++ b/build.sbt @@ -745,6 +745,19 @@ lazy val mainProj = (project in file("main")) exclude[DirectMissingMethodProblem]("sbt.Classpaths.mkIvyConfiguration"), exclude[MissingClassProblem]("sbt.internal.librarymanagement.IvyXml"), exclude[MissingClassProblem]("sbt.internal.librarymanagement.IvyXml$"), + // Removed projectDescriptors key (sbt#8865) + exclude[DirectMissingMethodProblem]("sbt.Keys.projectDescriptors"), + // Removed descriptors field from GlobalPluginData (sbt#8865) + exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData.apply"), + exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData.this"), + exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData.descriptors"), + exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData.copy"), + exclude[IncompatibleResultTypeProblem]("sbt.internal.GlobalPluginData.copy$default$3"), + exclude[IncompatibleResultTypeProblem]("sbt.internal.GlobalPluginData.copy$default$4"), + exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData.copy$default$6"), + exclude[IncompatibleResultTypeProblem]("sbt.internal.GlobalPluginData._3"), + exclude[IncompatibleResultTypeProblem]("sbt.internal.GlobalPluginData._4"), + exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData._6"), ), ) .dependsOn(lmCore, lmCoursierShadedPublishing) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 83e7242dc..081cd20cd 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -3134,7 +3134,6 @@ object Classpaths { makePom / artifact := Artifact.pom(moduleName.value), projectID := defaultProjectID.value, projectID := pluginProjectID.value, - projectDescriptors := Def.uncached(Map.empty[Any, Any]), updateConfiguration := { // Tell the UpdateConfiguration which artifact types are special (for sources and javadocs) val specialArtifactTypes = sourceArtifactTypes.value.toSet union docArtifactTypes.value.toSet diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 3fc94743c..86f16699f 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -635,7 +635,6 @@ object Keys { @transient val publishTo = taskKey[Option[Resolver]]("The resolver to publish to.").withRank(ASetting) val artifacts = settingKey[Seq[Artifact]]("The artifact definitions for the current module. Must be consistent with " + packagedArtifacts.key.label + ".").withRank(BSetting) - val projectDescriptors = taskKey[Map[Any, Any]]("Project dependency map for the inter-project resolver.").withRank(DTask) val autoUpdate = settingKey[Boolean]("").withRank(Invisible) val retrieveManaged = settingKey[Boolean]("If true, enables retrieving dependencies to the current build. Otherwise, dependencies are used directly from the cache.").withRank(BSetting) val retrieveManagedSync = settingKey[Boolean]("If true, enables synchronizing the dependencies retrieved to the current build by removed unneeded files.").withRank(BSetting) diff --git a/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala b/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala index 2bc721185..cbfe1615a 100644 --- a/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala +++ b/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala @@ -103,7 +103,7 @@ object CoursierInputsTasks { private[sbt] def coursierExtraProjectsTask: Def.Initialize[sbt.Task[Seq[CProject]]] = { Def.task { // Coursier handles inter-project dependencies natively via csrInterProjectDependencies. - // The Ivy-typed projectDescriptors are only populated when IvyDependencyPlugin is enabled. + // When IvyDependencyPlugin is enabled, it overrides csrExtraProjects with Ivy descriptors. Seq.empty[CProject] } } diff --git a/main/src/main/scala/sbt/internal/GlobalPlugin.scala b/main/src/main/scala/sbt/internal/GlobalPlugin.scala index bb3c63fa8..adb6e804d 100644 --- a/main/src/main/scala/sbt/internal/GlobalPlugin.scala +++ b/main/src/main/scala/sbt/internal/GlobalPlugin.scala @@ -31,7 +31,6 @@ object GlobalPlugin { // static here meaning that the relevant tasks for the global plugin have already been evaluated def inject(gp: GlobalPluginData): Seq[Setting[?]] = Seq[Setting[?]]( - projectDescriptors ~= { _ ++ gp.descriptors }, projectDependencies ++= gp.projectID +: gp.dependencies, resolvers := { val rs = resolvers.value @@ -77,12 +76,10 @@ object GlobalPlugin { val taskInit = Def.task { val intcp = (Runtime / internalDependencyClasspath).value val prods = (Runtime / exportedProducts).value - val depMap = projectDescriptors.value GlobalPluginData( projectID.value, projectDependencies.value, - depMap, resolvers.value.toVector, (Runtime / fullClasspath).value, (prods ++ intcp).distinct @@ -121,7 +118,6 @@ object GlobalPlugin { final case class GlobalPluginData( projectID: ModuleID, dependencies: Seq[ModuleID], - descriptors: Map[Any, Any], resolvers: Vector[Resolver], fullClasspath: Classpath, internalClasspath: Classpath diff --git a/sbt-ivy/src/main/scala/sbt/plugins/IvyDependencyPlugin.scala b/sbt-ivy/src/main/scala/sbt/plugins/IvyDependencyPlugin.scala index 6cb1234b3..791d75e05 100644 --- a/sbt-ivy/src/main/scala/sbt/plugins/IvyDependencyPlugin.scala +++ b/sbt-ivy/src/main/scala/sbt/plugins/IvyDependencyPlugin.scala @@ -93,15 +93,6 @@ object IvyDependencyPlugin extends AutoPlugin: ) .value ), - projectDescriptors := Def.uncached( - Def - .ifS(Def.task { useIvy.value })( - Def.task { depMap.value.asInstanceOf[Map[Any, Any]] } - )( - Def.task { Map.empty[Any, Any] } - ) - .value - ), projectResolver := Def.uncached( Def .ifS(Def.task { useIvy.value })( @@ -166,9 +157,8 @@ object IvyDependencyPlugin extends AutoPlugin: } private def projectResolverTask: Initialize[Task[Resolver]] = - projectDescriptors.map { m => - val typedMap = m.asInstanceOf[Map[ModuleRevisionId, ModuleDescriptor]] - val resolver = new ProjectResolver(ProjectResolver.InterProject, typedMap) + depMap.map { m => + val resolver = new ProjectResolver(ProjectResolver.InterProject, m) new RawRepository(resolver, resolver.getName) } @@ -194,10 +184,8 @@ object IvyDependencyPlugin extends AutoPlugin: Def.task { val projects = csrInterProjectDependencies.value val projectModules = projects.map(_.module).toSet - projectDescriptors.value - .map { (k, v) => - val id = k.asInstanceOf[ModuleRevisionId] - val desc = v.asInstanceOf[ModuleDescriptor] + depMap.value + .map { (id, desc) => moduleFromIvy(id) -> desc } .filter { case (module, _) =>