Skip to content

Commit 30cd109

Browse files
committed
Add global consistent resolution feature
1 parent 385d326 commit 30cd109

File tree

6 files changed

+358
-0
lines changed

6 files changed

+358
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# JVM Dependency Conflict Resolution Gradle plugin - Changelog
22

33
## Version 2.1
4+
* [New] [102](https://github.com/gradlex-org/jvm-dependency-conflict-resolution/issues/102) Global consistent resolution feature
45
* [New Rule] [#125](https://github.com/gradlex-org/jvm-dependency-conflict-resolution/issues/125) mysql:mysql-connector-java / com.mysql:mysql-connector-j (Thanks [Eduardo Acosta Miguens](https://github.com/eduacostam)!)
56
* [New Rule] [#131](https://github.com/gradlex-org/jvm-dependency-conflict-resolution/issues/131) org.json:json / com.vaadin.external.google:android-json (Thanks [Piotr Kubowicz](https://github.com/pkubowicz)!)
67
* [Adjusted Rule] [#130](https://github.com/gradlex-org/jvm-dependency-conflict-resolution/issues/130) javax-transaction-api rule now also covers jboss-transaction-api artifacts (Thanks [Piotr Kubowicz](https://github.com/pkubowicz) for reporting)

src/docs/asciidoc/parts/resolution.adoc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ jvmDependencyConflicts {
5353
// Patch or extend wrong metadata
5454
module("com.googlecode.json-simple:json-simple") { removeDependency("junit:junit") }
5555
}
56+
57+
consistentResolution {
58+
// The runtime classpath of ':app' is always respected in version conflict detection and resolution
59+
providesVersions(":app")
60+
}
5661
}
5762
----
5863

@@ -261,3 +266,39 @@ jvmDependencyConflicts {
261266
| Set the status of pre-release versions that are identified by one of the _marker string_ (e.g. `-rc`, `-m`) to `integration` (will then not be considered when using `latest.release` as version).
262267

263268
|===
269+
270+
271+
[[consistent-resolution-block]]
272+
== Configure gloabl consistent resolution
273+
274+
The `consistentResolution` section of `jvmDependencyConflicts` allows to configure
275+
https://docs.gradle.org/current/userguide/resolution_strategy_tuning.html#resolution_consistency[consistent resolution]
276+
for all modules (subprojects) of your build.
277+
By configuring which projects aggregate the final _software product_ (applications or services that are delivered) you make sure
278+
that the same versions of all third party dependencies you deliver as part of your product are also used when compiling and testing
279+
parts of your softare (single subprojects) in isolation.
280+
281+
[source,groovy]
282+
----
283+
jvmDependencyConflicts {
284+
consistentResolution {
285+
// The runtime classpaths of the configured projects are always respected in
286+
// version conflict detection and resolution
287+
providesVersions(":app")
288+
providesVersions(":service")
289+
// If the build has a platform project, use it to provision additional version information
290+
platform(":versions")
291+
}
292+
}
293+
----
294+
295+
|===
296+
| Method | Documentation
297+
298+
| `providesVersions(dependency)`
299+
| Respect runtime classpaths of given project in all version conflict detection and resolution.
300+
301+
| `platform(dependency)`
302+
| A platform/BOM to provide versions not available through consistent resolution alone.
303+
304+
|===
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright the GradleX team.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.gradlex.jvm.dependency.conflict.resolution;
18+
19+
import org.gradle.api.artifacts.Configuration;
20+
import org.gradle.api.artifacts.ConfigurationContainer;
21+
import org.gradle.api.artifacts.Dependency;
22+
import org.gradle.api.artifacts.dsl.DependencyHandler;
23+
import org.gradle.api.attributes.Bundling;
24+
import org.gradle.api.attributes.Category;
25+
import org.gradle.api.attributes.LibraryElements;
26+
import org.gradle.api.attributes.Usage;
27+
import org.gradle.api.attributes.java.TargetJvmEnvironment;
28+
import org.gradle.api.model.ObjectFactory;
29+
import org.gradle.api.tasks.SourceSetContainer;
30+
import org.gradle.util.GradleVersion;
31+
32+
import javax.inject.Inject;
33+
import java.util.Collections;
34+
35+
import static org.gradlex.jvm.dependency.conflict.resolution.JvmDependencyConflictResolutionPlugin.INTERNAL_CONFIGURATION_NAME;
36+
import static org.gradlex.jvm.dependency.conflict.resolution.JvmDependencyConflictResolutionPlugin.MAIN_RUNTIME_CLASSPATH_CONFIGURATION_NAME;
37+
38+
public abstract class ConsistentResolution {
39+
40+
@Inject
41+
protected abstract ObjectFactory getObjects();
42+
43+
@Inject
44+
protected abstract ConfigurationContainer getConfigurations();
45+
46+
@Inject
47+
protected abstract DependencyHandler getDependencies();
48+
49+
@Inject
50+
protected abstract SourceSetContainer getSourceSets();
51+
52+
/**
53+
* The runtime classpath of the given project always respected in version conflict detection and resolution.
54+
*/
55+
public Configuration providesVersions(String versionProvidingProject) {
56+
Configuration mainRuntimeClasspath = maybeCreateMainRuntimeClasspathConfiguration();
57+
getDependencies().add(mainRuntimeClasspath.getName(), createDependency(versionProvidingProject));
58+
return mainRuntimeClasspath;
59+
}
60+
61+
/**
62+
* A platform/BOM (<a href="https://docs.gradle.org/current/userguide/java_platform_plugin.html">Java Platform Plugin</a>)
63+
* used to provide versions not available through consistent resolution alone.
64+
* Useful if additional dependencies are needed only for tests.
65+
*/
66+
public void platform(String platform) {
67+
Configuration internal = maybeCreateInternalConfiguration();
68+
internal.withDependencies(d -> {
69+
Dependency platformDependency = getDependencies().platform(createDependency(platform));
70+
d.add(platformDependency);
71+
});
72+
73+
getSourceSets().configureEach(sourceSet -> {
74+
ConfigurationContainer configurations = getConfigurations();
75+
configurations.getByName(sourceSet.getRuntimeClasspathConfigurationName()).extendsFrom(internal);
76+
configurations.getByName(sourceSet.getCompileClasspathConfigurationName()).extendsFrom(internal);
77+
configurations.getByName(sourceSet.getAnnotationProcessorConfigurationName()).extendsFrom(internal);
78+
});
79+
}
80+
81+
private Configuration maybeCreateMainRuntimeClasspathConfiguration() {
82+
Configuration existing = getConfigurations().findByName(MAIN_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
83+
if (existing != null) {
84+
return existing;
85+
}
86+
87+
Configuration mainRuntimeClasspath = getConfigurations().create(MAIN_RUNTIME_CLASSPATH_CONFIGURATION_NAME, c -> {
88+
ObjectFactory objects = getObjects();
89+
c.setCanBeResolved(true);
90+
c.setCanBeConsumed(false);
91+
c.extendsFrom(maybeCreateInternalConfiguration());
92+
c.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.class, Usage.JAVA_RUNTIME));
93+
c.getAttributes().attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.class, Category.LIBRARY));
94+
c.getAttributes().attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.class, LibraryElements.JAR));
95+
c.getAttributes().attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.class, Bundling.EXTERNAL));
96+
if (GradleVersion.current().compareTo(GradleVersion.version("7.0")) >= 0) {
97+
c.getAttributes().attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
98+
objects.named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM));
99+
}
100+
});
101+
getSourceSets().configureEach(sourceSet -> {
102+
ConfigurationContainer configurations = getConfigurations();
103+
Configuration runtime = configurations.getByName(sourceSet.getRuntimeClasspathConfigurationName());
104+
Configuration compile = configurations.getByName(sourceSet.getCompileClasspathConfigurationName());
105+
Configuration processor = configurations.getByName(sourceSet.getAnnotationProcessorConfigurationName());
106+
runtime.shouldResolveConsistentlyWith(mainRuntimeClasspath);
107+
compile.shouldResolveConsistentlyWith(runtime);
108+
processor.shouldResolveConsistentlyWith(runtime);
109+
});
110+
return mainRuntimeClasspath;
111+
}
112+
113+
private Configuration maybeCreateInternalConfiguration() {
114+
Configuration internal = getConfigurations().findByName(INTERNAL_CONFIGURATION_NAME);
115+
if (internal != null) {
116+
return internal;
117+
}
118+
return getConfigurations().create(INTERNAL_CONFIGURATION_NAME, c -> {
119+
c.setCanBeResolved(false);
120+
c.setCanBeConsumed(false);
121+
});
122+
}
123+
124+
private Dependency createDependency(String project) {
125+
boolean isProjectInBuild = project.startsWith(":");
126+
return getDependencies().create(isProjectInBuild
127+
? getDependencies().project(Collections.singletonMap("path", project))
128+
: project);
129+
}
130+
}

