@@ -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