jdk-24/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java
2024-10-16 07:23:12 +00:00

203 lines
7.7 KiB
Java

/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package compiler.lib.compile_framework;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.List;
import jdk.test.lib.JDKToolFinder;
/**
* Helper class for compilation of Java and Jasm {@link SourceCode}.
*/
class Compile {
private static final int COMPILE_TIMEOUT = 60;
private static final String JAVA_PATH = JDKToolFinder.getJDKTool("java");
private static final String JAVAC_PATH = JDKToolFinder.getJDKTool("javac");
/**
* Compile all sources in {@code javaSources}. First write them to the {@code sourceDir},
* then compile them to class-files which are stored in {@code classesDir}.
*/
public static void compileJavaSources(List<SourceCode> javaSources, Path sourceDir, Path classesDir) {
if (javaSources.isEmpty()) {
Utils.printlnVerbose("No java sources to compile.");
return;
}
Utils.printlnVerbose("Compiling Java sources: " + javaSources.size());
List<Path> javaFilePaths = writeSourcesToFiles(javaSources, sourceDir);
compileJavaFiles(javaFilePaths, classesDir);
Utils.printlnVerbose("Java sources compiled.");
}
/**
* Compile a list of files (i.e. {@code paths}) using javac and store
* them in {@code classesDir}.
*/
private static void compileJavaFiles(List<Path> paths, Path classesDir) {
List<String> command = new ArrayList<>();
command.add(JAVAC_PATH);
command.add("-classpath");
// Note: the backslashes from windows paths must be escaped!
command.add(Utils.getEscapedClassPathAndClassesDir(classesDir));
command.add("-d");
command.add(classesDir.toString());
for (Path path : paths) {
command.add(path.toAbsolutePath().toString());
}
executeCompileCommand(command);
}
/**
* Compile all sources in {@code jasmSources}. First write them to the {@code sourceDir},
* then compile them to class-files which are stored in {@code classesDir}.
*/
public static void compileJasmSources(List<SourceCode> jasmSources, Path sourceDir, Path classesDir) {
if (jasmSources.isEmpty()) {
Utils.printlnVerbose("No jasm sources to compile.");
return;
}
Utils.printlnVerbose("Compiling jasm sources: " + jasmSources.size());
List<Path> jasmFilePaths = writeSourcesToFiles(jasmSources, sourceDir);
compileJasmFiles(jasmFilePaths, classesDir);
Utils.printlnVerbose("Jasm sources compiled.");
}
/**
* Compile a list of files (i.e. {@code paths}) using asmtools jasm and store
* them in {@code classesDir}.
*/
private static void compileJasmFiles(List<Path> paths, Path classesDir) {
List<String> command = new ArrayList<>();
command.add(JAVA_PATH);
command.add("-classpath");
command.add(getAsmToolsPath());
command.add("org.openjdk.asmtools.jasm.Main");
command.add("-d");
command.add(classesDir.toString());
for (Path path : paths) {
command.add(path.toAbsolutePath().toString());
}
executeCompileCommand(command);
}
/**
* Get the path of asmtools, which is shipped with JTREG.
*/
private static String getAsmToolsPath() {
for (String path : Utils.getClassPaths()) {
if (path.endsWith("jtreg.jar")) {
File jtreg = new File(path);
File dir = jtreg.getAbsoluteFile().getParentFile();
File asmtools = new File(dir, "asmtools.jar");
if (!asmtools.exists()) {
throw new InternalCompileFrameworkException("Found jtreg.jar in classpath, but could not find asmtools.jar");
}
return asmtools.getAbsolutePath();
}
}
throw new InternalCompileFrameworkException("Could not find asmtools because could not find jtreg.jar in classpath");
}
private static void writeCodeToFile(String code, Path path) {
Utils.printlnVerbose("File: " + path);
// Ensure directory of the file exists.
Path dir = path.getParent();
try {
Files.createDirectories(dir);
} catch (Exception e) {
throw new CompileFrameworkException("Could not create directory: " + dir, e);
}
// Write to file.
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
writer.write(code);
} catch (Exception e) {
throw new CompileFrameworkException("Could not write file: " + path, e);
}
}
/**
* Write each source in {@code sources} to a file inside {@code sourceDir}.
*/
private static List<Path> writeSourcesToFiles(List<SourceCode> sources, Path sourceDir) {
List<Path> storedFiles = new ArrayList<>();
for (SourceCode sourceCode : sources) {
Path path = sourceDir.resolve(sourceCode.filePathName());
writeCodeToFile(sourceCode.code(), path);
storedFiles.add(path);
}
return storedFiles;
}
/**
* Execute a given compilation, given as a {@code command}.
*/
private static void executeCompileCommand(List<String> command) {
Utils.printlnVerbose("Compile command: " + String.join(" ", command));
ProcessBuilder builder = new ProcessBuilder(command);
builder.redirectErrorStream(true);
String output;
int exitCode;
try {
Process process = builder.start();
boolean exited = process.waitFor(COMPILE_TIMEOUT, TimeUnit.SECONDS);
if (!exited) {
process.destroyForcibly();
System.out.println("Timeout: compile command: " + String.join(" ", command));
throw new InternalCompileFrameworkException("Process timeout: compilation took too long.");
}
output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
exitCode = process.exitValue();
} catch (IOException e) {
throw new InternalCompileFrameworkException("IOException during compilation", e);
} catch (InterruptedException e) {
throw new CompileFrameworkException("InterruptedException during compilation", e);
}
if (exitCode != 0 || !output.isEmpty()) {
System.err.println("Compilation failed.");
System.err.println("Exit code: " + exitCode);
System.err.println("Output: '" + output + "'");
throw new CompileFrameworkException("Compilation failed.");
}
}
}