Skip to content

Commit

Permalink
Support getting GraalVM version at native executable build time
Browse files Browse the repository at this point in the history
* Moves core parts of `GraalVM.Version` to `io.quarkus.runtime` to
  enable us to use the functionality during native executable build time,
  e.g. from the generated `io.quarkus.runner.Feature`
* Infers version from `org.graalvm.vendorversion` property
* Avoids breaking existing API
* Deprecates methods in the deployment package in favor of those in the
  runtime package, so that we can eliminate the former in the future.
  • Loading branch information
zakkak committed Oct 4, 2024
1 parent e2c1d31 commit 2efe0aa
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import java.util.Objects;

import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.deployment.pkg.steps.GraalVM;
import io.quarkus.runtime.graal.GraalVM;

/**
* A build item that indicates that a Java package should be exported using
Expand All @@ -23,6 +23,25 @@ public JPMSExportBuildItem(String moduleName, String packageName, GraalVM.Versio
this(moduleName, packageName, exportSince, null);
}

/**
* Creates a build item that indicates that a Java package should be exported for a specific GraalVM version range.
*
* @param moduleName the module name
* @param packageName the package name
* @param exportSince the version of GraalVM since which the package should be exported (inclusive)
* @param exportBefore the version of GraalVM before which the package should be exported (exclusive)
* @deprecated use {@link #JPMSExportBuildItem(String, String, GraalVM.Version, GraalVM.Version)} instead
*/
@Deprecated
public JPMSExportBuildItem(String moduleName, String packageName,
io.quarkus.deployment.pkg.steps.GraalVM.Version exportSince,
io.quarkus.deployment.pkg.steps.GraalVM.Version exportBefore) {
this.moduleName = moduleName;
this.packageName = packageName;
this.exportSince = exportSince;
this.exportBefore = exportBefore;
}

/**
* Creates a build item that indicates that a Java package should be exported for a specific GraalVM version range.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.quarkus.deployment.pkg.steps;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
Expand All @@ -9,6 +8,7 @@
import java.util.stream.Stream;

import io.quarkus.deployment.builditem.nativeimage.NativeMinimalJavaVersionBuildItem;
import io.quarkus.runtime.graal.GraalVM.Distribution;

public final class GraalVM {

Expand Down Expand Up @@ -152,7 +152,7 @@ private static String graalVersion(String buildInfo, int jdkFeature) {
if (versMatcher.find()) {
return matchVersion(version);
} else {
return GRAAL_MAPPING.get(jdkFeature);
return Version.GRAAL_MAPPING.get(Integer.toString(jdkFeature));
}
}

Expand All @@ -165,15 +165,10 @@ private static String buildVersion(String buildInfo, String buildPrefix) {
}
}

// Temporarily work around https://github.com/quarkusio/quarkus/issues/36246,
// till we have a consensus on how to move forward in
// https://github.com/quarkusio/quarkus/issues/34161
private static final Map<Integer, String> GRAAL_MAPPING = Map.of(22, "24.0",
23, "24.1",
24, "24.2",
25, "25.0");
public static final class Version extends io.quarkus.runtime.graal.GraalVM.Version {

public static final class Version implements Comparable<Version> {
// Get access to GRAAL_MAPPING without making it public
private static final Map<String, String> GRAAL_MAPPING = io.quarkus.runtime.graal.GraalVM.Version.GRAAL_MAPPING;

/**
* JDK version used with native-image tool:
Expand All @@ -200,25 +195,28 @@ public static final class Version implements Comparable<Version> {
/**
* The minimum version of GraalVM supported by Quarkus.
* Versions prior to this are expected to cause major issues.
*
* @deprecated Use {@link io.quarkus.runtime.graal.GraalVM.Version.MINIMUM} instead.
*/
@Deprecated
public static final Version MINIMUM = VERSION_23_0_0;
/**
* The current version of GraalVM supported by Quarkus.
* This version is the one actively being tested and is expected to give the best experience.
*
* @deprecated Use {@link io.quarkus.runtime.graal.GraalVM.Version.CURRENT} instead.
*/
@Deprecated
public static final Version CURRENT = VERSION_23_1_0;
/**
* The minimum version of GraalVM officially supported by Quarkus.
* Versions prior to this are expected to work but are not given the same level of testing or priority.
*
* @deprecated Use {@link io.quarkus.runtime.graal.GraalVM.Version.MINIMUM_SUPPORTED} instead.
*/
@Deprecated
public static final Version MINIMUM_SUPPORTED = CURRENT;

