blob: 9ffc2e86a23ffbad00b7436c75d5e25fd8447e08 [file] [log] [blame]
// Copyright (c) 2023, 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.BaseCommand.LibraryInputOrigin;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
/**
* Immutable command structure for an invocation of the {@link GlobalSyntheticsGenerator} compiler.
*/
@KeepForApi
public final class GlobalSyntheticsGeneratorCommand {
private final GlobalSyntheticsConsumer globalsConsumer;
private final Reporter reporter;
private final int minApiLevel;
private final boolean classfileDesugaringOnly;
private final boolean printHelp;
private final boolean printVersion;
private final AndroidApp inputApp;
private final DexItemFactory factory = new DexItemFactory();
private GlobalSyntheticsGeneratorCommand(
AndroidApp inputApp,
GlobalSyntheticsConsumer globalsConsumer,
Reporter reporter,
int minApiLevel,
boolean classfileDesugaringOnly) {
this.inputApp = inputApp;
this.globalsConsumer = globalsConsumer;
this.minApiLevel = minApiLevel;
this.classfileDesugaringOnly = classfileDesugaringOnly;
this.reporter = reporter;
this.printHelp = false;
this.printVersion = false;
}
private GlobalSyntheticsGeneratorCommand(boolean printHelp, boolean printVersion) {
this.printHelp = printHelp;
this.printVersion = printVersion;
this.inputApp = null;
this.globalsConsumer = null;
this.minApiLevel = AndroidApiLevel.B.getLevel();
this.classfileDesugaringOnly = false;
reporter = new Reporter();
}
public AndroidApp getInputApp() {
return inputApp;
}
public boolean isPrintHelp() {
return printHelp;
}
public boolean isPrintVersion() {
return printVersion;
}
/**
* Parse the GlobalSyntheticsGenerator command-line.
*
* <p>Parsing will set the supplied options or their default value if they have any.
*
* @param args Command-line arguments array.
* @param origin Origin description of the command-line arguments.
* @return GlobalSyntheticsGenerator command builder with state according to parsed command line.
*/
public static Builder parse(String[] args, Origin origin) {
return GlobalSyntheticsGeneratorCommandParser.parse(args, origin);
}
/**
* Parse the GlobalSyntheticsGenerator command-line.
*
* <p>Parsing will set the supplied options or their default value if they have any.
*
* @param args Command-line arguments array.
* @param origin Origin description of the command-line arguments.
* @param handler Custom defined diagnostics handler.
* @return GlobalSyntheticsGenerator command builder with state according to parsed command line.
*/
public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
return GlobalSyntheticsGeneratorCommandParser.parse(args, origin, handler);
}
protected static class DefaultR8DiagnosticsHandler implements DiagnosticsHandler {
@Override
public void error(Diagnostic error) {
if (error instanceof DexFileOverflowDiagnostic) {
DexFileOverflowDiagnostic overflowDiagnostic = (DexFileOverflowDiagnostic) error;
DiagnosticsHandler.super.error(
new StringDiagnostic(
overflowDiagnostic.getDiagnosticMessage()
+ ". Library too large. GlobalSyntheticsGenerator can only produce a single"
+ " .dex file"));
return;
}
DiagnosticsHandler.super.error(error);
}
}
public static Builder builder() {
return new Builder();
}
public static Builder builder(DiagnosticsHandler diagnosticsHandler) {
return new Builder(diagnosticsHandler);
}
InternalOptions getInternalOptions() {
InternalOptions internal = new InternalOptions(factory, reporter);
assert !internal.debug;
assert !internal.minimalMainDex;
internal.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(minApiLevel));
assert internal.retainCompileTimeAnnotations;
internal.intermediate = true;
internal.programConsumer =
classfileDesugaringOnly ? new ThrowingCfConsumer() : new ThrowingDexConsumer();
internal.setGlobalSyntheticsConsumer(globalsConsumer);
if (classfileDesugaringOnly) {
internal.apiModelingOptions().disableApiCallerIdentification();
internal.apiModelingOptions().disableOutliningAndStubbing();
}
// Assert and fixup defaults.
assert !internal.isShrinking();
assert !internal.isMinifying();
assert !internal.passthroughDexCode;
internal.tool = Tool.GlobalSyntheticsGenerator;
internal.desugarState = DesugarState.ON;
internal.enableVarHandleDesugaring = true;
internal.getArtProfileOptions().setEnableCompletenessCheckForTesting(false);
return internal;
}
private static class ThrowingCfConsumer implements ClassFileConsumer {
@Override
public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
throw new Unreachable("Unexpected attempt to write a non-global artifact");
}
@Override
public void finished(DiagnosticsHandler handler) {
// Nothing to do.
}
}
private static class ThrowingDexConsumer implements DexIndexedConsumer {
@Override
public void accept(
int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
throw new Unreachable("Unexpected attempt to write a non-global artifact");
}
@Override
public void finished(DiagnosticsHandler handler) {
// Nothing to do.
}
}
/**
* Builder for constructing a GlobalSyntheticsGeneratorCommand.
*
* <p>A builder is obtained by calling {@link GlobalSyntheticsGeneratorCommand#builder}.
*/
@KeepForApi
public static class Builder {
private GlobalSyntheticsConsumer globalsConsumer = null;
private final Reporter reporter;
private int minApiLevel = AndroidApiLevel.B.getLevel();
private boolean classfileDesugaringOnly = false;
private boolean printHelp = false;
private boolean printVersion = false;
private final AndroidApp.Builder appBuilder = AndroidApp.builder();
private Builder() {
this(new DefaultR8DiagnosticsHandler());
}
private Builder(DiagnosticsHandler diagnosticsHandler) {
this.reporter = new Reporter(diagnosticsHandler);
}
/** Set the min api level. */
public Builder setMinApiLevel(int minApiLevel) {
this.minApiLevel = minApiLevel;
return this;
}
public Builder setClassfileDesugaringOnly(boolean value) {
this.classfileDesugaringOnly = value;
return this;
}
/** Set the value of the print-help flag. */
public Builder setPrintHelp(boolean printHelp) {
this.printHelp = printHelp;
return this;
}
/** Set the value of the print-version flag. */
public Builder setPrintVersion(boolean printVersion) {
this.printVersion = printVersion;
return this;
}
/** Add library file resources. */
public Builder addLibraryFiles(Path... files) {
addLibraryFiles(Arrays.asList(files));
return this;
}
/** Add library file resources. */
public Builder addLibraryFiles(Collection<Path> files) {
guard(
() -> {
for (Path path : files) {
try {
appBuilder.addLibraryFile(path);
} catch (CompilationError e) {
error(new LibraryInputOrigin(path), e);
}
}
});
return this;
}
/** Set a destination to write the resulting global synthetics output file. */
public Builder setGlobalSyntheticsOutput(Path path) {
return setGlobalSyntheticsConsumer(
new GlobalSyntheticsConsumer() {
private boolean written = false;
@Override
public synchronized void accept(
ByteDataView data, ClassReference context, DiagnosticsHandler handler) {
if (written) {
throw new Unreachable("Unexpected attempt to repeatedly write global synthetics");
}
written = true;
try {
Files.write(path, data.copyByteData());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
/** Set a consumer for obtaining the resulting global synthetics output. */
public Builder setGlobalSyntheticsConsumer(GlobalSyntheticsConsumer globalsConsumer) {
this.globalsConsumer = globalsConsumer;
return this;
}
public GlobalSyntheticsGeneratorCommand build() {
validate();
if (isPrintHelpOrPrintVersion()) {
return new GlobalSyntheticsGeneratorCommand(printHelp, printVersion);
}
return new GlobalSyntheticsGeneratorCommand(
appBuilder.build(), globalsConsumer, reporter, minApiLevel, classfileDesugaringOnly);
}
private boolean isPrintHelpOrPrintVersion() {
return printHelp || printVersion;
}
private void validate() {
if (isPrintHelpOrPrintVersion()) {
return;
}
if (globalsConsumer == null) {
reporter.error("GlobalSyntheticsGenerator does not support compiling without output");
}
}
// Helper to guard and handle exceptions.
private void guard(Runnable action) {
try {
action.run();
} catch (CompilationError e) {
reporter.error(e.toStringDiagnostic());
} catch (AbortException e) {
// Error was reported and exception will be thrown by build.
}
}
/** Signal an error. */
public void error(Diagnostic diagnostic) {
reporter.error(diagnostic);
}
// Helper to signify an error.
public void error(Origin origin, Throwable throwable) {
reporter.error(new ExceptionDiagnostic(throwable, origin));
}
}
}