blob: b961367098fef23d969dec08f7510ab525eafda5 [file] [log] [blame]
// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
import static com.android.tools.r8.ToolHelper.isWindows;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.ZipUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.rules.TemporaryFolder;
public class JavaCompilerTool {
private final CfRuntime jdk;
private final TestState state;
private String source = null;
private String target = null;
private final List<Path> sources = new ArrayList<>();
private final List<String> classNames = new ArrayList<>();
private final List<Path> classpath = new ArrayList<>();
private final List<String> options = new ArrayList<>();
private final List<Path> annotationProcessorPath = new ArrayList<>();
private final List<String> annotationProcessors = new ArrayList<>();
private Path output = null;
private JavaCompilerTool(CfRuntime jdk, TestState state) {
this.jdk = jdk;
this.state = state;
}
public static JavaCompilerTool create(CfRuntime jdk, TemporaryFolder temp) {
assert temp != null;
return create(jdk, new TestState(temp));
}
public static JavaCompilerTool create(CfRuntime jdk, TestState state) {
assert state != null;
return new JavaCompilerTool(jdk, state);
}
public JavaCompilerTool addOptions(String... options) {
return addOptions(Arrays.asList(options));
}
public JavaCompilerTool addOptions(Collection<String> options) {
this.options.addAll(options);
return this;
}
public JavaCompilerTool setSource(String source) {
this.source = source;
return this;
}
public JavaCompilerTool setTarget(String target) {
this.target = target;
return this;
}
public JavaCompilerTool addSourceFiles(Path... files) {
return addSourceFiles(Arrays.asList(files));
}
public JavaCompilerTool addSourceFiles(Collection<Path> files) {
sources.addAll(files);
return this;
}
public JavaCompilerTool addClassNames(Collection<String> classNames) {
this.classNames.addAll(classNames);
return this;
}
public JavaCompilerTool addClasspathFiles(Path... files) {
return addClasspathFiles(Arrays.asList(files));
}
public JavaCompilerTool addClasspathFiles(Collection<Path> files) {
classpath.addAll(files);
return this;
}
public JavaCompilerTool addAnnotationProcessorPathFiles(Path... files) {
return addAnnotationProcessorPathFiles(Arrays.asList(files));
}
public JavaCompilerTool addAnnotationProcessorPathFiles(Collection<Path> files) {
annotationProcessorPath.addAll(files);
return this;
}
public JavaCompilerTool addAnnotationProcessors(String... processors) {
return addAnnotationProcessor(Arrays.asList(processors));
}
public JavaCompilerTool addAnnotationProcessor(Collection<String> processors) {
annotationProcessors.addAll(processors);
return this;
}
/** Set the output. Must be to an existing directory or to an non-existing jar file. */
public JavaCompilerTool setOutputPath(Path file) {
assert (Files.exists(file) && Files.isDirectory(file))
|| (!Files.exists(file) && FileUtils.isJarFile(file) && Files.exists(file.getParent()));
this.output = file;
return this;
}
private Path getOrCreateOutputPath() throws IOException {
return output != null ? output : state.getNewTempFolder().resolve("out.jar");
}
/** Compile and return the compilations process result object. */
public ProcessResult compileRaw() throws IOException {
assertNotNull("An output path must be specified prior to compilation.", output);
return compileInternal(output);
}
/**
* Compile asserting success and return the path to the output file.
*
* <p>If no output file has been set, the output is compiled to a zip archive in a temporary
* directory.
*/
public Path compile() throws IOException {
Path output = getOrCreateOutputPath();
ProcessResult result = compileInternal(output);
assertEquals(result.toString(), result.exitCode, 0);
return output;
}
// For javac command line options see
// https://docs.oracle.com/en/java/javase/17/docs/specs/man/javac.html.
private ProcessResult compileInternal(Path output) throws IOException {
Path outdir = Files.isDirectory(output) ? output : state.getNewTempFolder();
List<String> cmdline = new ArrayList<>();
cmdline.add(jdk.getJavaExecutable() + "c");
cmdline.addAll(options);
if (!classpath.isEmpty()) {
cmdline.add("-cp");
cmdline.add(
classpath.stream()
.map(Path::toString)
.collect(Collectors.joining(isWindows() ? ";" : ":")));
}
if (!annotationProcessorPath.isEmpty()) {
cmdline.add("-processorpath");
cmdline.add(
annotationProcessorPath.stream()
.map(Path::toString)
.collect(Collectors.joining(isWindows() ? ";" : ":")));
}
if (!annotationProcessors.isEmpty()) {
cmdline.add("-processor");
cmdline.add(String.join(",", annotationProcessors));
}
if (source != null) {
cmdline.add("-source");
cmdline.add(source);
}
if (target != null) {
cmdline.add("-target");
cmdline.add(target);
}
cmdline.add("-d");
cmdline.add(outdir.toString());
for (Path source : sources) {
cmdline.add(source.toString());
}
cmdline.addAll(classNames);
ProcessBuilder builder = new ProcessBuilder(cmdline);
ProcessResult javacResult = ToolHelper.runProcess(builder);
if (FileUtils.isJarFile(output)) {
assert !outdir.equals(output);
ZipUtils.zip(output, outdir);
}
return javacResult;
}
}