Skip to content

Commit 207f41c

Browse files
committed
Manually enforce platform constraints for conditional dependencies in QuarkusComponentVariants
Addresses #49743
1 parent c3944a9 commit 207f41c

File tree

31 files changed

+800
-42
lines changed

31 files changed

+800
-42
lines changed

devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/dependency/ApplicationDeploymentClasspathBuilder.java

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.gradle.api.artifacts.Configuration;
1717
import org.gradle.api.artifacts.ConfigurationContainer;
1818
import org.gradle.api.artifacts.Dependency;
19+
import org.gradle.api.artifacts.ExcludeRule;
1920
import org.gradle.api.artifacts.ModuleDependency;
2021
import org.gradle.api.artifacts.ModuleIdentifier;
2122
import org.gradle.api.artifacts.ModuleVersionIdentifier;
@@ -29,6 +30,7 @@
2930
import org.gradle.api.internal.tasks.TaskDependencyFactory;
3031
import org.gradle.api.plugins.JavaPlugin;
3132
import org.gradle.api.provider.ListProperty;
33+
import org.gradle.api.provider.Property;
3234

3335
import io.quarkus.bootstrap.BootstrapConstants;
3436
import io.quarkus.bootstrap.model.PlatformImports;
@@ -38,6 +40,7 @@
3840
import io.quarkus.gradle.tooling.dependency.DependencyUtils;
3941
import io.quarkus.gradle.tooling.dependency.ExtensionDependency;
4042
import io.quarkus.maven.dependency.ArtifactCoords;
43+
import io.quarkus.maven.dependency.ArtifactKey;
4144
import io.quarkus.runtime.LaunchMode;
4245

4346
public class ApplicationDeploymentClasspathBuilder {
@@ -93,13 +96,14 @@ public static void initConfigurations(Project project) {
9396
final ConfigurationContainer configContainer = project.getConfigurations();
9497

9598
// Custom configuration for dev mode
96-
configContainer.register(ToolingUtils.DEV_MODE_CONFIGURATION_NAME, config -> {
97-
config.extendsFrom(configContainer.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME));
98-
config.setCanBeConsumed(false);
99-
if (!isDisableComponentVariants(project)) {
100-
QuarkusComponentVariants.setConditionalAttributes(config, project, LaunchMode.DEVELOPMENT);
101-
}
102-
});
99+
configContainer
100+
.register(ToolingUtils.DEV_MODE_CONFIGURATION_NAME, config -> {
101+
config.extendsFrom(configContainer.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME));
102+
config.setCanBeConsumed(false);
103+
if (!isDisableComponentVariants(project)) {
104+
QuarkusComponentVariants.setConditionalAttributes(config, project, LaunchMode.DEVELOPMENT);
105+
}
106+
});
103107

104108
// Base runtime configurations for every launch mode
105109
configContainer
@@ -178,6 +182,7 @@ private static Configuration[] getOriginalRuntimeClasspaths(Project project, Lau
178182
private final String platformImportName;
179183

180184
private final List<Dependency> platformDataDeps = new ArrayList<>();
185+
private final Map<ArtifactKey, PlatformSpec.Constraint> platformConstraints = new HashMap<>();
181186

182187
public ApplicationDeploymentClasspathBuilder(Project project, LaunchMode mode,
183188
TaskDependencyFactory taskDependencyFactory) {
@@ -239,6 +244,18 @@ private void setUpPlatformConfiguration() {
239244
break;
240245
}
241246
}
247+
} else {
248+
ArtifactKey artifactKey = ArtifactKey.ga(d.getTarget().getGroup(), name);
249+
final Set<ExcludeRule> depExcludeRules = (d instanceof ModuleDependency md
250+
&& !md.getExcludeRules().isEmpty())
251+
? new HashSet<>(md.getExcludeRules())
252+
: Set.of();
253+
platformConstraints.computeIfAbsent(artifactKey,
254+
k -> new PlatformSpec.Constraint(
255+
d.getTarget().getGroup(),
256+
name,
257+
d.getTarget().getVersion(),
258+
depExcludeRules));
242259
}
243260
});
244261
});
@@ -276,14 +293,22 @@ private List<Dependency> resolvePlatformDependencies() {
276293
return platformDataDeps;
277294
}
278295

