Skip to content

Commit 7fffa41

Browse files
Update ParparVM CI to test on multiple JDKs (11, 17, 21, 25) (#4285)
* Update ParparVM CI to test on JDK 11, 17, 21, and 25 This change updates the `parparvm-tests.yml` workflow to run the ParparVM tests on JDK 11, 17, 21, and 25 (Early Access) in addition to the existing JDK 8 run. The tests are run sequentially. For JDK 11+, the execution is limited to the `tests` module (and dependencies) to avoid compilation issues with the `JavaAPI` module, which targets Java 1.5 (unsupported by newer JDKs). JaCoCo is skipped for newer JDKs to avoid version conflicts. The ASM library version in `vm/pom.xml` is updated to 9.7 to ensure compatibility with JDK 21+ environments. The existing JDK 8 run is moved to the end of the sequence to ensure the generated coverage report reflects the primary supported environment. * Update ParparVM tests to run against multiple JDKs and targets This change updates the CI workflow and the test infrastructure to support running integration tests against multiple JDK versions (8, 11, 17, 21, and 25). Changes: - Updated `.github/workflows/parparvm-tests.yml` to install JDKs 8, 11, 17, 21, and 25 and export their paths as environment variables. The main test execution remains on JDK 8. - Created `CompilerHelper` in `vm/tests` to detect available JDKs from environment variables and manage compilation using `ProcessBuilder` instead of the in-process Compiler API. This avoids classpath pollution and supports newer JDKs. - Updated `BytecodeInstructionIntegrationTest` and `CleanTargetIntegrationTest` to use `CompilerHelper` and run parameterized tests across valid permutations of available Compilers and Target Versions (1.5, 1.8, 11, 17, 21, 25). - Added logic to handle `javac` flag changes on JDK 9+ (replacing `-bootclasspath` with `-classpath` or `--patch-module` as appropriate) and to skip invalid permutations. - Updated `vm/pom.xml` to use ASM 9.7 to support newer Java class file versions. * Update ParparVM CI to test cross-compilation on JDKs 8-25 This change updates the CI workflow and test infrastructure to validate that the ParparVM ByteCodeTranslator correctly handles bytecode compiled by JDKs 8, 11, 17, 21, and 25 against a range of target versions (1.5, 1.8, 11, 17, 21, 25). Key changes: - `.github/workflows/parparvm-tests.yml`: Installs JDKs 8, 11, 17, 21, and 25 and exports their paths for use by the test suite. - `vm/pom.xml`: Updated `asm.version` to 9.8 to support Java 25 class files. - `vm/tests/.../CompilerHelper.java`: New helper to manage out-of-process `javac` execution using the detected JDK paths. - `vm/tests/.../BytecodeInstructionIntegrationTest.java` & `CleanTargetIntegrationTest.java`: Refactored to use parameterized tests spanning the matrix of available JDKs and target versions. Added logic to adapt compilation flags (e.g., handling the removal of `-bootclasspath` in JDK 9+). --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent e621e15 commit 7fffa41

File tree

5 files changed

+327
-124
lines changed

5 files changed

+327
-124
lines changed

.github/workflows/parparvm-tests.yml

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,69 @@ jobs:
2222
- name: Check out repository
2323
uses: actions/checkout@v4
2424

25+
- name: Install native build tools
26+
run: |
27+
sudo apt-get update
28+
sudo apt-get install -y clang
29+
30+
# Install JDKs and export their paths
2531
- name: Set up JDK 8
2632
uses: actions/setup-java@v4
2733
with:
2834
distribution: 'temurin'
2935
java-version: '8'
30-
cache: 'maven'
36+
- name: Save JDK 8 Path
37+
run: echo "JDK_8_HOME=$JAVA_HOME" >> $GITHUB_ENV
3138

32-
- name: Install native build tools
33-
run: |
34-
sudo apt-get update
35-
sudo apt-get install -y clang
39+
- name: Set up JDK 11
40+
uses: actions/setup-java@v4
41+
with:
42+
distribution: 'temurin'
43+
java-version: '11'
44+
- name: Save JDK 11 Path
45+
run: echo "JDK_11_HOME=$JAVA_HOME" >> $GITHUB_ENV
46+
47+
- name: Set up JDK 17
48+
uses: actions/setup-java@v4
49+
with:
50+
distribution: 'temurin'
51+
java-version: '17'
52+
- name: Save JDK 17 Path
53+
run: echo "JDK_17_HOME=$JAVA_HOME" >> $GITHUB_ENV
54+
55+
- name: Set up JDK 21
56+
uses: actions/setup-java@v4
57+
with:
58+
distribution: 'temurin'
59+
java-version: '21'
60+
- name: Save JDK 21 Path
61+
run: echo "JDK_21_HOME=$JAVA_HOME" >> $GITHUB_ENV
62+
63+
- name: Set up JDK 25
64+
uses: actions/setup-java@v4
65+
with:
66+
distribution: 'zulu'
67+
java-version: '25'
68+
- name: Save JDK 25 Path
69+
run: echo "JDK_25_HOME=$JAVA_HOME" >> $GITHUB_ENV
70+
71+
# Restore JDK 8 as the main runner
72+
- name: Restore JDK 8
73+
uses: actions/setup-java@v4
74+
with:
75+
distribution: 'temurin'
76+
java-version: '8'
77+
cache: 'maven'
3678

3779
- name: Run ParparVM JVM tests
3880
working-directory: vm
39-
run: mvn -B test
81+
run: mvn -B clean test -pl tests -am
82+
env:
83+
JDK_8_HOME: ${{ env.JDK_8_HOME }}
84+
JDK_11_HOME: ${{ env.JDK_11_HOME }}
85+
JDK_17_HOME: ${{ env.JDK_17_HOME }}
86+
JDK_21_HOME: ${{ env.JDK_21_HOME }}
87+
JDK_25_HOME: ${{ env.JDK_25_HOME }}
4088

4189
- name: Publish ByteCodeTranslator quality previews
4290
if: ${{ always() && github.server_url == 'https://github.com' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) }}