src/main/java/org/gradlex/jvm/dependency/conflict/resolution/JvmDependencyConflictResolutionPlugin.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import static org.gradlex.jvm.dependency.conflict.resolution.DefaultResolutionStrategy.HIGHEST_VERSION;
3333

3434
public abstract class JvmDependencyConflictResolutionPlugin implements Plugin<Project> {
35+
public static final String MAIN_RUNTIME_CLASSPATH_CONFIGURATION_NAME = "mainRuntimeClasspath";
36+
public static final String INTERNAL_CONFIGURATION_NAME = "internal";
3537

3638
@Override
3739
public void apply(Project project) {

src/main/java/org/gradlex/jvm/dependency/conflict/resolution/JvmDependencyConflictsExtension.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public abstract class JvmDependencyConflictsExtension {
2626
private final ConflictResolution conflictResolution;
2727
private final Logging logging;
2828
private final Patch patch;
29+
private final ConsistentResolution consistentResolution;
2930

3031
@Inject
3132
protected abstract ObjectFactory getObjects();
@@ -34,6 +35,7 @@ public JvmDependencyConflictsExtension() {
3435
conflictResolution = getObjects().newInstance(ConflictResolution.class);
3536
logging = getObjects().newInstance(Logging.class);
3637
patch = getObjects().newInstance(Patch.class);
38+
consistentResolution = getObjects().newInstance(ConsistentResolution.class);
3739
}
3840

3941
public ConflictResolution getConflictResolution() {
@@ -59,4 +61,12 @@ public Patch getPatch() {
5961
public void patch(Action<Patch> action) {
6062
action.execute(patch);
6163
}
64+
65+
public ConsistentResolution getConsistentResolution() {
66+
return consistentResolution;
67+
}
68+
69+
public void consistentResolution(Action<ConsistentResolution> action) {
70+
action.execute(consistentResolution);
71+
}
6272
}

0 commit comments

Comments
 (0)