Populate R8 build metadata
Bug: b/290258417
Fixes: b/361751071
Change-Id: I136a26047ca4c5edd9d56232d80c0417573e6055
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 3c858a9..316020f 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -449,7 +449,7 @@
options.reporter.failIfPendingErrors();
// Supply info to all additional resource consumers.
if (!(programConsumer instanceof ConvertedCfFiles)) {
- supplyAdditionalConsumers(appView);
+ supplyAdditionalConsumers(appView, virtualFiles);
}
} finally {
timing.end();
@@ -651,7 +651,7 @@
}
@SuppressWarnings("DefaultCharset")
- public static void supplyAdditionalConsumers(AppView<?> appView) {
+ public static void supplyAdditionalConsumers(AppView<?> appView, List<VirtualFile> virtualFiles) {
InternalOptions options = appView.options();
Reporter reporter = options.reporter;
appView.getArtProfileCollection().supplyConsumers(appView);
@@ -729,7 +729,7 @@
if (options.buildMetadataConsumer != null) {
assert appView.hasClassHierarchy();
options.buildMetadataConsumer.accept(
- BuildMetadataFactory.create(appView.withClassHierarchy()));
+ BuildMetadataFactory.create(appView.withClassHierarchy(), virtualFiles));
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index dc35e67..b58266c 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -80,6 +80,7 @@
private final DexString primaryClassDescriptor;
private final DexString primaryClassSynthesizingContextDescriptor;
private DebugRepresentation debugRepresentation;
+ private boolean startup = false;
VirtualFile(int id, AppView<?> appView) {
this(id, appView, null, null, StartupProfile.empty());
@@ -169,6 +170,14 @@
return debugRepresentation;
}
+ public void setStartup() {
+ startup = true;
+ }
+
+ public boolean isStartup() {
+ return startup;
+ }
+
public static String deriveCommonPrefixAndSanityCheck(List<String> fileNames) {
Iterator<String> nameIterator = fileNames.iterator();
String first = nameIterator.next();
@@ -1466,6 +1475,7 @@
boolean isSingleStartupDexFile = hasSpaceForTransaction(virtualFile, options);
if (isSingleStartupDexFile) {
virtualFile.commitTransaction();
+ virtualFile.setStartup();
} else {
virtualFile.abortTransaction();
@@ -1473,6 +1483,7 @@
MultiStartupDexDistributor distributor =
MultiStartupDexDistributor.get(options, startupProfile);
distributor.distribute(classPartioning.getStartupClasses(), this, virtualFile, cycler);
+ cycler.filesForDistribution.forEach(VirtualFile::setStartup);
options.reporter.warning(
createStartupClassesOverflowDiagnostic(cycler.filesForDistribution.size()));
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index b5b5299..20de02a 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -62,6 +62,7 @@
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
@@ -186,7 +187,7 @@
}
globalsConsumer.finished(appView);
}
- ApplicationWriter.supplyAdditionalConsumers(appView);
+ ApplicationWriter.supplyAdditionalConsumers(appView, Collections.emptyList());
}
private void writeClassCatchingErrors(
diff --git a/src/main/java/com/android/tools/r8/metadata/BuildMetadataFactory.java b/src/main/java/com/android/tools/r8/metadata/BuildMetadataFactory.java
index be06792..486abda 100644
--- a/src/main/java/com/android/tools/r8/metadata/BuildMetadataFactory.java
+++ b/src/main/java/com/android/tools/r8/metadata/BuildMetadataFactory.java
@@ -4,13 +4,24 @@
package com.android.tools.r8.metadata;
import com.android.tools.r8.Version;
+import com.android.tools.r8.dex.VirtualFile;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
public class BuildMetadataFactory {
- @SuppressWarnings("UnusedVariable")
- public static R8BuildMetadata create(AppView<? extends AppInfoWithClassHierarchy> appView) {
- return R8BuildMetadataImpl.builder().setVersion(Version.LABEL).build();
+ public static R8BuildMetadata create(
+ AppView<? extends AppInfoWithClassHierarchy> appView, List<VirtualFile> virtualFiles) {
+ InternalOptions options = appView.options();
+ return R8BuildMetadataImpl.builder()
+ .setOptions(new R8OptionsImpl(options))
+ .setBaselineProfileRewritingOptions(R8BaselineProfileRewritingOptionsImpl.create(options))
+ .setResourceOptimizationOptions(R8ResourceOptimizationOptionsImpl.create(options))
+ .setStartupOptimizationOptions(
+ R8StartupOptimizationOptionsImpl.create(options, virtualFiles))
+ .setVersion(Version.LABEL)
+ .build();
}
}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8BaselineProfileRewritingOptions.java b/src/main/java/com/android/tools/r8/metadata/R8BaselineProfileRewritingOptions.java
new file mode 100644
index 0000000..7598aee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8BaselineProfileRewritingOptions.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2024, 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.metadata;
+
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+
+@KeepForApi
+public interface R8BaselineProfileRewritingOptions {}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8BaselineProfileRewritingOptionsImpl.java b/src/main/java/com/android/tools/r8/metadata/R8BaselineProfileRewritingOptionsImpl.java
new file mode 100644
index 0000000..1585469
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8BaselineProfileRewritingOptionsImpl.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2024, 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.metadata;
+
+import com.android.tools.r8.utils.InternalOptions;
+
+public class R8BaselineProfileRewritingOptionsImpl implements R8BaselineProfileRewritingOptions {
+
+ private R8BaselineProfileRewritingOptionsImpl() {}
+
+ public static R8BaselineProfileRewritingOptionsImpl create(InternalOptions options) {
+ if (options.getArtProfileOptions().getArtProfilesForRewriting().isEmpty()) {
+ return null;
+ }
+ return new R8BaselineProfileRewritingOptionsImpl();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java b/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
index e186575..c5b0dd6 100644
--- a/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
+++ b/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
@@ -4,15 +4,52 @@
package com.android.tools.r8.metadata;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
-import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializer;
@KeepForApi
public interface R8BuildMetadata {
static R8BuildMetadata fromJson(String json) {
- return new Gson().fromJson(json, R8BuildMetadataImpl.class);
+ return new GsonBuilder()
+ .excludeFieldsWithoutExposeAnnotation()
+ .registerTypeAdapter(R8Options.class, deserializeTo(R8OptionsImpl.class))
+ .registerTypeAdapter(
+ R8BaselineProfileRewritingOptions.class,
+ deserializeTo(R8BaselineProfileRewritingOptionsImpl.class))
+ .registerTypeAdapter(
+ R8KeepAttributesOptions.class, deserializeTo(R8KeepAttributesOptionsImpl.class))
+ .registerTypeAdapter(
+ R8ResourceOptimizationOptions.class,
+ deserializeTo(R8ResourceOptimizationOptionsImpl.class))
+ .registerTypeAdapter(
+ R8StartupOptimizationOptions.class,
+ deserializeTo(R8StartupOptimizationOptionsImpl.class))
+ .create()
+ .fromJson(json, R8BuildMetadataImpl.class);
}
+ private static <T> JsonDeserializer<T> deserializeTo(Class<T> implClass) {
+ return (element, type, context) -> context.deserialize(element, implClass);
+ }
+
+ R8Options getOptions();
+
+ /**
+ * @return null if baseline profile rewriting is disabled.
+ */
+ R8BaselineProfileRewritingOptions getBaselineProfileRewritingOptions();
+
+ /**
+ * @return null if resource optimization is disabled.
+ */
+ R8ResourceOptimizationOptions getResourceOptimizationOptions();
+
+ /**
+ * @return null if startup optimization is disabled.
+ */
+ R8StartupOptimizationOptions getStartupOptizationOptions();
+
String getVersion();
String toJson();
diff --git a/src/main/java/com/android/tools/r8/metadata/R8BuildMetadataImpl.java b/src/main/java/com/android/tools/r8/metadata/R8BuildMetadataImpl.java
index 1032696..d59db11 100644
--- a/src/main/java/com/android/tools/r8/metadata/R8BuildMetadataImpl.java
+++ b/src/main/java/com/android/tools/r8/metadata/R8BuildMetadataImpl.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
import com.android.tools.r8.keepanno.annotations.UsedByReflection;
import com.google.gson.Gson;
+import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
@UsedByReflection(
@@ -20,10 +21,36 @@
fieldAnnotatedByClassConstant = SerializedName.class)
public class R8BuildMetadataImpl implements R8BuildMetadata {
+ @Expose
+ @SerializedName("options")
+ private final R8Options options;
+
+ @Expose
+ @SerializedName("baselineProfileRewritingOptions")
+ private final R8BaselineProfileRewritingOptions baselineProfileRewritingOptions;
+
+ @Expose
+ @SerializedName("resourceOptimizationOptions")
+ private final R8ResourceOptimizationOptions resourceOptimizationOptions;
+
+ @Expose
+ @SerializedName("startupOptimizationOptions")
+ private final R8StartupOptimizationOptions startupOptimizationOptions;
+
+ @Expose
@SerializedName("version")
private final String version;
- public R8BuildMetadataImpl(String version) {
+ public R8BuildMetadataImpl(
+ R8Options options,
+ R8BaselineProfileRewritingOptions baselineProfileRewritingOptions,
+ R8ResourceOptimizationOptions resourceOptimizationOptions,
+ R8StartupOptimizationOptions startupOptimizationOptions,
+ String version) {
+ this.options = options;
+ this.baselineProfileRewritingOptions = baselineProfileRewritingOptions;
+ this.resourceOptimizationOptions = resourceOptimizationOptions;
+ this.startupOptimizationOptions = startupOptimizationOptions;
this.version = version;
}
@@ -32,6 +59,26 @@
}
@Override
+ public R8Options getOptions() {
+ return options;
+ }
+
+ @Override
+ public R8BaselineProfileRewritingOptions getBaselineProfileRewritingOptions() {
+ return baselineProfileRewritingOptions;
+ }
+
+ @Override
+ public R8ResourceOptimizationOptions getResourceOptimizationOptions() {
+ return resourceOptimizationOptions;
+ }
+
+ @Override
+ public R8StartupOptimizationOptions getStartupOptizationOptions() {
+ return startupOptimizationOptions;
+ }
+
+ @Override
public String getVersion() {
return version;
}
@@ -43,15 +90,47 @@
public static class Builder {
+ private R8Options options;
+ private R8BaselineProfileRewritingOptions baselineProfileRewritingOptions;
+ private R8ResourceOptimizationOptions resourceOptimizationOptions;
+ private R8StartupOptimizationOptions startupOptimizationOptions;
private String version;
+ public Builder setOptions(R8Options options) {
+ this.options = options;
+ return this;
+ }
+
+ public Builder setBaselineProfileRewritingOptions(
+ R8BaselineProfileRewritingOptions baselineProfileRewritingOptions) {
+ this.baselineProfileRewritingOptions = baselineProfileRewritingOptions;
+ return this;
+ }
+
+ public Builder setResourceOptimizationOptions(
+ R8ResourceOptimizationOptions resourceOptimizationOptions) {
+ this.resourceOptimizationOptions = resourceOptimizationOptions;
+ return this;
+ }
+
+ public Builder setStartupOptimizationOptions(
+ R8StartupOptimizationOptions startupOptimizationOptions) {
+ this.startupOptimizationOptions = startupOptimizationOptions;
+ return this;
+ }
+
public Builder setVersion(String version) {
this.version = version;
return this;
}
public R8BuildMetadataImpl build() {
- return new R8BuildMetadataImpl(version);
+ return new R8BuildMetadataImpl(
+ options,
+ baselineProfileRewritingOptions,
+ resourceOptimizationOptions,
+ startupOptimizationOptions,
+ version);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8KeepAttributesOptions.java b/src/main/java/com/android/tools/r8/metadata/R8KeepAttributesOptions.java
new file mode 100644
index 0000000..b590c7e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8KeepAttributesOptions.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2024, 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.metadata;
+
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+
+@KeepForApi
+public interface R8KeepAttributesOptions {
+
+ boolean isAnnotationDefaultKept();
+
+ boolean isEnclosingMethodKept();
+
+ boolean isExceptionsKept();
+
+ boolean isInnerClassesKept();
+
+ boolean isLocalVariableTableKept();
+
+ boolean isLocalVariableTypeTableKept();
+
+ boolean isMethodParametersKept();
+
+ boolean isPermittedSubclassesKept();
+
+ boolean isRuntimeInvisibleAnnotationsKept();
+
+ boolean isRuntimeInvisibleParameterAnnotationsKept();
+
+ boolean isRuntimeInvisibleTypeAnnotationsKept();
+
+ boolean isRuntimeVisibleAnnotationsKept();
+
+ boolean isRuntimeVisibleParameterAnnotationsKept();
+
+ boolean isRuntimeVisibleTypeAnnotationsKept();
+
+ boolean isSignatureKept();
+
+ boolean isSourceDebugExtensionKept();
+
+ boolean isSourceDirKept();
+
+ boolean isSourceFileKept();
+
+ boolean isStackMapTableKept();
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8KeepAttributesOptionsImpl.java b/src/main/java/com/android/tools/r8/metadata/R8KeepAttributesOptionsImpl.java
new file mode 100644
index 0000000..3e12905
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8KeepAttributesOptionsImpl.java
@@ -0,0 +1,218 @@
+// Copyright (c) 2024, 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.metadata;
+
+import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
+import com.android.tools.r8.keepanno.annotations.FieldAccessFlags;
+import com.android.tools.r8.keepanno.annotations.KeepConstraint;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+@UsedByReflection(
+ description = "Keep and preserve @SerializedName for correct (de)serialization",
+ constraints = {KeepConstraint.LOOKUP},
+ constrainAnnotations = @AnnotationPattern(constant = SerializedName.class),
+ kind = KeepItemKind.CLASS_AND_FIELDS,
+ fieldAccess = {FieldAccessFlags.PRIVATE},
+ fieldAnnotatedByClassConstant = SerializedName.class)
+public class R8KeepAttributesOptionsImpl implements R8KeepAttributesOptions {
+
+ @Expose
+ @SerializedName("isAnnotationDefaultKept")
+ private final boolean isAnnotationDefaultKept;
+
+ @Expose
+ @SerializedName("isEnclosingMethodKept")
+ private final boolean isEnclosingMethodKept;
+
+ @Expose
+ @SerializedName("isExceptionsKept")
+ private final boolean isExceptionsKept;
+
+ @Expose
+ @SerializedName("isInnerClassesKept")
+ private final boolean isInnerClassesKept;
+
+ @Expose
+ @SerializedName("isLocalVariableTableKept")
+ private final boolean isLocalVariableTableKept;
+
+ @Expose
+ @SerializedName("isLocalVariableTypeTableKept")
+ private final boolean isLocalVariableTypeTableKept;
+
+ @Expose
+ @SerializedName("isMethodParametersKept")
+ private final boolean isMethodParametersKept;
+
+ @Expose
+ @SerializedName("isPermittedSubclassesKept")
+ private final boolean isPermittedSubclassesKept;
+
+ @Expose
+ @SerializedName("isRuntimeInvisibleAnnotationsKept")
+ private final boolean isRuntimeInvisibleAnnotationsKept;
+
+ @Expose
+ @SerializedName("isRuntimeInvisibleParameterAnnotationsKept")
+ private final boolean isRuntimeInvisibleParameterAnnotationsKept;
+
+ @Expose
+ @SerializedName("isRuntimeInvisibleTypeAnnotationsKept")
+ private final boolean isRuntimeInvisibleTypeAnnotationsKept;
+
+ @Expose
+ @SerializedName("isRuntimeVisibleAnnotationsKept")
+ private final boolean isRuntimeVisibleAnnotationsKept;
+
+ @Expose
+ @SerializedName("isRuntimeVisibleParameterAnnotationsKept")
+ private final boolean isRuntimeVisibleParameterAnnotationsKept;
+
+ @Expose
+ @SerializedName("isRuntimeVisibleTypeAnnotationsKept")
+ private final boolean isRuntimeVisibleTypeAnnotationsKept;
+
+ @Expose
+ @SerializedName("isSignatureKept")
+ private final boolean isSignatureKept;
+
+ @Expose
+ @SerializedName("isSourceDebugExtensionKept")
+ private final boolean isSourceDebugExtensionKept;
+
+ @Expose
+ @SerializedName("isSourceDirKept")
+ private final boolean isSourceDirKept;
+
+ @Expose
+ @SerializedName("isSourceFileKept")
+ private final boolean isSourceFileKept;
+
+ @Expose
+ @SerializedName("isStackMapTableKept")
+ private final boolean isStackMapTableKept;
+
+ public R8KeepAttributesOptionsImpl(ProguardKeepAttributes keepAttributes) {
+ this.isAnnotationDefaultKept = keepAttributes.annotationDefault;
+ this.isEnclosingMethodKept = keepAttributes.enclosingMethod;
+ this.isExceptionsKept = keepAttributes.exceptions;
+ this.isInnerClassesKept = keepAttributes.innerClasses;
+ this.isLocalVariableTableKept = keepAttributes.localVariableTable;
+ this.isLocalVariableTypeTableKept = keepAttributes.localVariableTypeTable;
+ this.isMethodParametersKept = keepAttributes.methodParameters;
+ this.isPermittedSubclassesKept = keepAttributes.permittedSubclasses;
+ this.isRuntimeInvisibleAnnotationsKept = keepAttributes.runtimeInvisibleAnnotations;
+ this.isRuntimeInvisibleParameterAnnotationsKept =
+ keepAttributes.runtimeInvisibleParameterAnnotations;
+ this.isRuntimeInvisibleTypeAnnotationsKept = keepAttributes.runtimeInvisibleTypeAnnotations;
+ this.isRuntimeVisibleAnnotationsKept = keepAttributes.runtimeVisibleAnnotations;
+ this.isRuntimeVisibleParameterAnnotationsKept =
+ keepAttributes.runtimeVisibleParameterAnnotations;
+ this.isRuntimeVisibleTypeAnnotationsKept = keepAttributes.runtimeVisibleTypeAnnotations;
+ this.isSignatureKept = keepAttributes.signature;
+ this.isSourceDebugExtensionKept = keepAttributes.sourceDebugExtension;
+ this.isSourceDirKept = keepAttributes.sourceDir;
+ this.isSourceFileKept = keepAttributes.sourceFile;
+ this.isStackMapTableKept = keepAttributes.stackMapTable;
+ }
+
+ @Override
+ public boolean isAnnotationDefaultKept() {
+ return isAnnotationDefaultKept;
+ }
+
+ @Override
+ public boolean isEnclosingMethodKept() {
+ return isEnclosingMethodKept;
+ }
+
+ @Override
+ public boolean isExceptionsKept() {
+ return isExceptionsKept;
+ }
+
+ @Override
+ public boolean isInnerClassesKept() {
+ return isInnerClassesKept;
+ }
+
+ @Override
+ public boolean isLocalVariableTableKept() {
+ return isLocalVariableTableKept;
+ }
+
+ @Override
+ public boolean isLocalVariableTypeTableKept() {
+ return isLocalVariableTypeTableKept;
+ }
+
+ @Override
+ public boolean isMethodParametersKept() {
+ return isMethodParametersKept;
+ }
+
+ @Override
+ public boolean isPermittedSubclassesKept() {
+ return isPermittedSubclassesKept;
+ }
+
+ @Override
+ public boolean isRuntimeInvisibleAnnotationsKept() {
+ return isRuntimeInvisibleAnnotationsKept;
+ }
+
+ @Override
+ public boolean isRuntimeInvisibleParameterAnnotationsKept() {
+ return isRuntimeInvisibleParameterAnnotationsKept;
+ }
+
+ @Override
+ public boolean isRuntimeInvisibleTypeAnnotationsKept() {
+ return isRuntimeInvisibleTypeAnnotationsKept;
+ }
+
+ @Override
+ public boolean isRuntimeVisibleAnnotationsKept() {
+ return isRuntimeVisibleAnnotationsKept;
+ }
+
+ @Override
+ public boolean isRuntimeVisibleParameterAnnotationsKept() {
+ return isRuntimeVisibleParameterAnnotationsKept;
+ }
+
+ @Override
+ public boolean isRuntimeVisibleTypeAnnotationsKept() {
+ return isRuntimeVisibleTypeAnnotationsKept;
+ }
+
+ @Override
+ public boolean isSignatureKept() {
+ return isSignatureKept;
+ }
+
+ @Override
+ public boolean isSourceDebugExtensionKept() {
+ return isSourceDebugExtensionKept;
+ }
+
+ @Override
+ public boolean isSourceDirKept() {
+ return isSourceDirKept;
+ }
+
+ @Override
+ public boolean isSourceFileKept() {
+ return isSourceFileKept;
+ }
+
+ @Override
+ public boolean isStackMapTableKept() {
+ return isStackMapTableKept;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8Options.java b/src/main/java/com/android/tools/r8/metadata/R8Options.java
new file mode 100644
index 0000000..42fe2fc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8Options.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2024, 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.metadata;
+
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+
+@KeepForApi
+public interface R8Options {
+
+ /**
+ * @return null if no ProGuard configuration is provided.
+ */
+ R8KeepAttributesOptions getKeepAttributesOptions();
+
+ int getMinApiLevel();
+
+ boolean isAccessModificationEnabled();
+
+ boolean isDebugModeEnabled();
+
+ boolean isProGuardCompatibilityModeEnabled();
+
+ boolean isObfuscationEnabled();
+
+ boolean isOptimizationsEnabled();
+
+ boolean isShrinkingEnabled();
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8OptionsImpl.java b/src/main/java/com/android/tools/r8/metadata/R8OptionsImpl.java
new file mode 100644
index 0000000..a65dfc7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8OptionsImpl.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2024, 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.metadata;
+
+import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
+import com.android.tools.r8.keepanno.annotations.FieldAccessFlags;
+import com.android.tools.r8.keepanno.annotations.KeepConstraint;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+@UsedByReflection(
+ description = "Keep and preserve @SerializedName for correct (de)serialization",
+ constraints = {KeepConstraint.LOOKUP},
+ constrainAnnotations = @AnnotationPattern(constant = SerializedName.class),
+ kind = KeepItemKind.CLASS_AND_FIELDS,
+ fieldAccess = {FieldAccessFlags.PRIVATE},
+ fieldAnnotatedByClassConstant = SerializedName.class)
+public class R8OptionsImpl implements R8Options {
+
+ @Expose
+ @SerializedName("keepAttributesOptions")
+ private final R8KeepAttributesOptions keepAttributesOptions;
+
+ @Expose
+ @SerializedName("minApiLevel")
+ private final int minApiLevel;
+
+ @Expose
+ @SerializedName("isAccessModificationEnabled")
+ private final boolean isAccessModificationEnabled;
+
+ @Expose
+ @SerializedName("isDebugModeEnabled")
+ private final boolean isDebugModeEnabled;
+
+ @Expose
+ @SerializedName("isProGuardCompatibilityModeEnabled")
+ private final boolean isProGuardCompatibilityModeEnabled;
+
+ @Expose
+ @SerializedName("isObfuscationEnabled")
+ private final boolean isObfuscationEnabled;
+
+ @Expose
+ @SerializedName("isOptimizationsEnabled")
+ private final boolean isOptimizationsEnabled;
+
+ @Expose
+ @SerializedName("isShrinkingEnabled")
+ private final boolean isShrinkingEnabled;
+
+ public R8OptionsImpl(InternalOptions options) {
+ this.keepAttributesOptions =
+ options.hasProguardConfiguration()
+ ? new R8KeepAttributesOptionsImpl(
+ options.getProguardConfiguration().getKeepAttributes())
+ : null;
+ this.minApiLevel = options.getMinApiLevel().getLevel();
+ this.isAccessModificationEnabled = options.isAccessModificationEnabled();
+ this.isDebugModeEnabled = options.debug;
+ this.isProGuardCompatibilityModeEnabled = options.forceProguardCompatibility;
+ this.isObfuscationEnabled = options.isMinifying();
+ this.isOptimizationsEnabled = options.isOptimizing();
+ this.isShrinkingEnabled = options.isShrinking();
+ }
+
+ @Override
+ public R8KeepAttributesOptions getKeepAttributesOptions() {
+ return keepAttributesOptions;
+ }
+
+ @Override
+ public int getMinApiLevel() {
+ return minApiLevel;
+ }
+
+ @Override
+ public boolean isAccessModificationEnabled() {
+ return isAccessModificationEnabled;
+ }
+
+ @Override
+ public boolean isDebugModeEnabled() {
+ return isDebugModeEnabled;
+ }
+
+ @Override
+ public boolean isProGuardCompatibilityModeEnabled() {
+ return isProGuardCompatibilityModeEnabled;
+ }
+
+ @Override
+ public boolean isObfuscationEnabled() {
+ return isObfuscationEnabled;
+ }
+
+ @Override
+ public boolean isOptimizationsEnabled() {
+ return isOptimizationsEnabled;
+ }
+
+ @Override
+ public boolean isShrinkingEnabled() {
+ return isShrinkingEnabled;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8ResourceOptimizationOptions.java b/src/main/java/com/android/tools/r8/metadata/R8ResourceOptimizationOptions.java
new file mode 100644
index 0000000..693286e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8ResourceOptimizationOptions.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2024, 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.metadata;
+
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+
+@KeepForApi
+public interface R8ResourceOptimizationOptions {
+
+ boolean isOptimizedShrinkingEnabled();
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8ResourceOptimizationOptionsImpl.java b/src/main/java/com/android/tools/r8/metadata/R8ResourceOptimizationOptionsImpl.java
new file mode 100644
index 0000000..c9cc341
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8ResourceOptimizationOptionsImpl.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2024, 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.metadata;
+
+import com.android.tools.r8.ResourceShrinkerConfiguration;
+import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
+import com.android.tools.r8.keepanno.annotations.FieldAccessFlags;
+import com.android.tools.r8.keepanno.annotations.KeepConstraint;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+@UsedByReflection(
+ description = "Keep and preserve @SerializedName for correct (de)serialization",
+ constraints = {KeepConstraint.LOOKUP},
+ constrainAnnotations = @AnnotationPattern(constant = SerializedName.class),
+ kind = KeepItemKind.CLASS_AND_FIELDS,
+ fieldAccess = {FieldAccessFlags.PRIVATE},
+ fieldAnnotatedByClassConstant = SerializedName.class)
+public class R8ResourceOptimizationOptionsImpl implements R8ResourceOptimizationOptions {
+
+ @Expose
+ @SerializedName("isOptimizedShrinkingEnabled")
+ private final boolean isOptimizedShrinkingEnabled;
+
+ private R8ResourceOptimizationOptionsImpl(
+ ResourceShrinkerConfiguration resourceShrinkerConfiguration) {
+ this.isOptimizedShrinkingEnabled = resourceShrinkerConfiguration.isOptimizedShrinking();
+ }
+
+ public static R8ResourceOptimizationOptionsImpl create(InternalOptions options) {
+ if (options.androidResourceProvider == null) {
+ return null;
+ }
+ return new R8ResourceOptimizationOptionsImpl(options.resourceShrinkerConfiguration);
+ }
+
+ @Override
+ public boolean isOptimizedShrinkingEnabled() {
+ return isOptimizedShrinkingEnabled;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8StartupOptimizationOptions.java b/src/main/java/com/android/tools/r8/metadata/R8StartupOptimizationOptions.java
new file mode 100644
index 0000000..a57b01e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8StartupOptimizationOptions.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2024, 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.metadata;
+
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+
+@KeepForApi
+public interface R8StartupOptimizationOptions {
+
+ int getNumberOfStartupDexFiles();
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8StartupOptimizationOptionsImpl.java b/src/main/java/com/android/tools/r8/metadata/R8StartupOptimizationOptionsImpl.java
new file mode 100644
index 0000000..07b0a0f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8StartupOptimizationOptionsImpl.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2024, 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.metadata;
+
+import com.android.tools.r8.dex.VirtualFile;
+import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
+import com.android.tools.r8.keepanno.annotations.FieldAccessFlags;
+import com.android.tools.r8.keepanno.annotations.KeepConstraint;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+
+@UsedByReflection(
+ description = "Keep and preserve @SerializedName for correct (de)serialization",
+ constraints = {KeepConstraint.LOOKUP},
+ constrainAnnotations = @AnnotationPattern(constant = SerializedName.class),
+ kind = KeepItemKind.CLASS_AND_FIELDS,
+ fieldAccess = {FieldAccessFlags.PRIVATE},
+ fieldAnnotatedByClassConstant = SerializedName.class)
+public class R8StartupOptimizationOptionsImpl implements R8StartupOptimizationOptions {
+
+ @Expose
+ @SerializedName("numberOfStartupDexFiles")
+ private final int numberOfStartupDexFiles;
+
+ public R8StartupOptimizationOptionsImpl(List<VirtualFile> virtualFiles) {
+ this.numberOfStartupDexFiles =
+ (int) virtualFiles.stream().filter(VirtualFile::isStartup).count();
+ }
+
+ public static R8StartupOptimizationOptionsImpl create(
+ InternalOptions options, List<VirtualFile> virtualFiles) {
+ if (options.getStartupOptions().getStartupProfileProviders().isEmpty()) {
+ return null;
+ }
+ return new R8StartupOptimizationOptionsImpl(virtualFiles);
+ }
+
+ @Override
+ public int getNumberOfStartupDexFiles() {
+ return numberOfStartupDexFiles;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
index 445e0b0..ce5e106 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
@@ -56,15 +56,7 @@
public boolean stackMapTable = false;
public boolean permittedSubclasses = false;
- private ProguardKeepAttributes() {
- }
-
- public static ProguardKeepAttributes filterOnlySignatures() {
- ProguardKeepAttributes result = new ProguardKeepAttributes();
- result.applyPatterns(KEEP_ALL);
- result.signature = false;
- return result;
- }
+ private ProguardKeepAttributes() {}
/**
* Implements ProGuards attribute matching rules.
diff --git a/src/test/java/com/android/tools/r8/metadata/BuildMetadataTest.java b/src/test/java/com/android/tools/r8/metadata/BuildMetadataTest.java
index 0f90b82..cec355d 100644
--- a/src/test/java/com/android/tools/r8/metadata/BuildMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/metadata/BuildMetadataTest.java
@@ -3,12 +3,29 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.metadata;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.Version;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.profile.ExternalStartupClass;
+import com.android.tools.r8.startup.profile.ExternalStartupItem;
+import com.android.tools.r8.startup.utils.StartupTestingUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -28,19 +45,63 @@
@Test
public void test() throws Exception {
+ ClassReference mainReference = Reference.classFromClass(Main.class);
+ List<ExternalStartupItem> startupProfile =
+ ImmutableList.of(ExternalStartupClass.builder().setClassReference(mainReference).build());
R8BuildMetadata buildMetadata =
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(
+ ExternalArtProfile.builder().addClassRule(mainReference).build())
+ .apply(StartupTestingUtils.addStartupProfile(startupProfile))
+ .applyIf(
+ parameters.isDexRuntime(),
+ testBuilder ->
+ testBuilder.addAndroidResources(getTestResources()).enableOptimizedShrinking())
+ .allowDiagnosticInfoMessages(parameters.canUseNativeMultidex())
.collectBuildMetadata()
.setMinApi(parameters)
- .compile()
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (parameters.canUseNativeMultidex()) {
+ diagnostics.assertInfosMatch(
+ diagnosticMessage(containsString("Startup DEX files contains")));
+ } else {
+ diagnostics.assertNoMessages();
+ }
+ })
.getBuildMetadata();
String json = buildMetadata.toJson();
// Inspecting the exact contents is not important here, but it *is* important to test that the
// property names are unobfuscated when testing with R8lib (!).
- assertEquals("{\"version\":\"" + Version.LABEL + "\"}", json);
+ assertThat(json, containsString("\"version\":\"" + Version.LABEL + "\""));
buildMetadata = R8BuildMetadata.fromJson(json);
+ inspectDeserializedBuildMetadata(buildMetadata);
+ }
+
+ private AndroidTestResource getTestResources() throws IOException {
+ return new AndroidTestResourceBuilder().withSimpleManifestAndAppNameString().build(temp);
+ }
+
+ private void inspectDeserializedBuildMetadata(R8BuildMetadata buildMetadata) {
+ assertNotNull(buildMetadata.getBaselineProfileRewritingOptions());
+ assertNotNull(buildMetadata.getOptions());
+ assertNotNull(buildMetadata.getOptions().getKeepAttributesOptions());
+ if (parameters.isDexRuntime()) {
+ R8ResourceOptimizationOptions resourceOptimizationOptions =
+ buildMetadata.getResourceOptimizationOptions();
+ assertNotNull(resourceOptimizationOptions);
+ assertTrue(resourceOptimizationOptions.isOptimizedShrinkingEnabled());
+ } else {
+ assertNull(buildMetadata.getResourceOptimizationOptions());
+ }
+ R8StartupOptimizationOptions startupOptimizationOptions =
+ buildMetadata.getStartupOptizationOptions();
+ assertNotNull(startupOptimizationOptions);
+ assertEquals(
+ parameters.isDexRuntime() && parameters.canUseNativeMultidex() ? 1 : 0,
+ startupOptimizationOptions.getNumberOfStartupDexFiles());
assertEquals(Version.LABEL, buildMetadata.getVersion());
}