Hi,

>Interesting idea, if you have to patch a high number of packages I guess
>it saves tokens. But in this case I prefer patching the library to
>restore the compatibility (as we do with Guava and Bouncy Castle).

It might also save effort on upgrade as it will automatically update
patches, without the need to invoke LLM at all.

> The last time I checked Gradle didn't support relocation POMs.

I think that's relevant to version-only relocation[1], because the
dependency graph is built using group id/artifact id, so this creates
a cyclic dependency.
This can be worked around with a minor hack, where relocated poms get
an unique prefix so that they are no longer cycling on themselves,
e.g. see attached patch.
With this patch I get a version-only relocation work same as a full one:
---
runtimeClasspath - Runtime classpath of source set 'main'.
\--- org.example:lib:1.0 -> 2.0
     \--- org.example:transitive-dep:1.0
---

Best Regards,
 Vladimir.

[1] 
https://github.com/gradle/gradle/blame/219f16ca1bb39ecaa0d6ef6bb47d07f37d0a33cd/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java#L138

On Thu, Sep 24, 2026 at 12:56 AM Emmanuel Bourg <[email protected]> wrote:
>
> Le 23/09/2026 à 11:31, Vladimir Petko a écrit :
>
> > I did come across the paper that describes an interesting approach. It
> > claims that we can have reproducible results by having the LLM harness
> > generate refactoring recipes[1] rather than directly patching the
> > source code. This also saves tokens, as a single recipe (such as
> > dropping all instances of the dokka plugin) can be applied across
> > multiple packages.
>
> Interesting idea, if you have to patch a high number of packages I guess
> it saves tokens. But in this case I prefer patching the library to
> restore the compatibility (as we do with Guava and Bouncy Castle).
>
>
> > I was also wondering if  moving from maven.rules / maven.ignoreRules
> > to publishing relocation POMs could simplify things. This allows the
> > agent to work directly with the prepared Maven repository and the
> > source tree without struggling with the Gradle plugin that rewrites
> > the artifact coordinates.
>
> The last time I checked Gradle didn't support relocation POMs.
>
>
> > The Kotlin DSL requires Kotlin that requires Intellij IDEA Platform
> > that requires Bazel. Bazel was recently upgraded to 7.7.1 (which is a
> > great news) , but we need 9 for Intellij IDEA  ;( Since those are not
> > backwards compatible I was wondering if the goal is to have 9 as the
> > bazel-bootstrap or we will need a versioned package?
>
> Bazel was introduced "recently" (in 2024), it doesn't come into the
> picture before Kotlin 2.3.20 and Gradle 9.5. We haven't reached this
> point yet.
>
> Emmanuel Bourg
>
commit 15a479db3659b9dbcabe2440ac88adb8b7f724ca
Author: Vladimir Petko <[email protected]>
Date:   Thu Sep 24 09:53:34 2026 +1200

    patch group id for the version-only relocation

diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java
index 8fe4a680099..cab4399d02f 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradlePomModuleDescriptorParser.java
@@ -134,22 +134,16 @@ private void doParsePom(DescriptorParseContext parserSettings, GradlePomModuleDe
 
         ModuleVersionIdentifier relocation = pomReader.getRelocation();
         if (relocation != null) {
+            LOGGER.info(mdBuilder.getComponentIdentifier()
+                + " is relocated to " + relocation
+                + ". Please update your dependencies.");
+            LOGGER.debug("Relocated module will be considered as a dependency");
             if (groupId != null && artifactId != null && artifactId.equals(relocation.getName()) && groupId.equals(relocation.getGroup())) {
-                LOGGER.error("POM relocation to an other version number is not fully supported in Gradle : {} relocated to {}.",
-                    mdBuilder.getComponentIdentifier(), relocation);
-                LOGGER.warn("Please update your dependency to directly use the correct version '{}'.", relocation);
-                LOGGER.warn("Resolution will only pick dependencies of the relocated element.  Artifacts and other metadata will be ignored.");
-                PomReader relocatedModule = parsePomForId(parserSettings, DefaultModuleComponentIdentifier.newId(relocation), new HashMap<>());
-                addDependencies(mdBuilder, relocatedModule);
-            } else {
-                LOGGER.info(mdBuilder.getComponentIdentifier()
-                    + " is relocated to " + relocation
-                    + ". Please update your dependencies.");
-                LOGGER.debug("Relocated module will be considered as a dependency");
-                ModuleComponentSelector selector = DefaultModuleComponentSelector.newSelector(
-                    DefaultModuleIdentifier.newId(relocation.getGroup(), relocation.getName()), new DefaultMutableVersionConstraint(relocation.getVersion()));
-                mdBuilder.addDependencyForRelocation(selector);
+                mdBuilder.setModuleRevId("relocated." + groupId, artifactId, version);
             }
+            ModuleComponentSelector selector = DefaultModuleComponentSelector.newSelector(
+                DefaultModuleIdentifier.newId(relocation.getGroup(), relocation.getName()), new DefaultMutableVersionConstraint(relocation.getVersion()));
+            mdBuilder.addDependencyForRelocation(selector);
         } else {
             overrideDependencyMgtsWithImported(parserSettings, pomReader);
             addDependencies(mdBuilder, pomReader);
@@ -215,9 +209,6 @@ private boolean isDependencyImportScoped(PomDependencyMgt dependencyMgt) {
         return DEPENDENCY_IMPORT_SCOPE.equals(dependencyMgt.getScope());
     }
 
-    private PomReader parsePomForId(DescriptorParseContext parseContext, ModuleComponentIdentifier identifier, Map<String, String> childProperties) throws IOException, SAXException {
-        return parsePomResource(parseContext, parseContext.getMetaDataArtifact(identifier, ArtifactType.MAVEN_POM), childProperties);
-    }
 
     private PomReader parsePomForSelector(DescriptorParseContext parseContext, ModuleComponentSelector selector, Map<String, String> childProperties) throws IOException, SAXException {
         VersionSelector acceptor = mavenVersionSelectorScheme.parseSelector(selector.getVersion());
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultMavenPomMetadataSource.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultMavenPomMetadataSource.java
index afd432e8811..3f67d94c9f2 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultMavenPomMetadataSource.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultMavenPomMetadataSource.java
@@ -17,6 +17,7 @@
 
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleDescriptorHashModuleSource;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
@@ -27,6 +28,7 @@
 import org.gradle.api.internal.artifacts.repositories.resolver.MavenUniqueSnapshotComponentIdentifier;
 import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
 import org.gradle.api.internal.artifacts.repositories.resolver.VersionLister;
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
 import org.gradle.internal.component.external.model.maven.MutableMavenModuleResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
 import org.gradle.internal.hash.ChecksumService;
@@ -66,6 +68,11 @@ protected MetaDataParser.ParseResult<MutableMavenModuleResolveMetadata> parseMet
                 metaData.setId(snapshotComponentIdentifier);
                 metaData.setSnapshotTimestamp(snapshotComponentIdentifier.getTimestamp());
             } else {
+                if (metaData.isRelocated()) {
+                    moduleComponentIdentifier = DefaultModuleComponentIdentifier
+                        .newId(DefaultModuleIdentifier.newId("relocated." + moduleComponentIdentifier.getGroup(), moduleComponentIdentifier.getModule()),
+                            moduleComponentIdentifier.getVersion());
+                }
                 checkMetadataConsistency(moduleComponentIdentifier, metaData);
             }
             MutableMavenModuleResolveMetadata result = MavenResolver.processMetaData(metaData);

Reply via email to