Skip to content

Commit 9f49c1b

Browse files
committed
Upgrade to GraalVM SDK 23.0.2
The Maven plugin was using the dependency at compile time, when it should only have been used at runtime. Unfortunately, if the dependency is added as a runtime only dependency, then the Maven plugin loading system will fail. Therefore, this commit changes how the JUnit Platform Native dependency is resolved by the plugin to do it in a similar way as what is done in the Gradle plugin, by resolving it at runtime.
1 parent 2bc6976 commit 9f49c1b

File tree

6 files changed

+192
-30
lines changed

6 files changed

+192
-30
lines changed

build-logic/common-plugins/src/main/kotlin/org.graalvm.build.publishing.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ val mavenExtension = project.extensions.create<MavenExtension>("maven").also {
5858
it.description.convention(project.description)
5959
}
6060

61-
val publishingTasks = tasks.withType<PublishToMavenRepository>()
61+
val publishingTasks = tasks.withType<AbstractPublishToMaven>()
6262
.matching { it.name.endsWith("ToCommonRepository") }
6363

6464
val repositoryElements by configurations.creating {

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ maven = "3.8.6"
99
mavenAnnotations = "3.6.4"
1010
mavenEmbedder = "3.8.6"
1111
mavenWagon = "3.4.3"
12-
graalvm = "22.3.5"
12+
graalvm = "23.0.2"
1313
jackson = "2.13.5"
1414
junitPlatform = "1.10.0"
1515
junitJupiter = "5.10.0"
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2003-2021 the original author or authors.
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+
* https://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+
package org.graalvm.build.maven;
17+
18+
import org.gradle.api.DefaultTask;
19+
import org.gradle.api.file.DirectoryProperty;
20+
import org.gradle.api.provider.MapProperty;
21+
import org.gradle.api.provider.Property;
22+
import org.gradle.api.tasks.Input;
23+
import org.gradle.api.tasks.OutputDirectory;
24+
import org.gradle.api.tasks.TaskAction;
25+
26+
import java.io.File;
27+
import java.io.IOException;
28+
import java.io.PrintWriter;
29+
import java.nio.charset.StandardCharsets;
30+
import java.nio.file.Files;
31+
import java.nio.file.Path;
32+
import java.util.Map;
33+
34+
public abstract class GenerateRuntimeMetadata extends DefaultTask {
35+
@Input
36+
public abstract Property<String> getClassName();
37+
38+
@Input
39+
public abstract MapProperty<String, String> getMetadata();
40+
41+
@OutputDirectory
42+
public abstract DirectoryProperty getOutputDirectory();
43+
44+
@TaskAction
45+
public void generateClass() throws IOException {
46+
String fqcn = getClassName().get();
47+
Map<String, String> metadata = getMetadata().get();
48+
File outputDir = getOutputDirectory().getAsFile().get();
49+
String packageName = fqcn.substring(0, fqcn.lastIndexOf("."));
50+
String packagePath = packageName.replace(".", "/");
51+
String className = fqcn.substring(fqcn.lastIndexOf(".") + 1);
52+
Path outputPath = outputDir.toPath().resolve(packagePath);
53+
Files.createDirectories(outputPath);
54+
Path outputFile = outputPath.resolve(className + ".java");
55+
try (PrintWriter writer = new PrintWriter(outputFile.toFile(), StandardCharsets.UTF_8)) {
56+
writer.println("package " + packageName + ";");
57+
writer.println();
58+
writer.println("public abstract class " + className + " {");
59+
writer.println(" private " + className + "() { }");
60+
writer.println();
61+
for (Map.Entry<String, String> entry : metadata.entrySet()) {
62+
String key = entry.getKey();
63+
String value = entry.getValue();
64+
writer.println(" public static final String " + key + " = \"" + value + "\";");
65+
}
66+
writer.println();
67+
writer.println("}");
68+
}
69+
}
70+
}

native-maven-plugin/build-plugins/src/main/kotlin/org.graalvm.build.maven-plugin.gradle.kts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import org.graalvm.build.maven.GeneratePluginDescriptor
2+
import org.graalvm.build.maven.GenerateRuntimeMetadata
23
import org.gradle.api.publish.maven.tasks.GenerateMavenPom
34
import org.gradle.api.tasks.Copy
45
import org.gradle.kotlin.dsl.register
@@ -73,6 +74,22 @@ val generatePluginDescriptor = tasks.register<GeneratePluginDescriptor>("generat
7374
outputDirectory.set(project.layout.buildDirectory.dir("generated/maven-plugin"))
7475
}
7576

77+
val writeConstants = tasks.register<GenerateRuntimeMetadata>("writeRuntimeMetadata") {
78+
className.set("org.graalvm.buildtools.maven.RuntimeMetadata")
79+
outputDirectory.set(layout.buildDirectory.dir("generated/runtime-metadata"))
80+
metadata.put("GROUP_ID", project.group as String)
81+
metadata.put("VERSION", project.version as String)
82+
metadata.put("JUNIT_PLATFORM_NATIVE_ARTIFACT_ID", "junit-platform-native")
83+
}
84+
85+
sourceSets {
86+
main {
87+
java {
88+
srcDir(writeConstants)
89+
}
90+
}
91+
}
92+
7693
tasks {
7794
jar {
7895
from(generatePluginDescriptor)

native-maven-plugin/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,9 @@ maven {
6060
}
6161

6262
dependencies {
63-
implementation(libs.junitPlatformNative)
6463
implementation(libs.utils)
6564
implementation(libs.jackson.databind)
6665
implementation(libs.jvmReachabilityMetadata)
67-
implementation(libs.graalvm.svm)
6866
implementation(libs.plexus.utils)
6967
implementation(libs.plexus.xml)
7068

@@ -177,4 +175,6 @@ tasks {
177175

178176
tasks.withType<Checkstyle>().configureEach {
179177
configFile = layout.projectDirectory.dir("../config/checkstyle.xml").asFile
178+
// generated code
179+
exclude("**/RuntimeMetadata*")
180180
}

native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeTestMojo.java

Lines changed: 101 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,32 +50,43 @@
5050
import org.apache.maven.plugins.annotations.Parameter;
5151
import org.apache.maven.plugins.annotations.ResolutionScope;
5252
import org.codehaus.plexus.util.xml.Xpp3Dom;
53+
import org.eclipse.aether.DefaultRepositorySystemSession;
54+
import org.eclipse.aether.RepositorySystemSession;
55+
import org.eclipse.aether.artifact.DefaultArtifact;
56+
import org.eclipse.aether.collection.CollectRequest;
57+
import org.eclipse.aether.graph.Dependency;
58+
import org.eclipse.aether.repository.RemoteRepository;
59+
import org.eclipse.aether.resolution.ArtifactResult;
60+
import org.eclipse.aether.resolution.DependencyRequest;
61+
import org.eclipse.aether.resolution.DependencyResolutionException;
62+
import org.eclipse.aether.resolution.DependencyResult;
5363
import org.graalvm.buildtools.utils.NativeImageConfigurationUtils;
54-
import org.graalvm.junit.platform.JUnitPlatformFeature;
5564

56-
import java.io.File;
5765
import java.io.IOException;
5866
import java.io.UncheckedIOException;
59-
import java.net.URISyntaxException;
6067
import java.nio.file.Files;
6168
import java.nio.file.Path;
6269
import java.nio.file.Paths;
6370
import java.util.ArrayList;
6471
import java.util.Arrays;
72+
import java.util.Collections;
6573
import java.util.HashMap;
74+
import java.util.HashSet;
6675
import java.util.List;
67-
import java.util.Optional;
76+
import java.util.Set;
77+
import java.util.stream.Collectors;
6878
import java.util.stream.Stream;
6979

7080
import static org.graalvm.buildtools.utils.NativeImageConfigurationUtils.NATIVE_TESTS_EXE;
7181

7282
/**
7383
* This goal builds and runs native tests.
84+
*
7485
* @author Sebastien Deleuze
7586
*/
7687
@Mojo(name = "test", defaultPhase = LifecyclePhase.TEST, threadSafe = true,
77-
requiresDependencyResolution = ResolutionScope.TEST,
78-
requiresDependencyCollection = ResolutionScope.TEST)
88+
requiresDependencyResolution = ResolutionScope.TEST,
89+
requiresDependencyCollection = ResolutionScope.TEST)
7990
public class NativeTestMojo extends AbstractNativeImageMojo {
8091

8192
@Parameter(property = "skipTests", defaultValue = "false")
@@ -89,31 +100,39 @@ protected void populateApplicationClasspath() throws MojoExecutionException {
89100
super.populateApplicationClasspath();
90101
imageClasspath.add(Paths.get(project.getBuild().getTestOutputDirectory()));
91102
project.getBuild()
92-
.getTestResources()
93-
.stream()
94-
.map(FileSet::getDirectory)
95-
.map(Paths::get)
96-
.forEach(imageClasspath::add);
103+
.getTestResources()
104+
.stream()
105+
.map(FileSet::getDirectory)
106+
.map(Paths::get)
107+
.forEach(imageClasspath::add);
97108
}
98109

99110
@Override
100111
protected List<String> getDependencyScopes() {
101112
return Arrays.asList(
102-
Artifact.SCOPE_COMPILE,
103-
Artifact.SCOPE_RUNTIME,
104-
Artifact.SCOPE_TEST,
105-
Artifact.SCOPE_COMPILE_PLUS_RUNTIME
113+
Artifact.SCOPE_COMPILE,
114+
Artifact.SCOPE_RUNTIME,
115+
Artifact.SCOPE_TEST,
116+
Artifact.SCOPE_COMPILE_PLUS_RUNTIME
106117
);
107118
}
108119

109120
@Override
110121
protected void addDependenciesToClasspath() throws MojoExecutionException {
111122
super.addDependenciesToClasspath();
123+
Set<Module> modules = new HashSet<>();
124+
//noinspection SimplifyStreamApiCallChains
112125
pluginArtifacts.stream()
113-
.filter(it -> it.getGroupId().startsWith(NativeImageConfigurationUtils.MAVEN_GROUP_ID) || it.getGroupId().startsWith("org.junit"))
114-
.map(it -> it.getFile().toPath())
115-
.forEach(imageClasspath::add);
116-
findNativePlatformJar().ifPresent(imageClasspath::add);
126+
// do not use peek as Stream implementations are free to discard it
127+
.map(a -> {
128+
modules.add(new Module(a.getGroupId(), a.getArtifactId()));
129+
return a;
130+
})
131+
.filter(it -> it.getGroupId().startsWith(NativeImageConfigurationUtils.MAVEN_GROUP_ID) || it.getGroupId().startsWith("org.junit"))
132+
.map(it -> it.getFile().toPath())
133+
.forEach(imageClasspath::add);
134+
var jars = findJunitPlatformNativeJars(modules);
135+
imageClasspath.addAll(jars);
117136
}
118137

119138
@Override
@@ -142,7 +161,7 @@ public void execute() throws MojoExecutionException {
142161
systemProperties = new HashMap<>();
143162
}
144163
systemProperties.put("junit.platform.listeners.uid.tracking.output.dir",
145-
NativeExtension.testIdsDirectory(outputDirectory.getAbsolutePath()));
164+
NativeExtension.testIdsDirectory(outputDirectory.getAbsolutePath()));
146165

147166
imageName = NATIVE_TESTS_EXE;
148167
mainClass = "org.graalvm.junit.platform.NativeImageJUnitLauncher";
@@ -248,15 +267,71 @@ private static Stream<Path> findFiles(Path dir, String prefix) throws IOExceptio
248267
return Stream.empty();
249268
}
250269
return Files.find(dir, Integer.MAX_VALUE,
251-
(path, basicFileAttributes) -> (basicFileAttributes.isRegularFile()
252-
&& path.getFileName().toString().startsWith(prefix)));
270+
(path, basicFileAttributes) -> (basicFileAttributes.isRegularFile()
271+
&& path.getFileName().toString().startsWith(prefix)));
253272
}
254273

255-
private static Optional<Path> findNativePlatformJar() {
274+
private List<Path> findJunitPlatformNativeJars(Set<Module> modulesAlreadyOnClasspath) {
275+
RepositorySystemSession repositorySession = mavenSession.getRepositorySession();
276+
DefaultRepositorySystemSession newSession = new DefaultRepositorySystemSession(repositorySession);
277+
CollectRequest collectRequest = new CollectRequest();
278+
List<RemoteRepository> repositories = project.getRemoteProjectRepositories();
279+
collectRequest.setRepositories(repositories);
280+
DefaultArtifact artifact = new DefaultArtifact(
281+
RuntimeMetadata.GROUP_ID,
282+
RuntimeMetadata.JUNIT_PLATFORM_NATIVE_ARTIFACT_ID,
283+
null,
284+
"jar",
285+
RuntimeMetadata.VERSION
286+
);
287+
Dependency dependency = new Dependency(artifact, "runtime");
288+
collectRequest.addDependency(dependency);
289+
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null);
290+
DependencyResult dependencyResult;
256291
try {
257-
return Optional.of(new File(JUnitPlatformFeature.class.getProtectionDomain().getCodeSource().getLocation().toURI()).toPath());
258-
} catch (URISyntaxException e) {
259-
return Optional.empty();
292+
dependencyResult = repositorySystem.resolveDependencies(newSession, dependencyRequest);
293+
} catch (DependencyResolutionException e) {
294+
return Collections.emptyList();
295+
}
296+
return dependencyResult.getArtifactResults()
297+
.stream()
298+
.map(ArtifactResult::getArtifact)
299+
.filter(a -> !modulesAlreadyOnClasspath.contains(new Module(a.getGroupId(), a.getArtifactId())))
300+
.map(a -> a.getFile().toPath())
301+
.collect(Collectors.toList());
302+
}
303+
304+
private static final class Module {
305+
private final String groupId;
306+
private final String artifactId;
307+
308+
private Module(String groupId, String artifactId) {
309+
this.groupId = groupId;
310+
this.artifactId = artifactId;
311+
}
312+
313+
@Override
314+
public boolean equals(Object o) {
315+
if (this == o) {
316+
return true;
317+
}
318+
if (o == null || getClass() != o.getClass()) {
319+
return false;
320+
}
321+
322+
Module module = (Module) o;
323+
324+
if (!groupId.equals(module.groupId)) {
325+
return false;
326+
}
327+
return artifactId.equals(module.artifactId);
328+
}
329+
330+
@Override
331+
public int hashCode() {
332+
int result = groupId.hashCode();
333+
result = 31 * result + artifactId.hashCode();
334+
return result;
260335
}
261336
}
262337
}

0 commit comments

Comments
 (0)