Skip to content

Commit 7c7e3c5

Browse files
authored
Minimize the eager resolution of transitive dependencies in Gradle recipes (#7423)
1 parent 2089dce commit 7c7e3c5

13 files changed

Lines changed: 237 additions & 27 deletions

rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependency.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) {
207207
if (maybeJp.isPresent() && !acc.modulesWithOldDependency.containsKey(maybeJp.get())) {
208208
outer:
209209
for (GradleDependencyConfiguration config : gradleProject.getConfigurations()) {
210-
for (ResolvedDependency resolved : config.getDirectResolved()) {
210+
for (ResolvedDependency resolved : config.getDirectResolvedShallow()) {
211211
if (StringUtils.matchesGlob(resolved.getGroupId(), oldGroupId) &&
212212
StringUtils.matchesGlob(resolved.getArtifactId(), oldArtifactId)) {
213213
acc.modulesWithOldDependency.put(maybeJp.get(), resolved);
@@ -459,7 +459,7 @@ private GradleProject updateGradleModel(GradleProject gp, ExecutionContext ctx)
459459
}
460460
return requested;
461461
}));
462-
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolved(), resolved -> {
462+
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolvedShallow(), resolved -> {
463463
assert resolved != null;
464464
if (depMatcher.matches(resolved.getGroupId(), resolved.getArtifactId())) {
465465
resolved = updatedResolved.computeIfAbsent(resolved, r -> {

rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ private GradleProject updateGradleModel(GradleProject gp) {
129129
}
130130
return requested;
131131
}));
132-
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolved(), resolved -> {
132+
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolvedShallow(), resolved -> {
133133
if (depMatcher.matches(resolved.getGroupId(), resolved.getArtifactId())) {
134134
return resolved.withGav(resolved.getGav().withArtifactId(newArtifactId));
135135
}

rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyClassifier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ private GradleProject updateGradleModel(GradleProject gp) {
295295
}
296296
return requested;
297297
}));
298-
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolved(), resolved -> {
298+
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolvedShallow(), resolved -> {
299299
if (depMatcher.matches(resolved.getGroupId(), resolved.getArtifactId()) && !Objects.equals(resolved.getClassifier(), newClassifier)) {
300300
return resolved.withClassifier(newClassifier);
301301
}

rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyGroupId.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ private GradleProject updateGradleModel(GradleProject gp) {
129129
}
130130
return requested;
131131
}));
132-
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolved(), resolved -> {
132+
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolvedShallow(), resolved -> {
133133
if (depMatcher.matches(resolved.getGroupId(), resolved.getArtifactId())) {
134134
return resolved.withGav(resolved.getGav().withGroupId(newGroupId));
135135
}

rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeManagedDependency.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ private GradleProject updateGradleModel(GradleProject gp) {
189189
}
190190
return requested;
191191
}));
192-
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolved(), resolved -> {
192+
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolvedShallow(), resolved -> {
193193
assert resolved != null;
194194
if (depMatcher.matches(resolved.getGroupId(), resolved.getArtifactId())) {
195195
resolved = updatedResolved.computeIfAbsent(resolved, r -> {

rewrite-gradle/src/main/java/org/openrewrite/gradle/RemoveDependency.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ private GradleProject updateGradleModel(GradleProject gp) {
143143
}
144144
return requested;
145145
}));
146-
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolved(), resolved -> {
146+
newGdc = newGdc.withDirectResolved(ListUtils.map(gdc.getDirectResolvedShallow(), resolved -> {
147147
if (depMatcher.matches(resolved.getGroupId(), resolved.getArtifactId())) {
148148
return null;
149149
}

rewrite-gradle/src/main/java/org/openrewrite/gradle/RemoveRedundantDependencyVersions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu
438438
private @Nullable String getSpringBootVersionFromPlugin() {
439439
GradleDependencyConfiguration gdc = gp.getBuildscript().getConfiguration("classpath");
440440
if (gdc != null) {
441-
for (ResolvedDependency dependency : gdc.getDirectResolved()) {
441+
for (ResolvedDependency dependency : gdc.getDirectResolvedShallow()) {
442442
if ("org.springframework.boot.gradle.plugin".equals(dependency.getArtifactId())) {
443443
return dependency.getVersion();
444444
}

rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) {
168168
DependencyMatcher depMatcher = new DependencyMatcher(groupId, artifactId, null);
169169
Set<ResolvedDependency> matched = new HashSet<>();
170170
for (GradleDependencyConfiguration config : gradleProject.getConfigurations()) {
171-
for (ResolvedDependency resolved : config.getDirectResolved()) {
171+
for (ResolvedDependency resolved : config.getDirectResolvedShallow()) {
172172
if (depMatcher.matches(resolved.getGroupId(), resolved.getArtifactId())) {
173173
matched.add(resolved);
174174
}
@@ -524,11 +524,7 @@ private JavaSourceFile applyPluginProvidedDependencies(JavaSourceFile sourceFile
524524
continue;
525525
}
526526

527-
for (ResolvedDependency resolved : configuration.getResolved()) {
528-
if (!resolved.isDirect()) {
529-
continue;
530-
}
531-
527+
for (ResolvedDependency resolved : configuration.getDirectResolvedShallow()) {
532528
if (!dependencyMatcher.matches(resolved.getGroupId(), resolved.getArtifactId())) {
533529
continue;
534530
}

rewrite-gradle/src/main/java/org/openrewrite/gradle/internal/AddDependencyVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ public static JavaSourceFile addDependency(
275275
newRequested));
276276
if (newGdc.isCanBeResolved() && resolvedGav != null) {
277277
newGdc = newGdc.withDirectResolved(ListUtils.concat(
278-
ListUtils.map(gdc.getDirectResolved(), resolved -> {
278+
ListUtils.map(gdc.getDirectResolvedShallow(), resolved -> {
279279
// Remove any existing dependency with the same group and artifact id
280280
if (Objects.equals(resolved.getGroupId(), resolvedGav.getGroupId()) && Objects.equals(resolved.getArtifactId(), resolvedGav.getArtifactId())) {
281281
return null;

rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleDependencyConfiguration.java

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ public class GradleDependencyConfiguration implements Serializable, Attributed {
9696
List<ResolvedDependency> directResolved;
9797

9898
/**
99-
* The list of direct dependencies resolved for this configuration.
99+
* The list of direct dependencies resolved for this configuration. Each returned dependency has its full
100+
* transitive tree populated under {@link ResolvedDependency#getDependencies()}.
100101
*/
101102
public List<ResolvedDependency> getDirectResolved() {
102103
if (resolutionContext.isResolveRequired()) {
@@ -106,6 +107,18 @@ public List<ResolvedDependency> getDirectResolved() {
106107
return directResolved == null ? emptyList() : directResolved;
107108
}
108109

110+
/**
111+
* Like {@link #getDirectResolved()}, but avoids downloading the transitive closure of each direct dependency.
112+
* Each returned dependency has an empty {@link ResolvedDependency#getDependencies()} list unless a full
113+
* resolution has already been performed previously. Use this when you only need the declared coordinates of
114+
* direct dependencies and do not intend to walk into their transitives.
115+
*/
116+
public List<ResolvedDependency> getDirectResolvedShallow() {
117+
resolutionContext.resolveDirect();
118+
//noinspection ConstantValue
119+
return directResolved == null ? emptyList() : directResolved;
120+
}
121+
109122
/**
110123
* The list of all dependencies resolved for this configuration, including transitive dependencies.
111124
*/
@@ -180,19 +193,29 @@ public GradleDependencyConfiguration markForReResolution(List<MavenRepository> r
180193
return this;
181194
}
182195
private class LazyResolutionContext {
183-
private @Getter boolean resolveRequired;
196+
private boolean resolveRequired;
197+
private boolean transitivesResolved;
184198
private @Nullable List<MavenRepository> repositories;
185199
private @Nullable ExecutionContext ctx;
186200
private @Nullable List<ResolvedDependency> resolved;
187201

202+
public boolean isResolveRequired() {
203+
// Resolution (or upgrade-to-deep) is still possible as long as we retain repositories + ctx.
204+
return (resolveRequired || !transitivesResolved) && repositories != null && ctx != null;
205+
}
206+
188207
public void markForReResolution(List<MavenRepository> repositories, ExecutionContext ctx) {
189208
this.repositories = repositories;
190209
this.resolveRequired = true;
210+
this.transitivesResolved = false;
191211
this.ctx = ctx;
192212
}
193213

194214
/**
195-
* Attempt to download the maven poms of the direct dependencies to produce an updated set of resolved dependencies.
215+
* Ensure {@code directResolved} is populated with the complete transitive dependency tree under each
216+
* direct dependency. If only a shallow resolution has been done previously within this run, this will
217+
* upgrade the state to a full resolution. After deep resolution succeeds, the captured repositories and
218+
* execution context are released.
196219
* It is expected that some dependencies may both be valid and beyond our ability to resolve.
197220
* Anything coming from an ivy repository, flat directory, gcp artifact service, etc., OpenRewrite does not support.
198221
* This method has not been tested in a Gradle composite build.
@@ -202,7 +225,34 @@ public void markForReResolution(List<MavenRepository> repositories, ExecutionCon
202225
* It is the responsibility of recipes to report this to the user, typically via GradleProject.maybeWarn()
203226
*/
204227
public void resolve() {
205-
if (!resolveRequired || repositories == null || ctx == null) {
228+
if (transitivesResolved || repositories == null || ctx == null) {
229+
return;
230+
}
231+
doResolve(true);
232+
transitivesResolved = true;
233+
repositories = null;
234+
ctx = null;
235+
}
236+
237+
/**
238+
* Populate {@code directResolved} with direct dependencies only, skipping the download of their transitive
239+
* closure. If a full resolution has already happened this is a no-op. Repositories + ctx are retained so
240+
* that a later call to {@link #resolve()} can upgrade the state to a full resolution if a caller that walks
241+
* transitive dependencies needs it.
242+
*/
243+
public void resolveDirect() {
244+
if (!resolveRequired) {
245+
return;
246+
}
247+
if (repositories == null || ctx == null) {
248+
return;
249+
}
250+
doResolve(false);
251+
resolveRequired = false;
252+
}
253+
254+
private void doResolve(boolean resolveTransitives) {
255+
if (repositories == null || ctx == null) {
206256
return;
207257
}
208258
if (isCanBeResolved) {
@@ -222,8 +272,10 @@ public void resolve() {
222272
} else {
223273
Pom singlePom = singleDependencyPom(dep, requested, repositories, ctx);
224274
ResolvedPom singleDependencyResolved = singlePom.resolve(emptyList(), mpd, ctx);
225-
ResolvedDependency resolved = singleDependencyResolved.resolveDependencies(Scope.Compile, mpd, ctx).get(0);
226-
newResolved.add(resolved);
275+
List<ResolvedDependency> resolvedList = resolveTransitives
276+
? singleDependencyResolved.resolveDependencies(Scope.Compile, mpd, ctx)
277+
: singleDependencyResolved.resolveDirectDependencies(Scope.Compile, mpd, ctx);
278+
newResolved.add(resolvedList.get(0));
227279
}
228280
} catch (MavenDownloadingException | MavenDownloadingExceptions e) {
229281
MavenDownloadingException m;
@@ -253,12 +305,11 @@ public void resolve() {
253305
resolved = null;
254306
}
255307
resolveRequired = false;
256-
repositories = null;
257-
ctx = null;
258308
}
259309

260310
public List<ResolvedDependency> getResolved() {
261311
if (resolved == null) {
312+
resolve();
262313
List<ResolvedDependency> newResolved = new ArrayList<>(getDirectResolved());
263314
Map<GroupArtifact, ResolvedDependency> alreadyResolved = new HashMap<>();
264315
Map<String, Version> versionCache = new HashMap<>();

0 commit comments

Comments
 (0)