Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GraalVM for JDK 21 support. #1236

Merged
merged 10 commits into from
Nov 29, 2023
4 changes: 0 additions & 4 deletions src/main/java/com/gluonhq/substrate/SubstrateDispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,6 @@ public SubstrateDispatcher(Path buildRoot, ProjectConfiguration config) throws I
System.out.println("Configuration: " + this.config);
}

Version javaVersion = this.config.checkGraalVMJavaVersion();
this.config.checkGraalVMVersion(javaVersion);
this.config.checkGraalVMVendor();

Triplet targetTriplet = config.getTargetTriplet();

this.targetConfiguration = Objects.requireNonNull(getTargetConfiguration(targetTriplet),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Gluon
* Copyright (c) 2021, 2023, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -29,8 +29,6 @@

import com.oracle.svm.core.jdk.NativeLibrarySupport;
import com.oracle.svm.core.jdk.PlatformNativeLibrarySupport;
import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl;
import com.oracle.svm.hosted.c.NativeLibraries;
import org.graalvm.nativeimage.hosted.Feature;


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ public class InternalProjectConfiguration {
private boolean useJavaFX = false;
private boolean usePrismSW = false;
private boolean enableCheckHash = true;
private boolean usesJDK11 = false;
private boolean sharedLibrary = false;
private boolean staticLibrary = false;

Expand All @@ -75,12 +74,14 @@ public class InternalProjectConfiguration {
private List<String> releaseSymbolsList;

private final ProjectConfiguration publicConfig;
private final Version javaVersion;
private final Version graalVersion;

/**
* Private projects configuration, which includes everything, including public settings
* @param config public project configuration
*/
public InternalProjectConfiguration(ProjectConfiguration config) {
public InternalProjectConfiguration(ProjectConfiguration config) throws IOException {

this.publicConfig = Objects.requireNonNull(config);

Expand Down Expand Up @@ -115,23 +116,19 @@ public InternalProjectConfiguration(ProjectConfiguration config) {
}

performHostChecks();

String versionString = getJavaVersionString();
javaVersion = parseGraalVMJavaVersion(versionString);
graalVersion = parseGraalVersion(versionString);
checkGraalVMJavaVersion();
checkGraalVMVersion();
checkGraalVMVendor();
}

public Path getGraalPath() {
return Objects.requireNonNull(this.publicConfig.getGraalPath(), "GraalVM Path is not defined");
}

public Version getGraalVersion() throws IOException {
ProcessRunner graalJava;
try {
graalJava = new ProcessRunner(getGraalVMBinPath().resolve("java").toString(), "-version");
graalJava.runProcess("java-version");
} catch (InterruptedException e) {
throw new IOException("Couldn't determine GraalVM version, " + e.toString());
}
return parseGraalVersion(graalJava.getResponse());
}

static Version parseGraalVersion(String versionString) {
String pattern = "GraalVM .*?(\\d{1,2}(\\.\\d+){0,2})";
Pattern r = Pattern.compile(pattern);
Expand All @@ -141,6 +138,23 @@ static Version parseGraalVersion(String versionString) {
return new Version(m.group(1));
}

/**
* Returns the Java version that GraalVM bundles
* @return the Java version of the GraalVM build
*/
public Version getJavaVersion() {
return javaVersion;
}

/**
* Returns the GraalVM internal version.
* This version corresponds with the version used for the GraalVM maven artifacts.
* @return The GraalVM internal version.
*/
public Version getGraalVersion() {
jperedadnr marked this conversation as resolved.
Show resolved Hide resolved
return graalVersion;
}

/**
* Returns the version string for the static JDK libs.
* If this has not been specified before, the default will be
Expand Down Expand Up @@ -470,16 +484,15 @@ public ReleaseConfiguration getReleaseConfiguration() {
}

public boolean usesJDK11() {
return usesJDK11;
return javaVersion.getMajor() == 11;
}

/**
* Check if the GraalVM provided by the configuration is supported
* @throws IOException if the GraalVM version is older than the minimum supported version
*/
public void checkGraalVMVersion(Version javaVersion) throws IOException {
private void checkGraalVMVersion() throws IOException {
if (isOldGraalVMVersioningScheme(javaVersion)) {
Version graalVersion = getGraalVersion();
if (graalVersion.compareTo(new Version(Constants.GRAALVM_MIN_VERSION)) < 0) {
throw new IOException("Current GraalVM version (" + graalVersion + ") not supported.\n" +
"Please upgrade to " + Constants.GRAALVM_MIN_VERSION + " or higher");
Expand All @@ -503,21 +516,18 @@ public boolean isOldGraalVMVersioningScheme(Version javaVersion) {
* Check if the GraalVM's java provided by the configuration is supported
* @throws IOException if the GraalVM's java version is older than the minimum supported version
*/
public Version checkGraalVMJavaVersion() throws IOException {
Version javaVersion = getGraalVMJavaVersion();
private void checkGraalVMJavaVersion() throws IOException {
if (javaVersion.compareTo(new Version(Constants.GRAALVM_JAVA_MIN_VERSION)) < 0) {
throw new IOException("Current GraalVM's java version (" + javaVersion + ") not supported.\n" +
"Please upgrade to " + Constants.GRAALVM_JAVA_MIN_VERSION + " or higher");
}
usesJDK11 = javaVersion.getMajor() == 11;
return javaVersion;
}

/**
* Check if Gluon is the vendor of the GraalVM build, or else logs a message.
* @throws IOException if the GraalVM path or the GraalVM/release file don't exist
*/
public void checkGraalVMVendor() throws IOException {
private void checkGraalVMVendor() throws IOException {
Path graalPath = getGraalPath();
if (!Files.exists(graalPath)) {
throw new IOException("Path provided for GraalVM doesn't exist: " + graalPath);
Expand Down Expand Up @@ -664,31 +674,30 @@ private void checkGraalVMPermissions(String graalvmHome) {
}

/**
* Gets the Java version that GraalVM bundles
* @return the Java version of the GraalVM's build
* Get the output of running 'java -version' on the GraalVM JDK
* @return The output of executing 'java -version' on the GraalVM JDK
* @throws NullPointerException when the configuration is null
* @throws IllegalArgumentException when the configuration doesn't contain a property graalPath
* @throws IOException if the Java version can't be found
*/
public Version getGraalVMJavaVersion() throws IOException {
ProcessRunner graalJava = null;
private String getJavaVersionString() throws IOException {
try {
graalJava = new ProcessRunner(getGraalVMBinPath().resolve("java").toString(), "-version");
ProcessRunner graalJava = new ProcessRunner(getGraalVMBinPath().resolve("java").toString(), "-version");
if (graalJava.runProcess("check version") != 0) {
throw new IllegalArgumentException("$GRAALVM_HOME/bin/java -version process failed");
}
String response = graalJava.getResponse();
if (response == null || response.isEmpty()) {
throw new IOException("Couldn't determine GraalVM's Java version");
}
return response;
} catch (InterruptedException e) {
throw new IllegalArgumentException("$GRAALVM_HOME/bin/java -version process failed");
}
List<String> responses = graalJava.getResponses();
if (responses == null || responses.isEmpty()) {
throw new IOException("Couldn't determine GraalVM's Java version");
}
return parseGraalVMJavaVersion(responses.get(0));
}

static Version parseGraalVMJavaVersion(String versionString) {
String realVersion = versionString.replaceAll("-internal", "");
String realVersion = versionString.replace("-internal", "");
jperedadnr marked this conversation as resolved.
Show resolved Hide resolved
String pattern = "version \"(\\d{1,2}(\\.\\d+){0,2})\"";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(realVersion);
Expand All @@ -704,6 +713,8 @@ public String toString() {
"graalPath='" + publicConfig.getGraalPath() + '\'' +
", javaStaticSdkVersion='" + getJavaStaticSdkVersion() + '\'' +
", javafxStaticSdkVersion='" + getJavafxStaticSdkVersion() + '\'' +
", javaVersion=" + javaVersion +
", graalVersion=" + graalVersion +
", useJNI=" + useJNI +
", useJavaFX=" + useJavaFX +
", usePrismSW=" + usePrismSW +
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2022, Gluon
* Copyright (c) 2019, 2023, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -35,6 +35,7 @@
import com.gluonhq.substrate.model.Triplet;
import com.gluonhq.substrate.util.FileDeps;
import com.gluonhq.substrate.util.FileOps;
import com.gluonhq.substrate.util.Lib;
import com.gluonhq.substrate.util.Logger;
import com.gluonhq.substrate.util.ProcessRunner;
import com.gluonhq.substrate.util.Strings;
Expand Down Expand Up @@ -74,8 +75,10 @@ public abstract class AbstractTargetConfiguration implements TargetConfiguration
"png", "jpg", "jpeg", "gif", "bmp", "ttf", "raw",
"xml", "fxml", "css", "gls", "json", "dat",
"license", "frag", "vert", "obj", "mtl", "js");
protected static final List<String> ENABLED_FEATURES =
jperedadnr marked this conversation as resolved.
Show resolved Hide resolved
new ArrayList<>(Arrays.asList("org.graalvm.home.HomeFinderFeature"));
/**
* Manual registration of the HomeFinderFeature required until GraalVM for JDK 21.
*/
private static final String HOME_FINDER_FEATURE = "org.graalvm.home.HomeFinderFeature";

private static final List<String> baseNativeImageArguments = Arrays.asList(
"-Djdk.internal.lambda.eagerlyInitialize=false",
Expand All @@ -98,8 +101,12 @@ public abstract class AbstractTargetConfiguration implements TargetConfiguration
protected final boolean crossCompile;

private final List<String> defaultAdditionalSourceFiles = Collections.singletonList("launcher.c");
private final List<String> defaultStaticJavaLibs = List.of("java", "nio", "zip", "net", "prefs", "jvm",
"fdlibm", "z", "dl", "j2pkcs11", "sunec", "jaas", "extnet");
private final List<Lib> defaultStaticJavaLibs = List.of(
Lib.of("java"), Lib.of("nio"), Lib.of("zip"), Lib.of("net"),
Lib.of("prefs"), Lib.of("jvm"), Lib.upTo(20, "fdlibm"), Lib.of("z"),
Lib.of("dl"), Lib.of("j2pkcs11"), Lib.upTo(11, "sunec"), Lib.of("jaas"),
Lib.of("extnet")
);

AbstractTargetConfiguration(ProcessPaths paths, InternalProjectConfiguration configuration) {
this.projectConfiguration = configuration;
Expand All @@ -122,10 +129,10 @@ public abstract class AbstractTargetConfiguration implements TargetConfiguration
public boolean compile() throws IOException, InterruptedException {
String substrateClasspath = "";
try {
substrateClasspath = new File(AbstractTargetConfiguration.class.getProtectionDomain()
substrateClasspath = new File(AbstractTargetConfiguration.class.getProtectionDomain()
.getCodeSource().getLocation().toURI()).getPath();
} catch (URISyntaxException ex) {
throw new IOException ("Can't locate Substrate.jar", ex);
throw new IOException("Can't locate Substrate.jar", ex);
}
String processedClasspath = validateCompileRequirements();

Expand All @@ -139,12 +146,14 @@ public boolean compile() throws IOException, InterruptedException {

baseNativeImageArguments.forEach(compileRunner::addArg);

compileRunner.addArgs(getNativeImageArguments());

if (!projectConfiguration.isSharedLibrary() ||
!projectConfiguration.getTargetTriplet().equals(Triplet.fromCurrentOS())) {
compileRunner.addArg("-H:+ExitAfterRelocatableImageWrite");
}

compileRunner.addArgs(getEnabledFeatures());
compileRunner.addArgs(getEnabledFeaturesArgs());

compileRunner.addArg(createTempDirectoryArg());

Expand Down Expand Up @@ -380,13 +389,8 @@ private String getJniPlatformArg() {

private String getJniPlatform() {
Triplet target = projectConfiguration.getTargetTriplet();
boolean graalVM221 = false;
try {
Version graalVersion = projectConfiguration.getGraalVersion();
graalVM221 = ((graalVersion.getMajor() > 21) && (graalVersion.getMinor() >0));
} catch (IOException ex) {
Logger.logFatal(ex, "Could not detect GraalVM version, stopping now.");
}
Version graalVersion = projectConfiguration.getGraalVersion();
boolean graalVM221 = ((graalVersion.getMajor() > 21) && (graalVersion.getMinor() > 0));
String os = target.getOs();
String arch = target.getArch();
switch (os) {
Expand Down Expand Up @@ -445,7 +449,7 @@ private void ensureClibs() throws IOException {
target.getOsArch2());
}
if (FileOps.isDirectoryEmpty(clibPath)) {
throw new IOException("No clibraries found for the required architecture in "+clibPath);
throw new IOException("No clibraries found for the required architecture in " + clibPath);
}
checkPlatformSpecificClibs(clibPath);
}
Expand Down Expand Up @@ -492,14 +496,27 @@ private String getNativeImagePath() {
.toString();
}

private List<String> getEnabledFeatures() {
return ENABLED_FEATURES.stream()
.map(feature -> "--features=" + feature)
.collect(Collectors.toList());
protected List<String> getNativeImageArguments() {
return List.of();
}

protected List<String> getEnabledFeatures() {
return List.of();
}

private List<String> getEnabledFeaturesArgs() {
List<String> args = new ArrayList<>();
if (projectConfiguration.getJavaVersion().getMajor() < 21) {
args.add("--features=" + HOME_FINDER_FEATURE);
}
for (String feature : getEnabledFeatures()) {
args.add("--features=" + feature);
}
return args;
}

private String createTempDirectoryArg() throws IOException {
Path tmpPath = paths.getTmpPath();
Path tmpPath = paths.getTmpPath();
FileOps.rmdir(tmpPath);
String tmpDir = tmpPath.toFile().getAbsolutePath();
return "-H:TempDirectory=" + tmpDir;
Expand Down Expand Up @@ -564,7 +581,7 @@ private List<String> getInitializeAtBuildTimeList(String suffix, ConfigResolver
private Path createReflectionConfig(String suffix, ConfigResolver configResolver) throws IOException {
Path gvmPath = paths.getGvmPath();
Path reflectionPath = gvmPath.resolve(
Strings.substitute( Constants.REFLECTION_ARCH_FILE, Map.of("archOs", suffix)));
Strings.substitute(Constants.REFLECTION_ARCH_FILE, Map.of("archOs", suffix)));
Files.deleteIfExists(reflectionPath);
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(reflectionPath.toFile())))) {
bw.write("[\n");
Expand Down Expand Up @@ -677,10 +694,10 @@ private static void writeEntry(BufferedWriter bw, String javaClass, boolean excl
writeSingleEntry(bw, javaClass, exclude);
}

private static void writeSingleEntry (BufferedWriter bw, String javaClass, boolean exclude) throws IOException {
private static void writeSingleEntry(BufferedWriter bw, String javaClass, boolean exclude) throws IOException {
bw.write(" {\n");
bw.write(" \"name\" : \"" + javaClass + "\"");
if (! exclude) {
if (!exclude) {
bw.write(",\n");
bw.write(" \"allDeclaredConstructors\" : true,\n");
bw.write(" \"allPublicConstructors\" : true,\n");
Expand Down Expand Up @@ -914,7 +931,7 @@ String getAppPath(String appName) {
* linker when creating images for the specific target.
*/
List<String> getStaticJavaLibs() {
return defaultStaticJavaLibs;
return filterApplicableLibs(defaultStaticJavaLibs);
}

/**
Expand All @@ -929,6 +946,19 @@ List<String> getOtherStaticLibs() {
return List.of();
}

/**
* Return the list of library names applicable to the used java version.
* @param libs List to validate based on {@link Lib#inRange(int)}.
* @return The list of library names applicable to the used java version.
*/
final List<String> filterApplicableLibs(List<Lib> libs) {
int major = projectConfiguration.getJavaVersion().getMajor();
return libs.stream()
.filter(l -> l.inRange(major))
.map(Lib::getLibName)
.collect(Collectors.toList());
}

/**
* Return the arguments that need to be passed to the linker for including
* the Java libraries in the final image.
Expand All @@ -938,7 +968,6 @@ List<String> getOtherStaticLibs() {
*/
private List<String> getTargetSpecificJavaLinkLibraries() {
return Stream.concat(getStaticJavaLibs().stream(), getOtherStaticLibs().stream())
.filter(lib -> projectConfiguration.usesJDK11() || !lib.contains("sunec"))
.map(this::getLinkLibraryOption)
.collect(Collectors.toList());
}
Expand Down
Loading
Loading