|
21 | 21 | import netflix.nebula.dependency.recommender.provider.RecommendationProviderContainer; |
22 | 22 | import netflix.nebula.dependency.recommender.provider.RecommendationResolver; |
23 | 23 | import netflix.nebula.dependency.recommender.publisher.MavenBomXmlGenerator; |
| 24 | +import netflix.nebula.dependency.recommender.service.BomResolverService; |
| 25 | +import netflix.nebula.dependency.recommender.util.BomResolutionUtil; |
24 | 26 | import org.apache.commons.lang3.StringUtils; |
25 | 27 | import org.codehaus.groovy.runtime.MethodClosure; |
26 | 28 | import org.gradle.api.Action; |
|
38 | 40 | import org.gradle.api.logging.Logger; |
39 | 41 | import org.gradle.api.logging.Logging; |
40 | 42 | import org.gradle.api.plugins.ExtraPropertiesExtension; |
| 43 | +import org.gradle.api.provider.Provider; |
| 44 | +import org.gradle.util.GradleVersion; |
41 | 45 |
|
42 | 46 | import java.lang.reflect.Method; |
43 | 47 | import java.util.*; |
44 | 48 |
|
45 | 49 | public class DependencyRecommendationsPlugin implements Plugin<Project> { |
46 | 50 | public static final String NEBULA_RECOMMENDER_BOM = "nebulaRecommenderBom"; |
47 | 51 | public static final boolean CORE_BOM_SUPPORT_ENABLED = Boolean.getBoolean("nebula.features.coreBomSupport"); |
| 52 | + private static final GradleVersion GRADLE_9_0 = GradleVersion.version("9.0"); |
48 | 53 | private Logger logger = Logging.getLogger(DependencyRecommendationsPlugin.class); |
49 | 54 | private RecommendationProviderContainer recommendationProviderContainer; |
50 | 55 | //TODO: remove this exclusion once https://github.com/gradle/gradle/issues/6750 is resolved |
@@ -74,70 +79,36 @@ private void applyRecommendationsDirectly(final Project project, final Configura |
74 | 79 | project.afterEvaluate(new Action<Project>() { |
75 | 80 | @Override |
76 | 81 | public void execute(Project p) { |
77 | | - // Eagerly resolve and cache all BOMs after project evaluation |
78 | | - eagerlyResolveBoms(p, recommendationProviderContainer); |
| 82 | + // Eagerly resolve and cache all BOMs if using build service approach |
| 83 | + if (shouldUseBuildService(p) && BomResolutionUtil.shouldEagerlyResolveBoms(p, recommendationProviderContainer)) { |
| 84 | + BomResolutionUtil.eagerlyResolveBoms(p, recommendationProviderContainer, NEBULA_RECOMMENDER_BOM); |
| 85 | + } |
79 | 86 |
|
80 | 87 | p.getConfigurations().all(new ExtendRecommenderConfigurationAction(bomConfiguration, p, recommendationProviderContainer)); |
81 | 88 | p.subprojects(new Action<Project>() { |
82 | 89 | @Override |
83 | 90 | public void execute(Project sub) { |
84 | | - // Also eagerly resolve BOMs for subprojects |
85 | | - eagerlyResolveBoms(sub, recommendationProviderContainer); |
| 91 | + // Also eagerly resolve BOMs for subprojects if using build service |
| 92 | + if (shouldUseBuildService(sub) && BomResolutionUtil.shouldEagerlyResolveBoms(sub, recommendationProviderContainer)) { |
| 93 | + BomResolutionUtil.eagerlyResolveBoms(sub, recommendationProviderContainer, NEBULA_RECOMMENDER_BOM); |
| 94 | + } |
86 | 95 | sub.getConfigurations().all(new ExtendRecommenderConfigurationAction(bomConfiguration, sub, recommendationProviderContainer)); |
87 | 96 | } |
88 | 97 | }); |
89 | 98 | } |
90 | 99 | }); |
91 | 100 | } |
92 | 101 |
|
93 | | - /** |
94 | | - * Eagerly resolves BOM configurations during the configuration phase to prevent |
95 | | - * configuration resolution lock conflicts in parallel builds. |
96 | | - * |
97 | | - * <p>This method is called during {@code afterEvaluate} when exclusive locks are |
98 | | - * available. It instructs the {@link BomResolverService} to resolve all BOM |
99 | | - * configurations and cache the results for later use during dependency resolution.</p> |
100 | | - * |
101 | | - * <p>The eager resolution prevents the need to resolve configurations during the |
102 | | - * dependency resolution phase, which would cause {@code IllegalResolutionException} |
103 | | - * in parallel builds with Gradle 9+.</p> |
104 | | - * |
105 | | - * @param project the Gradle project whose BOM configurations should be resolved |
106 | | - * @param container the recommendation provider container to check for additional BOM providers |
107 | | - */ |
108 | | - private void eagerlyResolveBoms(Project project, RecommendationProviderContainer container) { |
109 | | - try { |
110 | | - // Get the build service |
111 | | - org.gradle.api.provider.Provider<netflix.nebula.dependency.recommender.service.BomResolverService> bomResolverService = |
112 | | - project.getGradle().getSharedServices().registerIfAbsent( |
113 | | - "bomResolver", netflix.nebula.dependency.recommender.service.BomResolverService.class, spec -> {} |
114 | | - ); |
115 | | - |
116 | | - // Resolve BOMs from the nebulaRecommenderBom configuration |
117 | | - bomResolverService.get().eagerlyResolveAndCacheBoms(project, NEBULA_RECOMMENDER_BOM); |
118 | | - |
119 | | - // Also trigger resolution for maven BOM provider if it exists |
120 | | - // This handles mavenBom providers configured in the extension |
121 | | - netflix.nebula.dependency.recommender.provider.MavenBomRecommendationProvider mavenBomProvider = container.getMavenBomProvider(); |
122 | | - if (mavenBomProvider != null) { |
123 | | - try { |
124 | | - mavenBomProvider.getVersion("dummy", "dummy"); // Trigger lazy initialization |
125 | | - } catch (Exception e) { |
126 | | - // Expected - just needed to trigger BOM resolution |
127 | | - } |
128 | | - } |
129 | | - } catch (Exception e) { |
130 | | - logger.warn("Failed to eagerly resolve BOMs for project " + project.getPath(), e); |
131 | | - } |
132 | | - } |
133 | 102 |
|
134 | 103 | private void applyRecommendations(final Project project) { |
135 | 104 | // Add eager BOM resolution for regular (non-core) BOM support |
136 | 105 | project.afterEvaluate(new Action<Project>() { |
137 | 106 | @Override |
138 | 107 | public void execute(Project p) { |
139 | | - // Eagerly resolve and cache all BOMs after project evaluation |
140 | | - eagerlyResolveBoms(p, recommendationProviderContainer); |
| 108 | + if (shouldUseBuildService(p) && BomResolutionUtil.shouldEagerlyResolveBoms(p, recommendationProviderContainer)) { |
| 109 | + // Eagerly resolve and cache all BOMs after project evaluation |
| 110 | + BomResolutionUtil.eagerlyResolveBoms(p, recommendationProviderContainer, NEBULA_RECOMMENDER_BOM); |
| 111 | + } |
141 | 112 | } |
142 | 113 | }); |
143 | 114 |
|
@@ -300,4 +271,60 @@ public Set<String> getReasonsRecursive(Project project) { |
300 | 271 | return getReasonsRecursive(project.getParent()); |
301 | 272 | return Collections.emptySet(); |
302 | 273 | } |
| 274 | + |
| 275 | + /** |
| 276 | + * Determines whether to use the BomResolverService (build service) approach. |
| 277 | + * |
| 278 | + * <p>The build service is used when:</p> |
| 279 | + * <ul> |
| 280 | + * <li>Gradle version is 9.0 or higher, OR</li> |
| 281 | + * <li>The gradle property 'nebula.dependency-recommender.useBuildService' is set to true</li> |
| 282 | + * </ul> |
| 283 | + * |
| 284 | + * @param project the Gradle project to check |
| 285 | + * @return true if build service should be used, false otherwise |
| 286 | + */ |
| 287 | + private boolean shouldUseBuildService(Project project) { |
| 288 | + // Check if explicitly enabled via gradle property |
| 289 | + if (project.hasProperty("nebula.dependency-recommender.useBuildService")) { |
| 290 | + Object property = project.property("nebula.dependency-recommender.useBuildService"); |
| 291 | + if (Boolean.parseBoolean(property.toString())) { |
| 292 | + return true; |
| 293 | + } |
| 294 | + } |
| 295 | + |
| 296 | + // Default behavior: use build service for Gradle 9+ |
| 297 | + GradleVersion currentVersion = GradleVersion.current(); |
| 298 | + return currentVersion.compareTo(GRADLE_9_0) >= 0; |
| 299 | + } |
| 300 | + |
| 301 | + /** |
| 302 | + * Eagerly resolves BOM configurations during the configuration phase to prevent |
| 303 | + * configuration resolution lock conflicts in parallel builds. |
| 304 | + * |
| 305 | + * <p>This method delegates to {@link BomResolutionUtil#eagerlyResolveBoms} and is |
| 306 | + * provided for backward compatibility and convenience for external plugins.</p> |
| 307 | + * |
| 308 | + * <p><strong>External Plugin Usage:</strong></p> |
| 309 | + * <pre>{@code |
| 310 | + * // Get the plugin instance and container |
| 311 | + * DependencyRecommendationsPlugin plugin = project.plugins.getPlugin(DependencyRecommendationsPlugin) |
| 312 | + * RecommendationProviderContainer container = project.extensions.getByType(RecommendationProviderContainer) |
| 313 | + * |
| 314 | + * // Disable automatic resolution and add BOMs |
| 315 | + * container.setEagerlyResolve(false) |
| 316 | + * container.mavenBom(module: 'com.example:custom-bom:1.0.0') |
| 317 | + * |
| 318 | + * // Manually trigger resolution |
| 319 | + * plugin.eagerlyResolveBoms(project, container) |
| 320 | + * }</pre> |
| 321 | + * |
| 322 | + * @param project the Gradle project whose BOM configurations should be resolved |
| 323 | + * @param container the recommendation provider container to check for additional BOM providers |
| 324 | + * @since 12.7.0 |
| 325 | + * @see BomResolutionUtil#eagerlyResolveBoms(Project, RecommendationProviderContainer, String) |
| 326 | + */ |
| 327 | + public void eagerlyResolveBoms(Project project, RecommendationProviderContainer container) { |
| 328 | + BomResolutionUtil.eagerlyResolveBoms(project, container, NEBULA_RECOMMENDER_BOM); |
| 329 | + } |
303 | 330 | } |
0 commit comments