blob: eb880f2f6e3e48ee433dc9155311dfbae155e4f3 [file] [log] [blame]
// Copyright (c) 2017, 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 com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.OutputMode;
import com.android.tools.r8.utils.Reporter;
import java.nio.file.Path;
/**
* Base class for commands and command builders for compiler applications/tools which besides an
* Android application (and a main-dex list) also takes compilation output, compilation mode and
* min API level as input.
*/
abstract class BaseCompilerCommand extends BaseCommand {
// TODO(b/70656566): Remove this once the deprecated API is removed.
protected static class OutputOptions {
final Path path;
final OutputMode mode;
public OutputOptions(Path path, OutputMode mode) {
this.path = path;
this.mode = mode;
}
}
private final OutputOptions outputOptions;
private final CompilationMode mode;
private final ProgramConsumer programConsumer;
private final int minApiLevel;
private final Reporter reporter;
private final boolean enableDesugaring;
BaseCompilerCommand(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
programConsumer = null;
mode = null;
minApiLevel = 0;
reporter = new Reporter(new DefaultDiagnosticsHandler());
enableDesugaring = true;
outputOptions = null;
}
BaseCompilerCommand(
AndroidApp app,
CompilationMode mode,
ProgramConsumer programConsumer,
OutputOptions outputOptions,
int minApiLevel,
Reporter reporter,
boolean enableDesugaring) {
super(app);
assert minApiLevel > 0;
assert mode != null;
this.mode = mode;
this.programConsumer = programConsumer;
this.minApiLevel = minApiLevel;
this.reporter = reporter;
this.enableDesugaring = enableDesugaring;
this.outputOptions = outputOptions;
}
public CompilationMode getMode() {
return mode;
}
public int getMinApiLevel() {
return minApiLevel;
}
@Deprecated
public Path getOutputPath() {
if (outputOptions == null) {
throw new CompilationError("Use of deprecated API may not be used with new consumer API");
}
return outputOptions.path;
}
@Deprecated
public OutputMode getOutputMode() {
if (outputOptions == null) {
throw new CompilationError("Use of deprecated API may not be used with new consumer API");
}
return outputOptions.mode;
}
public ProgramConsumer getProgramConsumer() {
return programConsumer;
}
public Reporter getReporter() {
return reporter;
}
public boolean getEnableDesugaring() {
return enableDesugaring;
}
abstract public static class Builder<C extends BaseCompilerCommand, B extends Builder<C, B>>
extends BaseCommand.Builder<C, B> {
private ProgramConsumer programConsumer = null;
private Path outputPath = null;
// TODO(b/70656566): Remove default output mode when deprecated API is removed.
private OutputMode outputMode = OutputMode.Indexed;
private CompilationMode mode;
private int minApiLevel = AndroidApiLevel.getDefault().getLevel();
private boolean enableDesugaring = true;
protected Builder() {
}
// Internal constructor for testing.
Builder(AndroidApp app) {
super(AndroidApp.builder(app));
}
Builder(AndroidApp app, DiagnosticsHandler diagnosticsHandler) {
super(AndroidApp.builder(app), diagnosticsHandler);
}
protected Builder(DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
}
/**
* Get current compilation mode.
*/
public CompilationMode getMode() {
return mode;
}
/**
* Set compilation mode.
*/
public B setMode(CompilationMode mode) {
assert mode != null;
this.mode = mode;
return self();
}
/**
* Get the output path.
*
* @return Current output path, null if no output path-and-mode have been set.
* @see #setOutput(Path, OutputMode)
*/
public Path getOutputPath() {
return outputPath;
}
/**
* Get the output mode.
*
* @return Currently set output mode, null if no output path-and-mode have been set.
* @see #setOutput(Path, OutputMode)
*/
public OutputMode getOutputMode() {
return outputMode;
}
/**
* Get the program consumer.
*
* @return The currently set program consumer, null if no program consumer or output
* path-and-mode is set, e.g., neither {@link #setProgramConsumer} nor
* {@link #setOutput} have been called.
*/
public ProgramConsumer getProgramConsumer() {
return programConsumer;
}
/**
* Set the program consumer.
*
* <p>Setting the program consumer will override any previous set consumer or any previous set
* output path & mode.
*
* @param programConsumer Program consumer to set as current. A null argument will clear the
* program consumer / output.
*/
public B setProgramConsumer(ProgramConsumer programConsumer) {
// Setting an explicit program consumer resets any output-path/mode setup.
outputPath = null;
outputMode = null;
this.programConsumer = programConsumer;
return self();
}
/**
* Set the output path-and-mode.
*
* <p>Setting the output path-and-mode will override any previous set consumer or any previous
* output path-and-mode, and implicitly sets the appropriate program consumer to write the
* output.
*
* @param outputPath Path to write the output to. Must be an archive or and existing directory.
* @param outputMode Mode in which to write the output.
*/
public B setOutput(Path outputPath, OutputMode outputMode) {
assert outputPath != null;
assert outputMode != null;
assert !outputMode.isDeprecated();
this.outputPath = outputPath;
this.outputMode = outputMode;
programConsumer = createProgramOutputConsumer(outputPath, outputMode);
return self();
}
/**
* Set an output path. Must be an existing directory or a zip file.
*
* @see #setOutput
*/
@Deprecated
public B setOutputPath(Path outputPath) {
// Ensure this is not mixed with uses of the new consumer API.
assert programConsumer == null;
this.outputPath = outputPath;
return self();
}
/**
* Set an output mode.
*
* @see #setOutput
*/
@Deprecated
public B setOutputMode(OutputMode outputMode) {
// Ensure this is not mixed with uses of the new consumer API.
assert programConsumer == null;
assert outputMode == null || outputMode.isDeprecated();
assert this.outputMode == null || this.outputMode.isDeprecated();
this.outputMode = outputMode;
return self();
}
private InternalProgramOutputPathConsumer createProgramOutputConsumer(
Path path,
OutputMode mode) {
if (mode.isDexIndexed()) {
return FileUtils.isArchive(path)
? new DexIndexedConsumer.ArchiveConsumer(path)
: new DexIndexedConsumer.DirectoryConsumer(path);
}
if (mode.isDexFilePerClassFile()) {
return FileUtils.isArchive(path)
? new DexFilePerClassFileConsumer.ArchiveConsumer(path)
: new DexFilePerClassFileConsumer.DirectoryConsumer(path);
}
if (mode.isClassFile()) {
return FileUtils.isArchive(path)
? new ClassFileConsumer.ArchiveConsumer(path)
: new ClassFileConsumer.DirectoryConsumer(path);
}
throw new Unreachable("Unexpected output mode: " + mode);
}
/**
* Get the minimum API level (aka SDK version).
*/
public int getMinApiLevel() {
return minApiLevel;
}
/**
* Set the minimum required API level (aka SDK version).
*/
public B setMinApiLevel(int minApiLevel) {
assert minApiLevel > 0;
this.minApiLevel = minApiLevel;
return self();
}
public B setEnableDesugaring(boolean enableDesugaring) {
this.enableDesugaring = enableDesugaring;
return self();
}
public boolean getEnableDesugaring() {
return enableDesugaring;
}
@Override
protected void validate() {
assert mode != null;
FileUtils.validateOutputFile(outputPath, reporter);
super.validate();
}
}
}