vm/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<maven.compiler.source>1.8</maven.compiler.source>
2020
<maven.compiler.target>1.8</maven.compiler.target>
2121
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
22-
<asm.version>9.2</asm.version>
22+
<asm.version>9.8</asm.version>
2323
<junit.jupiter.version>5.10.2</junit.jupiter.version>
2424
</properties>
2525

vm/tests/src/test/java/com/codename1/tools/translator/BytecodeInstructionIntegrationTest.java

Lines changed: 91 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,20 @@
4141

4242
class BytecodeInstructionIntegrationTest {
4343

44+
static Stream<CompilerHelper.CompilerConfig> provideCompilerConfigs() {
45+
List<CompilerHelper.CompilerConfig> configs = new ArrayList<>();
46+
configs.addAll(CompilerHelper.getAvailableCompilers("1.5"));
47+
configs.addAll(CompilerHelper.getAvailableCompilers("1.8"));
48+
configs.addAll(CompilerHelper.getAvailableCompilers("11"));
49+
configs.addAll(CompilerHelper.getAvailableCompilers("17"));
50+
configs.addAll(CompilerHelper.getAvailableCompilers("21"));
51+
configs.addAll(CompilerHelper.getAvailableCompilers("25"));
52+
return configs.stream();
53+
}
54+
4455
@ParameterizedTest
45-
@ValueSource(strings = {"1.5", "1.8"})
46-
void translatesOptimizedBytecodeToLLVMExecutable(String targetVersion) throws Exception {
47-
if ("1.5".equals(targetVersion) && !isSourceVersionSupported("1.5")) {
48-
return; // Skip on newer JDKs that dropped 1.5 support
49-
}
56+
@org.junit.jupiter.params.provider.MethodSource("provideCompilerConfigs")
57+
void translatesOptimizedBytecodeToLLVMExecutable(CompilerHelper.CompilerConfig config) throws Exception {
5058
Parser.cleanup();
5159

5260
Path sourceDir = Files.createTempDirectory("bytecode-integration-sources");
@@ -61,28 +69,43 @@ void translatesOptimizedBytecodeToLLVMExecutable(String targetVersion) throws Ex
6169
Path nativeReport = sourceDir.resolve("native_report.c");
6270
Files.write(nativeReport, nativeReportSource().getBytes(StandardCharsets.UTF_8));
6371

64-
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
65-
assertNotNull(compiler, "A JDK is required to compile test sources");
66-
67-
// Compile App using JavaAPI as bootclasspath
72+
// Compile App using the specific JDK
6873
List<String> compileArgs = new ArrayList<>();
69-
compileArgs.add("-source");
70-
compileArgs.add(targetVersion);
71-
compileArgs.add("-target");
72-
compileArgs.add(targetVersion);
73-
compileArgs.add("-bootclasspath");
74-
compileArgs.add(javaApiDir.toString());
74+
75+
double targetVer = 1.8;
76+
try { targetVer = Double.parseDouble(config.targetVersion); } catch (NumberFormatException ignored) {}
77+
78+
double jdkVer = 1.8;
79+
try { jdkVer = Double.parseDouble(config.jdkVersion); } catch (NumberFormatException ignored) {}
80+
81+
if (jdkVer >= 9) {
82+
compileArgs.add("-source");
83+
compileArgs.add(config.targetVersion);
84+
compileArgs.add("-target");
85+
compileArgs.add(config.targetVersion);
86+
// On JDK 9+, -bootclasspath is removed.
87+
// --patch-module is not allowed with -target 8.
88+
// We rely on the JDK's own bootstrap classes but include our JavaAPI in classpath
89+
// so that any non-replaced classes are found.
90+
// This means we compile against JDK 9+ API but emit older bytecode.
91+
compileArgs.add("-classpath");
92+
compileArgs.add(javaApiDir.toString());
93+
} else {
94+
compileArgs.add("-source");
95+
compileArgs.add(config.targetVersion);
96+
compileArgs.add("-target");
97+
compileArgs.add(config.targetVersion);
98+
compileArgs.add("-bootclasspath");
99+
compileArgs.add(javaApiDir.toString());
100+
compileArgs.add("-Xlint:-options");
101+
}
102+
75103
compileArgs.add("-d");
76104
compileArgs.add(classesDir.toString());
77105
compileArgs.add(sourceDir.resolve("BytecodeInstructionApp.java").toString());
78106

79-
int compileResult = compiler.run(
80-
null,
81-
null,
82-
null,
83-
compileArgs.toArray(new String[0])
84-
);
85-
assertEquals(0, compileResult, "BytecodeInstructionApp should compile");
107+
int compileResult = CompilerHelper.compile(config.jdkHome, compileArgs);
108+
assertEquals(0, compileResult, "BytecodeInstructionApp should compile with " + config);
86109

87110
Files.copy(nativeReport, classesDir.resolve("native_report.c"));
88111

@@ -190,11 +213,8 @@ private String invokeLdcLocalVarsAppSource() {
190213
}
191214

192215
@ParameterizedTest
193-
@ValueSource(strings = {"1.5", "1.8"})
194-
void translatesInvokeAndLdcBytecodeToLLVMExecutable(String targetVersion) throws Exception {
195-
if ("1.5".equals(targetVersion) && !isSourceVersionSupported("1.5")) {
196-
return; // Skip on newer JDKs that dropped 1.5 support
197-
}
216+
@org.junit.jupiter.params.provider.MethodSource("provideCompilerConfigs")
217+
void translatesInvokeAndLdcBytecodeToLLVMExecutable(CompilerHelper.CompilerConfig config) throws Exception {
198218
Parser.cleanup();
199219

200220
Path sourceDir = Files.createTempDirectory("invoke-ldc-sources");
@@ -209,27 +229,37 @@ void translatesInvokeAndLdcBytecodeToLLVMExecutable(String targetVersion) throws
209229
Path nativeReport = sourceDir.resolve("native_report.c");
210230
Files.write(nativeReport, nativeReportSource().getBytes(StandardCharsets.UTF_8));
211231

212-
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
213-
assertNotNull(compiler, "A JDK is required to compile test sources");
214-
215232
List<String> compileArgs = new ArrayList<>();
216-
compileArgs.add("-source");
217-
compileArgs.add(targetVersion);
218-
compileArgs.add("-target");
219-
compileArgs.add(targetVersion);
220-
compileArgs.add("-bootclasspath");
221-
compileArgs.add(javaApiDir.toString());
233+
234+
double targetVer = 1.8;
235+
try { targetVer = Double.parseDouble(config.targetVersion); } catch (NumberFormatException ignored) {}
236+
237+
double jdkVer = 1.8;
238+
try { jdkVer = Double.parseDouble(config.jdkVersion); } catch (NumberFormatException ignored) {}
239+
240+
if (jdkVer >= 9) {
241+
compileArgs.add("-source");
242+
compileArgs.add(config.targetVersion);
243+
compileArgs.add("-target");
244+
compileArgs.add(config.targetVersion);
245+
compileArgs.add("-classpath");
246+
compileArgs.add(javaApiDir.toString());
247+
} else {
248+
compileArgs.add("-source");
249+
compileArgs.add(config.targetVersion);
250+
compileArgs.add("-target");
251+
compileArgs.add(config.targetVersion);
252+
compileArgs.add("-bootclasspath");
253+
compileArgs.add(javaApiDir.toString());
254+
compileArgs.add("-Xlint:-options");
255+
}
256+
222257
compileArgs.add("-d");
223258
compileArgs.add(classesDir.toString());
224259
compileArgs.add(sourceDir.resolve("InvokeLdcLocalVarsApp.java").toString());
225260

226-
int compileResult = compiler.run(
227-
null,
228-
null,
229-
null,
230-
compileArgs.toArray(new String[0])
231-
);
232-
assertEquals(0, compileResult, "InvokeLdcLocalVarsApp should compile");
261+
int compileResult = CompilerHelper.compile(config.jdkHome, compileArgs);
262+
assertEquals(0, compileResult, "InvokeLdcLocalVarsApp should compile with " + config);
233263

234264
Files.copy(nativeReport, classesDir.resolve("native_report.c"));
235265

@@ -985,18 +1015,15 @@ private String nativeReportSource() {
9851015
}
9861016

9871017
@ParameterizedTest
988-
@ValueSource(strings = {"1.5", "1.8"})
989-
void handleDefaultOutputWritesOutput(String targetVersion) throws Exception {
990-
if ("1.5".equals(targetVersion) && !isSourceVersionSupported("1.5")) {
991-
return; // Skip on newer JDKs that dropped 1.5 support
992-
}
1018+
@org.junit.jupiter.params.provider.MethodSource("provideCompilerConfigs")
1019+
void handleDefaultOutputWritesOutput(CompilerHelper.CompilerConfig config) throws Exception {
9931020
Parser.cleanup();
9941021
resetByteCodeClass();
9951022
Path sourceDir = Files.createTempDirectory("default-output-source");
9961023
Path outputDir = Files.createTempDirectory("default-output-dest");
9971024

9981025
Files.write(sourceDir.resolve("resource.txt"), "data".getBytes(StandardCharsets.UTF_8));
999-
compileDummyMainClass(sourceDir, "com.example", "MyAppDefault", targetVersion);
1026+
compileDummyMainClass(sourceDir, "com.example", "MyAppDefault", config);
10001027

10011028
String[] args = new String[] {
10021029
"csharp",
@@ -1076,18 +1103,15 @@ void copyDirRecursivelyCopies() throws Exception {
10761103
}
10771104

10781105
@ParameterizedTest
1079-
@ValueSource(strings = {"1.5", "1.8"})
1080-
void handleIosOutputGeneratesProjectStructure(String targetVersion) throws Exception {
1081-
if ("1.5".equals(targetVersion) && !isSourceVersionSupported("1.5")) {
1082-
return; // Skip on newer JDKs that dropped 1.5 support
1083-
}
1106+
@org.junit.jupiter.params.provider.MethodSource("provideCompilerConfigs")
1107+
void handleIosOutputGeneratesProjectStructure(CompilerHelper.CompilerConfig config) throws Exception {
10841108
Parser.cleanup();
10851109
resetByteCodeClass();
10861110
Path sourceDir = Files.createTempDirectory("ios-output-source");
10871111
Path outputDir = Files.createTempDirectory("ios-output-dest");
10881112

10891113
Files.write(sourceDir.resolve("resource.txt"), "data".getBytes(StandardCharsets.UTF_8));
1090-
compileDummyMainClass(sourceDir, "com.example", "MyAppIOS", targetVersion);
1114+
compileDummyMainClass(sourceDir, "com.example", "MyAppIOS", config);
10911115

10921116
// Add a bundle to test copyDir invocation in execute loop
10931117
Path bundleDir = sourceDir.resolve("test.bundle");
@@ -1139,7 +1163,7 @@ private void resetByteCodeClass() throws Exception {
11391163
((Set<?>) writableFieldsField.get(null)).clear();
11401164
}
11411165

1142-
private void compileDummyMainClass(Path sourceDir, String packageName, String className, String targetVersion) throws Exception {
1166+
private void compileDummyMainClass(Path sourceDir, String packageName, String className, CompilerHelper.CompilerConfig config) throws Exception {
11431167
Path packageDir = sourceDir;
11441168
for (String part : packageName.split("\\.")) {
11451169
packageDir = packageDir.resolve(part);
@@ -1152,13 +1176,17 @@ private void compileDummyMainClass(Path sourceDir, String packageName, String cl
11521176
"}\n";
11531177
Files.write(javaFile, content.getBytes(StandardCharsets.UTF_8));
11541178

1155-
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
1156-
int result = compiler.run(null, null, null,
1157-
"-source", targetVersion,
1158-
"-target", targetVersion,
1159-
"-d", sourceDir.toString(),
1160-
javaFile.toString());
1161-
assertEquals(0, result, "Compilation failed");
1179+
List<String> args = new ArrayList<>();
1180+
args.add("-source");
1181+
args.add(config.targetVersion);
1182+
args.add("-target");
1183+
args.add(config.targetVersion);
1184+
args.add("-d");
1185+
args.add(sourceDir.toString());
1186+
args.add(javaFile.toString());
1187+
1188+
int result = CompilerHelper.compile(config.jdkHome, args);
1189+
assertEquals(0, result, "Compilation failed with " + config);
11621190
}
11631191

11641192
@Test
@@ -1262,19 +1290,4 @@ void testArithmeticExpressionCoverage() {
12621290
// or mock if possible. But here we can check basic behavior.
12631291
}
12641292

1265-
private boolean isSourceVersionSupported(String version) {
1266-
String javaVersion = System.getProperty("java.specification.version");
1267-
if (javaVersion.startsWith("1.")) {
1268-
return true;
1269-
}
1270-
try {
1271-
int major = Integer.parseInt(javaVersion);
1272-
if ("1.5".equals(version)) {
1273-
if (major >= 9) return false;
1274-
}
1275-
} catch (NumberFormatException e) {
1276-
return true;
1277-
}
1278-
return true;
1279-
}
12801293
}

0 commit comments

Comments
 (0)