Skip to content

Commit

Permalink
Upgrade to GraalVM SDK 23.0.2
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
melix committed Feb 1, 2024
1 parent 2bc6976 commit 9f49c1b
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ val mavenExtension = project.extensions.create<MavenExtension>("maven").also {
it.description.convention(project.description)
}

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

val repositoryElements by configurations.creating {
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ maven = "3.8.6"
mavenAnnotations = "3.6.4"
mavenEmbedder = "3.8.6"
mavenWagon = "3.4.3"
graalvm = "22.3.5"
graalvm = "23.0.2"
jackson = "2.13.5"
junitPlatform = "1.10.0"
junitJupiter = "5.10.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2003-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.graalvm.build.maven;

import org.gradle.api.DefaultTask;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.provider.MapProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;

public abstract class GenerateRuntimeMetadata extends DefaultTask {
@Input
public abstract Property<String> getClassName();

@Input
public abstract MapProperty<String, String> getMetadata();

@OutputDirectory
public abstract DirectoryProperty getOutputDirectory();

@TaskAction
public void generateClass() throws IOException {
String fqcn = getClassName().get();
Map<String, String> metadata = getMetadata().get();
File outputDir = getOutputDirectory().getAsFile().get();
String packageName = fqcn.substring(0, fqcn.lastIndexOf("."));
String packagePath = packageName.replace(".", "/");
String className = fqcn.substring(fqcn.lastIndexOf(".") + 1);
Path outputPath = outputDir.toPath().resolve(packagePath);
Files.createDirectories(outputPath);
Path outputFile = outputPath.resolve(className + ".java");
try (PrintWriter writer = new PrintWriter(outputFile.toFile(), StandardCharsets.UTF_8)) {
writer.println("package " + packageName + ";");
writer.println();
writer.println("public abstract class " + className + " {");
writer.println(" private " + className + "() { }");
writer.println();
for (Map.Entry<String, String> entry : metadata.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
writer.println(" public static final String " + key + " = \"" + value + "\";");
}
writer.println();
writer.println("}");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import org.graalvm.build.maven.GeneratePluginDescriptor
import org.graalvm.build.maven.GenerateRuntimeMetadata
import org.gradle.api.publish.maven.tasks.GenerateMavenPom
import org.gradle.api.tasks.Copy
import org.gradle.kotlin.dsl.register
Expand Down Expand Up @@ -73,6 +74,22 @@ val generatePluginDescriptor = tasks.register<GeneratePluginDescriptor>("generat
outputDirectory.set(project.layout.buildDirectory.dir("generated/maven-plugin"))
}

val writeConstants = tasks.register<GenerateRuntimeMetadata>("writeRuntimeMetadata") {
className.set("org.graalvm.buildtools.maven.RuntimeMetadata")
outputDirectory.set(layout.buildDirectory.dir("generated/runtime-metadata"))
metadata.put("GROUP_ID", project.group as String)
metadata.put("VERSION", project.version as String)
metadata.put("JUNIT_PLATFORM_NATIVE_ARTIFACT_ID", "junit-platform-native")
}

sourceSets {
main {
java {
srcDir(writeConstants)
}
}
}

tasks {
jar {
from(generatePluginDescriptor)
Expand Down
4 changes: 2 additions & 2 deletions native-maven-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,9 @@ maven {
}

dependencies {
implementation(libs.junitPlatformNative)
implementation(libs.utils)
implementation(libs.jackson.databind)
implementation(libs.jvmReachabilityMetadata)
implementation(libs.graalvm.svm)
implementation(libs.plexus.utils)
implementation(libs.plexus.xml)

Expand Down Expand Up @@ -177,4 +175,6 @@ tasks {

tasks.withType<Checkstyle>().configureEach {
configFile = layout.projectDirectory.dir("../config/checkstyle.xml").asFile
// generated code
exclude("**/RuntimeMetadata*")
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,32 +50,43 @@
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.resolution.DependencyResult;
import org.graalvm.buildtools.utils.NativeImageConfigurationUtils;
import org.graalvm.junit.platform.JUnitPlatformFeature;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

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

/**
* This goal builds and runs native tests.
*
* @author Sebastien Deleuze
*/
@Mojo(name = "test", defaultPhase = LifecyclePhase.TEST, threadSafe = true,
requiresDependencyResolution = ResolutionScope.TEST,
requiresDependencyCollection = ResolutionScope.TEST)
requiresDependencyResolution = ResolutionScope.TEST,
requiresDependencyCollection = ResolutionScope.TEST)
public class NativeTestMojo extends AbstractNativeImageMojo {

@Parameter(property = "skipTests", defaultValue = "false")
Expand All @@ -89,31 +100,39 @@ protected void populateApplicationClasspath() throws MojoExecutionException {
super.populateApplicationClasspath();
imageClasspath.add(Paths.get(project.getBuild().getTestOutputDirectory()));
project.getBuild()
.getTestResources()
.stream()
.map(FileSet::getDirectory)
.map(Paths::get)
.forEach(imageClasspath::add);
.getTestResources()
.stream()
.map(FileSet::getDirectory)
.map(Paths::get)
.forEach(imageClasspath::add);
}

@Override
protected List<String> getDependencyScopes() {
return Arrays.asList(
Artifact.SCOPE_COMPILE,
Artifact.SCOPE_RUNTIME,
Artifact.SCOPE_TEST,
Artifact.SCOPE_COMPILE_PLUS_RUNTIME
Artifact.SCOPE_COMPILE,
Artifact.SCOPE_RUNTIME,
Artifact.SCOPE_TEST,
Artifact.SCOPE_COMPILE_PLUS_RUNTIME
);
}

@Override
protected void addDependenciesToClasspath() throws MojoExecutionException {
super.addDependenciesToClasspath();
Set<Module> modules = new HashSet<>();
//noinspection SimplifyStreamApiCallChains
pluginArtifacts.stream()
.filter(it -> it.getGroupId().startsWith(NativeImageConfigurationUtils.MAVEN_GROUP_ID) || it.getGroupId().startsWith("org.junit"))
.map(it -> it.getFile().toPath())
.forEach(imageClasspath::add);
findNativePlatformJar().ifPresent(imageClasspath::add);
// do not use peek as Stream implementations are free to discard it
.map(a -> {
modules.add(new Module(a.getGroupId(), a.getArtifactId()));
return a;
})
.filter(it -> it.getGroupId().startsWith(NativeImageConfigurationUtils.MAVEN_GROUP_ID) || it.getGroupId().startsWith("org.junit"))
.map(it -> it.getFile().toPath())
.forEach(imageClasspath::add);
var jars = findJunitPlatformNativeJars(modules);
imageClasspath.addAll(jars);
}

@Override
Expand Down Expand Up @@ -142,7 +161,7 @@ public void execute() throws MojoExecutionException {
systemProperties = new HashMap<>();
}
systemProperties.put("junit.platform.listeners.uid.tracking.output.dir",
NativeExtension.testIdsDirectory(outputDirectory.getAbsolutePath()));
NativeExtension.testIdsDirectory(outputDirectory.getAbsolutePath()));

imageName = NATIVE_TESTS_EXE;
mainClass = "org.graalvm.junit.platform.NativeImageJUnitLauncher";
Expand Down Expand Up @@ -248,15 +267,71 @@ private static Stream<Path> findFiles(Path dir, String prefix) throws IOExceptio
return Stream.empty();
}
return Files.find(dir, Integer.MAX_VALUE,
(path, basicFileAttributes) -> (basicFileAttributes.isRegularFile()
&& path.getFileName().toString().startsWith(prefix)));
(path, basicFileAttributes) -> (basicFileAttributes.isRegularFile()
&& path.getFileName().toString().startsWith(prefix)));
}

private static Optional<Path> findNativePlatformJar() {
private List<Path> findJunitPlatformNativeJars(Set<Module> modulesAlreadyOnClasspath) {
RepositorySystemSession repositorySession = mavenSession.getRepositorySession();
DefaultRepositorySystemSession newSession = new DefaultRepositorySystemSession(repositorySession);
CollectRequest collectRequest = new CollectRequest();
List<RemoteRepository> repositories = project.getRemoteProjectRepositories();
collectRequest.setRepositories(repositories);
DefaultArtifact artifact = new DefaultArtifact(
RuntimeMetadata.GROUP_ID,
RuntimeMetadata.JUNIT_PLATFORM_NATIVE_ARTIFACT_ID,
null,
"jar",
RuntimeMetadata.VERSION
);
Dependency dependency = new Dependency(artifact, "runtime");
collectRequest.addDependency(dependency);
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null);
DependencyResult dependencyResult;
try {
return Optional.of(new File(JUnitPlatformFeature.class.getProtectionDomain().getCodeSource().getLocation().toURI()).toPath());
} catch (URISyntaxException e) {
return Optional.empty();
dependencyResult = repositorySystem.resolveDependencies(newSession, dependencyRequest);
} catch (DependencyResolutionException e) {
return Collections.emptyList();
}
return dependencyResult.getArtifactResults()
.stream()
.map(ArtifactResult::getArtifact)
.filter(a -> !modulesAlreadyOnClasspath.contains(new Module(a.getGroupId(), a.getArtifactId())))
.map(a -> a.getFile().toPath())
.collect(Collectors.toList());
}

private static final class Module {
private final String groupId;
private final String artifactId;

private Module(String groupId, String artifactId) {
this.groupId = groupId;
this.artifactId = artifactId;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

Module module = (Module) o;

if (!groupId.equals(module.groupId)) {
return false;
}
return artifactId.equals(module.artifactId);
}

@Override
public int hashCode() {
int result = groupId.hashCode();
result = 31 * result + artifactId.hashCode();
return result;
}
}
}

0 comments on commit 9f49c1b

Please sign in to comment.