Skip to content

Commit 7cbd16e

Browse files
authored
Prepare quarkus tests for gradle9 (#14516)
1 parent f5da4c0 commit 7cbd16e

File tree

17 files changed

+2645
-16
lines changed

17 files changed

+2645
-16
lines changed

.github/workflows/build-common.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ jobs:
133133
set +e
134134
grep '^ implementation(".*:.*:[0-9].*")\|^ api(".*:.*:[0-9].*")' \
135135
--include=\*.kts \
136+
--exclude-dir=quarkus\*-plugin \
136137
-r instrumentation \
137138
| grep -v testing/build.gradle.kts \
138139
| grep -v com.azure:azure-core-tracing-opentelemetry \

.github/workflows/codeql.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ jobs:
7575
# --no-build-cache is required for codeql to analyze all modules
7676
# --no-daemon is required for codeql to observe the compilation
7777
# (see https://docs.github.com/en/code-security/codeql-cli/getting-started-with-the-codeql-cli/preparing-your-code-for-codeql-analysis#specifying-build-commands)
78-
# quarkus tasks are disabled because they often cause the build to fail (see https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/13284)
79-
run: ./gradlew assemble -x javadoc -x :instrumentation:quarkus-resteasy-reactive:quarkus3-testing:quarkusGenerateCodeDev -x :instrumentation:quarkus-resteasy-reactive:quarkus2-testing:quarkusGenerateCodeDev --no-build-cache --no-daemon
78+
run: ./gradlew assemble -x javadoc --no-build-cache --no-daemon
8079

8180
- name: Perform CodeQL analysis
8281
uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
plugins {
2+
`java-gradle-plugin`
3+
}
4+
5+
repositories {
6+
mavenCentral()
7+
gradlePluginPortal()
8+
}
9+
10+
dependencies {
11+
implementation(gradleApi())
12+
implementation("io.quarkus:quarkus-gradle-model:2.16.7.Final")
13+
}
14+
15+
gradlePlugin {
16+
plugins {
17+
create("quarkus2Plugin") {
18+
id = "io.opentelemetry.instrumentation.quarkus2"
19+
implementationClass = "io.opentelemetry.instrumentation.quarkus2plugin.Quarkus2Plugin"
20+
}
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.quarkus2plugin;
7+
8+
import org.gradle.api.Plugin;
9+
import org.gradle.api.Project;
10+
11+
@SuppressWarnings("unused")
12+
public class Quarkus2Plugin implements Plugin<Project> {
13+
14+
@Override
15+
public void apply(Project project) {
16+
// we use this plugin with apply false and call its classes directly from the build script
17+
throw new IllegalStateException("this plugin is not meant to be applied");
18+
}
19+
}
20+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
// Includes work from:
7+
/*
8+
* Copyright Quarkus Authors
9+
*
10+
* Licensed under the Apache License, Version 2.0 (the "License");
11+
* you may not use this file except in compliance with the License.
12+
* You may obtain a copy of the License at
13+
*
14+
* http://www.apache.org/licenses/LICENSE-2.0
15+
*
16+
* Unless required by applicable law or agreed to in writing, software
17+
* distributed under the License is distributed on an "AS IS" BASIS,
18+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19+
* See the License for the specific language governing permissions and
20+
* limitations under the License.
21+
*/
22+
23+
package io.quarkus.gradle.dependency;
24+
25+
import java.util.ArrayList;
26+
import java.util.Collection;
27+
import java.util.HashMap;
28+
import java.util.HashSet;
29+
import java.util.List;
30+
import java.util.Map;
31+
import java.util.Set;
32+
33+
import org.gradle.api.Project;
34+
import org.gradle.api.artifacts.Configuration;
35+
import org.gradle.api.artifacts.Dependency;
36+
import org.gradle.api.artifacts.ModuleVersionIdentifier;
37+
import org.gradle.api.artifacts.ResolvedArtifact;
38+
39+
import io.quarkus.gradle.tooling.dependency.DependencyUtils;
40+
import io.quarkus.gradle.tooling.dependency.ExtensionDependency;
41+
import io.quarkus.maven.dependency.ArtifactCoords;
42+
import io.quarkus.maven.dependency.ArtifactKey;
43+
import io.quarkus.maven.dependency.GACT;
44+
import io.quarkus.runtime.LaunchMode;
45+
46+
public class ConditionalDependenciesEnabler {
47+
48+
/**
49+
* Links dependencies to extensions
50+
*/
51+
private final Map<GACT, Set<ExtensionDependency>> featureVariants = new HashMap<>();
52+
/**
53+
* Despite its name, only contains extensions which have no conditional dependencies, or have
54+
* resolved their conditional dependencies.
55+
*/
56+
private final Map<ModuleVersionIdentifier, ExtensionDependency> allExtensions = new HashMap<>();
57+
private final Project project;
58+
private final Configuration enforcedPlatforms;
59+
private final Set<ArtifactKey> existingArtifacts = new HashSet<>();
60+
private final List<Dependency> unsatisfiedConditionalDeps = new ArrayList<>();
61+
62+
public ConditionalDependenciesEnabler(Project project, LaunchMode mode,
63+
Configuration platforms) {
64+
this.project = project;
65+
this.enforcedPlatforms = platforms;
66+
67+
// Get runtimeClasspath (quarkusProdBaseRuntimeClasspathConfiguration to be exact)
68+
Configuration baseRuntimeConfig = project.getConfigurations()
69+
.getByName(ApplicationDeploymentClasspathBuilder.getBaseRuntimeConfigName(mode));
70+
71+
if (!baseRuntimeConfig.getIncoming().getDependencies().isEmpty()) {
72+
// Gather all extensions from the full resolved dependency tree
73+
collectConditionalDependencies(baseRuntimeConfig.getResolvedConfiguration().getResolvedArtifacts());
74+
// If there are any extensions which had unresolved conditional dependencies:
75+
while (!unsatisfiedConditionalDeps.isEmpty()) {
76+
boolean satisfiedConditionalDeps = false;
77+
final int originalUnsatisfiedCount = unsatisfiedConditionalDeps.size();
78+
int i = 0;
79+
// Go through each unsatisfied/unresolved dependency once:
80+
while (i < unsatisfiedConditionalDeps.size()) {
81+
final Dependency conditionalDep = unsatisfiedConditionalDeps.get(i);
82+
// Try to resolve it with the latest evolved graph available
83+
if (resolveConditionalDependency(conditionalDep)) {
84+
// Mark the resolution as a success so we know the graph evolved
85+
satisfiedConditionalDeps = true;
86+
unsatisfiedConditionalDeps.remove(i);
87+
} else {
88+
// No resolution (yet) or graph evolution; move on to the next
89+
++i;
90+
}
91+
}
92+
// If we didn't resolve any dependencies and the graph did not evolve, give up.
93+
if (!satisfiedConditionalDeps && unsatisfiedConditionalDeps.size() == originalUnsatisfiedCount) {
94+
break;
95+
}
96+
}
97+
reset();
98+
}
99+
100+
}
101+
102+
public Collection<ExtensionDependency> getAllExtensions() {
103+
return allExtensions.values();
104+
}
105+
106+
private void reset() {
107+
featureVariants.clear();
108+
existingArtifacts.clear();
109+
unsatisfiedConditionalDeps.clear();
110+
}
111+
112+
private void collectConditionalDependencies(Set<ResolvedArtifact> runtimeArtifacts) {
113+
// For every artifact in the dependency graph:
114+
for (ResolvedArtifact artifact : runtimeArtifacts) {
115+
// Add to master list of artifacts:
116+
existingArtifacts.add(getKey(artifact));
117+
ExtensionDependency extension = DependencyUtils.getExtensionInfoOrNull(project, artifact);
118+
// If this artifact represents an extension:
119+
if (extension != null) {
120+
// Add to master list of accepted extensions:
121+
allExtensions.put(extension.getExtensionId(), extension);
122+
for (Dependency conditionalDep : extension.getConditionalDependencies()) {
123+
// If the dependency is not present yet in the graph, queue it for resolution later
124+
if (!exists(conditionalDep)) {
125+
queueConditionalDependency(extension, conditionalDep);
126+
}
127+
}
128+
}
129+
}
130+
}
131+
132+
private boolean resolveConditionalDependency(Dependency conditionalDep) {
133+
134+
final Configuration conditionalDeps = createConditionalDependenciesConfiguration(project, conditionalDep);
135+
Set<ResolvedArtifact> resolvedArtifacts = conditionalDeps.getResolvedConfiguration().getResolvedArtifacts();
136+
137+
boolean satisfied = false;
138+
// Resolved artifacts don't have great linking back to the original artifact, so I think
139+
// this loop is trying to find the artifact that represents the original conditional
140+
// dependency
141+
for (ResolvedArtifact artifact : resolvedArtifacts) {
142+
if (conditionalDep.getName().equals(artifact.getName())
143+
&& conditionalDep.getVersion().equals(artifact.getModuleVersion().getId().getVersion())
144+
&& artifact.getModuleVersion().getId().getGroup().equals(conditionalDep.getGroup())) {
145+
// Once the dependency is found, reload the extension info from within
146+
final ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact);
147+
// Now check if this conditional dependency is resolved given the latest graph evolution
148+
if (extensionDependency != null && (extensionDependency.getDependencyConditions().isEmpty()
149+
|| exist(extensionDependency.getDependencyConditions()))) {
150+
satisfied = true;
151+
enableConditionalDependency(extensionDependency.getExtensionId());
152+
break;
153+
}
154+
}
155+
}
156+
157+
// No resolution (yet); give up.
158+
if (!satisfied) {
159+
return false;
160+
}
161+
162+
// The conditional dependency resolved! Let's now add all of /its/ dependencies
163+
for (ResolvedArtifact artifact : resolvedArtifacts) {
164+
// First add the artifact to the master list
165+
existingArtifacts.add(getKey(artifact));
166+
ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact);
167+
if (extensionDependency == null) {
168+
continue;
169+
}
170+
// If this artifact represents an extension, mark this one as a conditional extension
171+
extensionDependency.setConditional(true);
172+
// Add to the master list of accepted extensions
173+
allExtensions.put(extensionDependency.getExtensionId(), extensionDependency);
174+
for (Dependency cd : extensionDependency.getConditionalDependencies()) {
175+
// Add any unsatisfied/unresolved conditional dependencies of this dependency to the queue
176+
if (!exists(cd)) {
177+
queueConditionalDependency(extensionDependency, cd);
178+
}
179+
}
180+
}
181+
return satisfied;
182+
}
183+
184+
private void queueConditionalDependency(ExtensionDependency extension, Dependency conditionalDep) {
185+
// 1. Add to master list of unresolved/unsatisfied dependencies
186+
// 2. Add map entry to link dependency to extension
187+
featureVariants.computeIfAbsent(getFeatureKey(conditionalDep), k -> {
188+
unsatisfiedConditionalDeps.add(conditionalDep);
189+
return new HashSet<>();
190+
}).add(extension);
191+
}
192+
193+
private Configuration createConditionalDependenciesConfiguration(Project project, Dependency conditionalDep) {
194+
/*
195+
Configuration conditionalDepConfiguration = project.getConfigurations()
196+
.detachedConfiguration()
197+
.extendsFrom(enforcedPlatforms);
198+
*/
199+
Configuration conditionalDepConfiguration = project.getConfigurations().detachedConfiguration();
200+
enforcedPlatforms.getExcludeRules().forEach(rule -> {
201+
conditionalDepConfiguration.exclude(Map.of(
202+
"group", rule.getGroup(),
203+
"module", rule.getModule()));
204+
});
205+
enforcedPlatforms.getAllDependencies().forEach(dependency -> {
206+
conditionalDepConfiguration.getDependencies().add(dependency);
207+
});
208+
conditionalDepConfiguration.getDependencies().add(conditionalDep);
209+
return conditionalDepConfiguration;
210+
}
211+
212+
private void enableConditionalDependency(ModuleVersionIdentifier dependency) {
213+
final Set<ExtensionDependency> extensions = featureVariants.remove(getFeatureKey(dependency));
214+
if (extensions == null) {
215+
return;
216+
}
217+
extensions.forEach(e -> e.importConditionalDependency(project.getDependencies(), dependency));
218+
}
219+
220+
private boolean exist(List<ArtifactKey> dependencies) {
221+
return existingArtifacts.containsAll(dependencies);
222+
}
223+
224+
private boolean exists(Dependency dependency) {
225+
return existingArtifacts
226+
.contains(ArtifactKey.of(dependency.getGroup(), dependency.getName(), null, ArtifactCoords.TYPE_JAR));
227+
}
228+
229+
public boolean exists(ExtensionDependency dependency) {
230+
return existingArtifacts
231+
.contains(ArtifactKey.of(dependency.getGroup(), dependency.getName(), null, ArtifactCoords.TYPE_JAR));
232+
}
233+
234+
private static GACT getFeatureKey(ModuleVersionIdentifier version) {
235+
return new GACT(version.getGroup(), version.getName());
236+
}
237+
238+
private static GACT getFeatureKey(Dependency version) {
239+
return new GACT(version.getGroup(), version.getName());
240+
}
241+
242+
private static ArtifactKey getKey(ResolvedArtifact a) {
243+
return ArtifactKey.of(a.getModuleVersion().getId().getGroup(), a.getName(), a.getClassifier(), a.getType());
244+
}
245+
}

0 commit comments

Comments
 (0)