final String fullVersion;
public final Runtime.Version javaVersion;
final Distribution distribution;
private int[] versions;
private String suffix;

Version(String fullVersion, String version, Distribution distro) {
this(fullVersion, version, "11", distro);
}
Expand All @@ -228,35 +226,27 @@ public static final class Version implements Comparable<Version> {
}

Version(String fullVersion, String version, Runtime.Version javaVersion, Distribution distro) {
this.fullVersion = fullVersion;
breakdownVersion(version);
this.javaVersion = javaVersion;
this.distribution = distro;
super(fullVersion, version, javaVersion, distro);
}

private void breakdownVersion(String version) {
int dash = version.indexOf('-');
if (dash != -1) {
this.suffix = version.substring(dash + 1);
version = version.substring(0, dash);
}
this.versions = Arrays.stream(version.split("\\.")).mapToInt(Integer::parseInt).toArray();
public int compareTo(GraalVM.Version o) {
return compareTo((io.quarkus.runtime.graal.GraalVM.Version) o);
}

Distribution getDistribution() {
return distribution;
}

String getFullVersion() {
return fullVersion;
}

boolean isObsolete() {
return this.compareTo(MINIMUM) < 0;
return this.compareTo(io.quarkus.runtime.graal.GraalVM.Version.MINIMUM) < 0;
}

boolean isSupported() {
return this.compareTo(MINIMUM_SUPPORTED) >= 0;
}

boolean isMandrel() {
return distribution == Distribution.MANDREL;
return this.compareTo(io.quarkus.runtime.graal.GraalVM.Version.MINIMUM_SUPPORTED) >= 0;
}

boolean isNewerThan(Version version) {
Expand All @@ -278,26 +268,6 @@ public boolean jdkVersionGreaterOrEqualTo(String version) {
return javaVersion.compareToIgnoreOptional(Runtime.Version.parse(version)) >= 0;
}

@Override
public int compareTo(Version o) {
int i = 0;
for (; i < this.versions.length; i++) {
if (i >= o.versions.length) {
if (this.versions[i] != 0) {
return 1;
}
} else if (this.versions[i] != o.versions[i]) {
return this.versions[i] - o.versions[i];
}
}
for (; i < o.versions.length; i++) {
if (o.versions[i] != 0) {
return -1;
}
}
return 0;
}

public static Version of(Stream<String> output) {
String stringOutput = output.collect(Collectors.joining("\n"));
List<String> lines = stringOutput.lines()
Expand Down Expand Up @@ -344,43 +314,8 @@ private static boolean isMandrel(String s) {
return s != null && s.contains("Mandrel Distribution");
}

/**
* Returns the Mandrel/GraalVM version as a string. e.g. 21.3.0-rc1
*/
public String getVersionAsString() {
String version = Arrays.stream(versions).mapToObj(Integer::toString).collect(Collectors.joining("."));
if (suffix != null) {
return version + "-" + suffix;
}
return version;
}

public String getMajorMinorAsString() {
if (versions.length >= 2) {
return versions[0] + "." + versions[1];
}
return versions[0] + ".0";
}

@Override
public String toString() {
return "Version{" +
"version="
+ getVersionAsString() +
", fullVersion=" + fullVersion +
", distribution=" + distribution +
", javaVersion=" + javaVersion +
'}';
}

public boolean isJava17() {
return javaVersion.feature() == 17;
}
}

enum Distribution {
GRAALVM,
LIBERICA,
MANDREL;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,10 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, LocalesBuildTimeCon
System.setProperty("native.image.path", finalExecutablePath.toAbsolutePath().toString());

return new NativeImageBuildItem(finalExecutablePath,
new NativeImageBuildItem.GraalVMVersion(graalVMVersion.fullVersion,
new NativeImageBuildItem.GraalVMVersion(graalVMVersion.getFullVersion(),
graalVMVersion.getVersionAsString(),
graalVMVersion.javaVersion.feature(),
graalVMVersion.distribution.name()),
graalVMVersion.getDistribution().name()),
false);
} catch (ImageGenerationFailureException e) {
throw e;
Expand Down Expand Up @@ -489,8 +489,8 @@ private RuntimeException imageGenerationFailed(int exitValue, boolean isContaine
}

private void checkGraalVMVersion(GraalVM.Version version) {
log.info("Running Quarkus native-image plugin on " + version.distribution.name() + " " + version.getVersionAsString()
+ " JDK " + version.javaVersion);
log.info("Running Quarkus native-image plugin on " + version.getDistribution().name() + " "
+ version.getVersionAsString() + " JDK " + version.javaVersion);
if (version.isObsolete()) {
throw new IllegalStateException(
"Out of date version of GraalVM or Mandrel detected: " + version.getVersionAsString() + "."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedPackageBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeReinitializedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.UnsafeAccessedFieldBuildItem;
import io.quarkus.deployment.pkg.steps.GraalVM;
import io.quarkus.gizmo.CatchBlockCreator;
import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.ClassOutput;
import io.quarkus.gizmo.MethodCreator;
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.gizmo.TryBlock;
import io.quarkus.runtime.graal.GraalVM;

public class NativeImageFeatureStep {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package io.quarkus.deployment.pkg.steps;

import static io.quarkus.deployment.pkg.steps.GraalVM.Distribution.GRAALVM;
import static io.quarkus.deployment.pkg.steps.GraalVM.Distribution.MANDREL;
import static io.quarkus.runtime.graal.GraalVM.Distribution.GRAALVM;
import static io.quarkus.runtime.graal.GraalVM.Distribution.LIBERICA;
import static io.quarkus.runtime.graal.GraalVM.Distribution.MANDREL;
import static org.assertj.core.api.Assertions.assertThat;

import java.util.stream.Stream;
Expand All @@ -12,8 +13,8 @@
import org.junit.jupiter.params.provider.ValueSource;

import io.quarkus.deployment.builditem.nativeimage.NativeMinimalJavaVersionBuildItem;
import io.quarkus.deployment.pkg.steps.GraalVM.Distribution;
import io.quarkus.deployment.pkg.steps.GraalVM.Version;
import io.quarkus.runtime.graal.GraalVM.Distribution;

public class GraalVMTest {

Expand Down Expand Up @@ -98,18 +99,15 @@ public void testGraalVMVersionDetected() {

static void assertVersion(Version graalVmVersion, Distribution distro, Version version) {
assertThat(graalVmVersion.compareTo(version)).isEqualTo(0);
assertThat(version.distribution).isEqualTo(distro);
if (distro == MANDREL) {
assertThat(version.isMandrel()).isTrue();
}
assertThat(version.toString()).contains(distro.name());
}

@Test
public void testGraalVM21LibericaVersionParser() {
Version graalVM21Dev = Version.of(Stream.of(("native-image 21.0.1 2023-10-17\n"
+ "GraalVM Runtime Environment Liberica-NIK-23.1.1-1 (build 21.0.1+12-LTS)\n"
+ "Substrate VM Liberica-NIK-23.1.1-1 (build 21.0.1+12-LTS, serial gc)").split("\\n")));
assertThat(graalVM21Dev.distribution.name()).isEqualTo("LIBERICA");
assertThat(graalVM21Dev.toString()).contains(LIBERICA.name());
assertThat(graalVM21Dev.getVersionAsString()).isEqualTo("23.1.1");
assertThat(graalVM21Dev.javaVersion.toString()).isEqualTo("21.0.1+12-LTS");
assertThat(graalVM21Dev.javaVersion.feature()).isEqualTo(21);
Expand All @@ -121,7 +119,7 @@ public void testGraalVM21VersionParser() {
Version graalVM21Dev = Version.of(Stream.of(("native-image 21 2023-09-19\n"
+ "GraalVM Runtime Environment GraalVM CE 21+35.1 (build 21+35-jvmci-23.1-b15)\n"
+ "Substrate VM GraalVM CE 21+35.1 (build 21+35, serial gc)").split("\\n")));
assertThat(graalVM21Dev.distribution.name()).isEqualTo("GRAALVM");
assertThat(graalVM21Dev.toString()).contains(GRAALVM.name());
assertThat(graalVM21Dev.getVersionAsString()).isEqualTo("23.1");
assertThat(graalVM21Dev.javaVersion.toString()).isEqualTo("21+35-jvmci-23.1-b15");
assertThat(graalVM21Dev.javaVersion.feature()).isEqualTo(21);
Expand All @@ -133,7 +131,7 @@ public void testGraalVM21DevVersionParser() {
Version graalVM21Dev = Version.of(Stream.of(("native-image 21 2023-09-19\n" +
"GraalVM Runtime Environment GraalVM CE 21-dev+35.1 (build 21+35-jvmci-23.1-b14)\n" +
"Substrate VM GraalVM CE 21-dev+35.1 (build 21+35, serial gc)").split("\\n")));
assertThat(graalVM21Dev.distribution.name()).isEqualTo("GRAALVM");
assertThat(graalVM21Dev.toString()).contains(GRAALVM.name());
assertThat(graalVM21Dev.getVersionAsString()).isEqualTo("23.1-dev");
assertThat(graalVM21Dev.javaVersion.toString()).isEqualTo("21+35-jvmci-23.1-b14");
assertThat(graalVM21Dev.javaVersion.feature()).isEqualTo(21);
Expand All @@ -145,7 +143,7 @@ public void testGraalVM22DevVersionParser() {
Version graalVM22Dev = Version.of(Stream.of(("native-image 22 2024-03-19\n"
+ "GraalVM Runtime Environment GraalVM CE 22-dev+16.1 (build 22+16-jvmci-b01)\n"
+ "Substrate VM GraalVM CE 22-dev+16.1 (build 22+16, serial gc)").split("\\n")));
assertThat(graalVM22Dev.distribution.name()).isEqualTo("GRAALVM");
assertThat(graalVM22Dev.toString()).contains(GRAALVM.name());
assertThat(graalVM22Dev.getVersionAsString()).isEqualTo("24.0-dev");
assertThat(graalVM22Dev.javaVersion.toString()).isEqualTo("22+16-jvmci-b01");
assertThat(graalVM22Dev.javaVersion.feature()).isEqualTo(22);
Expand All @@ -158,7 +156,7 @@ public void testGraalVMEE22DevVersionParser() {
+ "Java(TM) SE Runtime Environment Oracle GraalVM 22-dev+25.1 (build 22+25-jvmci-b01)\n"
+ "Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 22-dev+25.1 (build 22+25-jvmci-b01, mixed mode, sharing)")
.split("\\n")));
assertThat(graalVMEE22Dev.distribution.name()).isEqualTo("GRAALVM");
assertThat(graalVMEE22Dev.toString()).contains(GRAALVM.name());
assertThat(graalVMEE22Dev.getVersionAsString()).isEqualTo("24.0-dev");
assertThat(graalVMEE22Dev.javaVersion.toString()).isEqualTo("22+25-jvmci-b01");
assertThat(graalVMEE22Dev.javaVersion.feature()).isEqualTo(22);
Expand All @@ -171,7 +169,7 @@ public void testGraalVMEA24DevVersionParser() {
+ "OpenJDK Runtime Environment Oracle GraalVM 24-dev.ea+10.1 (build 24-ea+10-1076)\n"
+ "OpenJDK 64-Bit Server VM Oracle GraalVM 24-dev.ea+10.1 (build 24-ea+10-1076, mixed mode, sharing)")
.split("\\n")));
assertThat(graalVMEA24Dev.distribution.name()).isEqualTo("GRAALVM");
assertThat(graalVMEA24Dev.toString()).contains(GRAALVM.name());
assertThat(graalVMEA24Dev.getVersionAsString()).isEqualTo("24.2-dev");
assertThat(graalVMEA24Dev.javaVersion.toString()).isEqualTo("24-ea+10-1076");
assertThat(graalVMEA24Dev.javaVersion.feature()).isEqualTo(24);
Expand Down
Loading

0 comments on commit 2efe0aa

Please sign in to comment.