296+
private PlatformSpec resolvePlatformSpec() {
297+
getPlatformConfiguration().resolve();
298+
return new PlatformSpec(platformConstraints, getPlatformConfiguration().getExcludeRules());
299+
}
300+
279301
private void setUpRuntimeConfiguration() {
280302
if (!project.getConfigurations().getNames().contains(this.runtimeConfigurationName)) {
281303
final String baseConfig;
282304
final boolean disableComponentVariants = isDisableComponentVariants(project);
283305
if (disableComponentVariants) {
284306
baseConfig = ApplicationDeploymentClasspathBuilder.getBaseRuntimeConfigName(mode);
285307
} else {
286-
QuarkusComponentVariants.addVariants(project, mode);
308+
Property<PlatformSpec> platformSpecProperty = project.getObjects()
309+
.property(PlatformSpec.class);
310+
QuarkusComponentVariants.addVariants(project, mode,
311+
platformSpecProperty.value(project.provider(this::resolvePlatformSpec)));
287312
baseConfig = QuarkusComponentVariants.getConditionalConfigurationName(mode);
288313
}
289314
project.getConfigurations().register(this.runtimeConfigurationName, configuration -> {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package io.quarkus.gradle.dependency;
2+
3+
import java.util.Map;
4+
import java.util.Set;
5+
6+
import org.gradle.api.artifacts.ExcludeRule;
7+
8+
import io.quarkus.maven.dependency.ArtifactKey;
9+
10+
class PlatformSpec {
11+
private final Map<ArtifactKey, Constraint> constraints;
12+
private final Set<ExcludeRule> exclusions;
13+
14+
public PlatformSpec(Map<ArtifactKey, Constraint> constraints, Set<ExcludeRule> exclusions) {
15+
this.constraints = constraints;
16+
this.exclusions = exclusions;
17+
}
18+
19+
public Map<ArtifactKey, Constraint> getConstraints() {
20+
return constraints;
21+
}
22+
23+
public Set<ExcludeRule> getExclusions() {
24+
return exclusions;
25+
}
26+
27+
static class Constraint {
28+
private final String groupId;
29+
private final String artifactId;
30+
private final String version;
31+
private final Set<ExcludeRule> exclusions;
32+
33+
public Constraint(String groupId, String artifactId, String version, Set<ExcludeRule> exclusions) {
34+
this.groupId = groupId;
35+
this.artifactId = artifactId;
36+
this.version = version;
37+
this.exclusions = exclusions;
38+
}
39+
40+
public String getGroupId() {
41+
return groupId;
42+
}
43+
44+
public String getArtifactId() {
45+
return artifactId;
46+
}
47+
48+
public String getVersion() {
49+
return version;
50+
}
51+
52+
public Set<ExcludeRule> getExclusions() {
53+
return exclusions;
54+
}
55+
}
56+
}

devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/dependency/QuarkusComponentVariants.java

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.HashSet;
99
import java.util.List;
1010
import java.util.Map;
11+
import java.util.Optional;
1112
import java.util.Set;
1213
import java.util.concurrent.atomic.AtomicInteger;
1314

@@ -28,6 +29,7 @@
2829
import org.gradle.api.attributes.java.TargetJvmEnvironment;
2930
import org.gradle.api.model.ObjectFactory;
3031
import org.gradle.api.provider.ListProperty;
32+
import org.gradle.api.provider.Property;
3133

3234
import io.quarkus.gradle.tooling.dependency.DependencyUtils;
3335
import io.quarkus.gradle.tooling.dependency.ExtensionDependency;
@@ -169,22 +171,26 @@ public static void setCommonAttributes(AttributeContainer attrs, ObjectFactory o
169171
* @param project project
170172
* @param mode launch mode
171173
*/
172-
public static void addVariants(Project project, LaunchMode mode) {
173-
new QuarkusComponentVariants(project, mode).configureAndAddVariants();
174+
public static void addVariants(Project project, LaunchMode mode,
175+
Property<PlatformSpec> platformSpecProperty) {
176+
new QuarkusComponentVariants(project, mode, platformSpecProperty).configureAndAddVariants();
174177
}
175178

176179
private final Attribute<String> quarkusDepAttr;
177180
private final Project project;
181+
private final Property<PlatformSpec> platformSpecProperty;
178182
private final Map<ArtifactKey, ProcessedDependency> processedDeps = new HashMap<>();
179183
private final Map<ArtifactKey, ConditionalDependency> allConditionalDeps = new HashMap<>();
180184
private final List<ConditionalDependencyVariant> dependencyVariantQueue = new ArrayList<>();
181185
private final Map<String, SatisfiedExtensionDeps> satisfiedExtensionDeps = new HashMap<>();
182186
private final LaunchMode mode;
183187
private final AtomicInteger configCopyCounter = new AtomicInteger();
184188

185-
private QuarkusComponentVariants(Project project, LaunchMode mode) {
189+
private QuarkusComponentVariants(Project project, LaunchMode mode,
190+
Property<PlatformSpec> platformSpecProperty) {
186191
this.project = project;
187192
this.mode = mode;
193+
this.platformSpecProperty = platformSpecProperty;
188194
this.quarkusDepAttr = getConditionalDependencyAttribute(project.getName(), mode);
189195
project.getDependencies().getAttributesSchema().attribute(quarkusDepAttr);
190196
project.getDependencies().getAttributesSchema().attribute(getDeploymentDependencyAttribute(project.getName(), mode));
@@ -407,7 +413,10 @@ private void processDependency(ProcessedDependency parent,
407413
}
408414

409415
private void queueConditionalDependency(ProcessedDependency parent, Dependency dep) {
410-
dependencyVariantQueue.add(new ConditionalDependencyVariant(parent.extension, getOrCreateConditionalDep(dep)));
416+
var conditionalDep = getOrCreateConditionalDep(dep);
417+
if (conditionalDep != null) {
418+
dependencyVariantQueue.add(new ConditionalDependencyVariant(parent.extension, conditionalDep));
419+
}
411420
}
412421

413422
private ConditionalDependency getOrCreateConditionalDep(Dependency dep) {
@@ -417,7 +426,7 @@ private ConditionalDependency getOrCreateConditionalDep(Dependency dep) {
417426
}
418427

419428
private ResolvedArtifact tryResolvingRelocationArtifact(Dependency dep) {
420-
final Configuration configForRelocated = project.getConfigurations().detachedConfiguration(dep).setTransitive(true);
429+
final Configuration configForRelocated = getDetachedWithExclusions(dep).setTransitive(true);
421430
setConditionalAttributes(configForRelocated, project, mode);
422431

423432
var firstLevelDeps = configForRelocated.getResolvedConfiguration().getFirstLevelModuleDependencies();
@@ -447,8 +456,10 @@ private ResolvedArtifact tryResolvingRelocationArtifact(Dependency dep) {
447456
return artifact;
448457
}
449458

450-
private ConditionalDependency newConditionalDep(Dependency dep) {
451-
final Configuration config = project.getConfigurations().detachedConfiguration(dep).setTransitive(false);
459+
private ConditionalDependency newConditionalDep(Dependency originalDep) {
460+
var dep = getConstrainedDep(originalDep);
461+
final Configuration config = getDetachedWithExclusions(dep).setTransitive(false);
462+
452463
setConditionalAttributes(config, project, mode);
453464
ResolvedArtifact resolvedArtifact = null;
454465

@@ -464,6 +475,11 @@ private ConditionalDependency newConditionalDep(Dependency dep) {
464475
}
465476

466477
if (resolvedArtifact == null) {
478+
// check if that's due to exclude rules, and if yes, ignore
479+
if (isExplicitlyExcluded(dep)) {
480+
project.getLogger().info("Conditional dependency {} ignored due to exclusion rule", dep);
481+
return null;
482+
}
467483
throw new RuntimeException(dep + " did not resolve to any artifacts");
468484
}
469485

@@ -474,6 +490,49 @@ private ConditionalDependency newConditionalDep(Dependency dep) {
474490

475491
}
476492

493+
private boolean isExplicitlyExcluded(Dependency dep) {
494+
return platformSpecProperty.get().getExclusions().stream().anyMatch(rule -> {
495+
rule.getGroup();
496+
if (!rule.getGroup().equals(dep.getGroup())) {
497+
return false;
498+
}
499+
rule.getModule();
500+
return rule.getModule().equals(dep.getName());
501+
});
502+
}
503+
504+
private Configuration getDetachedWithExclusions(Dependency dep) {
505+
var c = project.getConfigurations().detachedConfiguration(dep);
506+
PlatformSpec platformSpec = platformSpecProperty.get();
507+
platformSpec.getExclusions().forEach(rule -> {
508+
Map<String, String> excludeProperties = new HashMap<>();
509+
excludeProperties.put("group", rule.getGroup());
510+
excludeProperties.put("module", rule.getModule());
511+
c.exclude(excludeProperties);
512+
});
513+
// Also apply exclusions from matching constraints
514+
findMatchingConstraint(dep).ifPresent(constraint -> constraint.getExclusions().forEach(rule -> {
515+
Map<String, String> excludeProperties = new HashMap<>();
516+
excludeProperties.put("group", rule.getGroup());
517+
excludeProperties.put("module", rule.getModule());
518+
c.exclude(excludeProperties);
519+
}));
520+
return c;
521+
}
522+
523+
private Dependency getConstrainedDep(Dependency dep) {
524+
return findMatchingConstraint(dep).map(c -> project.getDependencies().create(
525+
dep.getGroup() + ":" + dep.getName() + ":" + c.getVersion())).orElse(dep);
526+
}
527+
528+
private Optional<PlatformSpec.Constraint> findMatchingConstraint(Dependency dep) {
529+
PlatformSpec platformSpec = platformSpecProperty.get();
530+
Map<ArtifactKey, PlatformSpec.Constraint> constraints = platformSpec.getConstraints();
531+
PlatformSpec.Constraint matchingConstraint = constraints
532+
.get(ArtifactKey.ga(dep.getGroup(), dep.getName()));
533+
return Optional.ofNullable(matchingConstraint);
534+
}
535+
477536
private class ProcessedDependency {
478537
private final ResolvedArtifact artifact;
479538
private final ExtensionDependency<?> extension;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
quarkusPlatformArtifactId=quarkus-bom
2+
quarkusPlatformGroupId=io.quarkus
3+
org.gradle.logging.level=INFO
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
plugins {
2+
id 'java'
3+
id 'io.quarkus'
4+
}
5+
6+
repositories {
7+
mavenLocal {
8+
content {
9+
includeGroup 'org.enforcing.conditional' // for dependencies built in this test
10+
includeGroupByRegex 'io.quarkus.*'
11+
includeGroup 'org.hibernate.orm'
12+
}
13+
}
14+
mavenCentral()
15+
}
16+
17+
dependencies {
18+
implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
19+
implementation enforcedPlatform("org.enforcing.conditional:test-bom:1.0.0")
20+
implementation 'io.quarkus:quarkus-rest'
21+
implementation 'org.enforcing.conditional:ext-a:1.0-SNAPSHOT'
22+
testImplementation 'io.quarkus:quarkus-junit5'
23+
testImplementation 'io.rest-assured:rest-assured'
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.acme;
2+
3+
import org.eclipse.microprofile.config.inject.ConfigProperty;
4+
5+
import jakarta.inject.Inject;
6+
import jakarta.ws.rs.GET;
7+
import jakarta.ws.rs.Path;
8+
import jakarta.ws.rs.Produces;
9+
import jakarta.ws.rs.core.MediaType;
10+
import java.io.IOException;
11+
import java.net.URL;
12+
import java.util.Enumeration;
13+
14+
@Path("/hello")
15+
public class HelloResource {
16+
17+
@GET
18+
@Produces(MediaType.TEXT_PLAIN)
19+
public String hello() {
20+
return "hello";
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
plugins {
2+
id 'java'
3+
id 'io.quarkus'
4+
}
5+
6+
repositories {
7+
mavenLocal {
8+
content {
9+
includeGroup 'org.enforcing.conditional' // for dependencies built in this test
10+
includeGroupByRegex 'io.quarkus.*'
11+
includeGroup 'org.hibernate.orm'
12+
}
13+
}
14+
mavenCentral()
15+
}
16+
17+
dependencies {
18+
implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
19+
implementation enforcedPlatform("org.enforcing.conditional:test-bom:1.0.0")
20+
implementation 'io.quarkus:quarkus-rest'
21+
implementation 'org.enforcing.conditional:ext-a:1.0-SNAPSHOT'
22+
testImplementation 'io.quarkus:quarkus-junit5'
23+
testImplementation 'io.rest-assured:rest-assured'
24+
}
25+
26+
configurations {
27+
configureEach {
28+
exclude group: 'org.enforcing.conditional', module: 'dev-mode-only-lib'
29+
}
30+
}

0 commit comments

Comments
 (0)