Populate R8 partial build metadata
This extends the R8 partial build metadata with the following pieces of information.
* Whether the patterns "androidx.**", "kotlin.**" and "kotlinx.**" are included.
* The number of included/excluded classes in the input to R8 partial. These numbers give an indication of how much of the application that is subject to optimization.
* The number of included classes in the output. This gives an indication how effective R8 was at shrinking the included part. (The metadata does not contain the number of excluded classes in the output, since this is similar, but not equivalent, to the number of excluded classes in the input.)
* The sum of the dex bytecode size of the included classes and the sum of the dex bytecode size of the excluded classes. These numbers given an indication how much of the generated DEX are due to the include/exclude patterns.
Bug: b/365934833
Change-Id: I4237a43d45f4e0471f6be0da051f52c47c0ef510
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index f193ba2..78c7d8c 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -250,7 +250,8 @@
static void runInternal(AndroidApp app, InternalOptions options, ExecutorService executor)
throws IOException {
- if (options.partialCompilationConfiguration.isEnabled()) {
+ if (options.partialCompilationConfiguration.isEnabled()
+ && options.partialSubCompilationConfiguration == null) {
try {
new R8Partial(options).runInternal(app, executor);
} catch (ResourceException e) {
diff --git a/src/main/java/com/android/tools/r8/R8Partial.java b/src/main/java/com/android/tools/r8/R8Partial.java
index e412af4..0a6fa63 100644
--- a/src/main/java/com/android/tools/r8/R8Partial.java
+++ b/src/main/java/com/android/tools/r8/R8Partial.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
-import com.android.tools.r8.partial.R8PartialCompilationConfiguration;
+import com.android.tools.r8.metadata.impl.R8PartialCompilationStatsMetadataBuilder;
import com.android.tools.r8.partial.R8PartialD8Input;
import com.android.tools.r8.partial.R8PartialD8Result;
import com.android.tools.r8.partial.R8PartialProgramPartitioning;
@@ -72,6 +72,9 @@
timing.begin("Process input");
R8PartialD8Input input = runProcessInputStep(app, executor);
+ R8PartialCompilationStatsMetadataBuilder statsMetadataBuilder =
+ R8PartialCompilationStatsMetadataBuilder.create(input, options);
+
timing.end().begin("Run D8");
R8PartialD8Result d8Result = runD8Step(input, executor);
timing.end();
@@ -80,7 +83,7 @@
lockFeatureSplitProgramResourceProviders();
timing.begin("Run R8");
- runR8Step(app, d8Result, executor);
+ runR8Step(app, d8Result, statsMetadataBuilder, executor);
timing.end();
if (options.isPrintTimesReportingEnabled()) {
@@ -150,7 +153,11 @@
subCompilationConfiguration.getStartupProfile());
}
- private void runR8Step(AndroidApp app, R8PartialD8Result d8Result, ExecutorService executor)
+ private void runR8Step(
+ AndroidApp app,
+ R8PartialD8Result d8Result,
+ R8PartialCompilationStatsMetadataBuilder statsMetadataBuilder,
+ ExecutorService executor)
throws IOException {
// Compile R8 input with R8 using the keep rules from trace references.
DiagnosticsHandler r8DiagnosticsHandler =
@@ -183,8 +190,7 @@
options.apiModelingOptions().isApiModelingEnabled())
.setMinApiLevel(options.getMinApiLevel().getLevel())
.setMode(options.getCompilationMode())
- .setPartialCompilationConfiguration(
- R8PartialCompilationConfiguration.disabledConfiguration())
+ .setPartialCompilationConfiguration(options.partialCompilationConfiguration)
.setProgramConsumer(options.programConsumer);
// The program input that R8 must compile is provided above using an
// InternalProgramClassProvider. This passes in the data resources that we must either rewrite
@@ -228,6 +234,7 @@
d8Result.getFlags(),
d8Result.getKeepDeclarations(),
d8Result.getStartupProfile(),
+ statsMetadataBuilder,
timing);
r8Options.setArtProfileOptions(
new ArtProfileOptions(r8Options, options.getArtProfileOptions()));
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 1142e3f..b2dcd69 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -53,6 +53,7 @@
import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapId;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialR8SubCompilationConfiguration;
+import com.android.tools.r8.partial.R8PartialUtils;
import com.android.tools.r8.profile.startup.StartupCompleteness;
import com.android.tools.r8.profile.startup.profile.StartupProfile;
import com.android.tools.r8.shaking.MainDexInfo;
@@ -446,6 +447,18 @@
}
timing.end();
+ // Finalize the R8 partial compilation build metadata stats. This must be done before writing
+ // the virtual files, since the writing of the virtual files unsets the code object of each
+ // method.
+ if (options.r8BuildMetadataConsumer != null) {
+ R8PartialUtils.acceptR8PartialR8SubCompilationConfiguration(
+ appView,
+ configuration ->
+ configuration
+ .getStatsMetadataBuilder()
+ .finalizeStats(appView.withClassHierarchy()));
+ }
+
// Write the actual dex code.
writeVirtualFiles(executorService, virtualFiles, forcedStrings, timing);
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 256e9d2..2f8f82d 100644
--- a/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
+++ b/src/main/java/com/android/tools/r8/metadata/R8BuildMetadata.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.metadata.impl.R8LibraryDesugaringMetadataImpl;
import com.android.tools.r8.metadata.impl.R8OptionsMetadataImpl;
import com.android.tools.r8.metadata.impl.R8PartialCompilationMetadataImpl;
+import com.android.tools.r8.metadata.impl.R8PartialCompilationStatsMetadataImpl;
import com.android.tools.r8.metadata.impl.R8ResourceOptimizationMetadataImpl;
import com.android.tools.r8.metadata.impl.R8StartupOptimizationMetadataImpl;
import com.android.tools.r8.metadata.impl.R8StatsMetadataImpl;
@@ -50,6 +51,9 @@
R8PartialCompilationMetadata.class,
deserializeTo(R8PartialCompilationMetadataImpl.class))
.registerTypeAdapter(
+ R8PartialCompilationStatsMetadata.class,
+ deserializeTo(R8PartialCompilationStatsMetadataImpl.class))
+ .registerTypeAdapter(
R8ResourceOptimizationMetadata.class,
deserializeTo(R8ResourceOptimizationMetadataImpl.class))
.registerTypeAdapter(
diff --git a/src/main/java/com/android/tools/r8/metadata/R8PartialCompilationMetadata.java b/src/main/java/com/android/tools/r8/metadata/R8PartialCompilationMetadata.java
index 99cf1a3..541a2d5 100644
--- a/src/main/java/com/android/tools/r8/metadata/R8PartialCompilationMetadata.java
+++ b/src/main/java/com/android/tools/r8/metadata/R8PartialCompilationMetadata.java
@@ -3,4 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.metadata;
-public interface R8PartialCompilationMetadata {}
+import java.util.List;
+
+public interface R8PartialCompilationMetadata {
+
+ List<String> getCommonIncludePatterns();
+
+ R8PartialCompilationStatsMetadata getStatsMetadata();
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/R8PartialCompilationStatsMetadata.java b/src/main/java/com/android/tools/r8/metadata/R8PartialCompilationStatsMetadata.java
new file mode 100644
index 0000000..8f72a77
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/R8PartialCompilationStatsMetadata.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2025, 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;
+
+public interface R8PartialCompilationStatsMetadata {
+
+ /**
+ * The sum of the bytecode sizes of the DEX code objects in the excldued part that is not subject
+ * to optimization.
+ */
+ int getDexCodeSizeOfExcludedClassesInBytes();
+
+ /**
+ * The sum of the bytecode sizes of the DEX code objects in the included part that is subject to
+ * optimization.
+ */
+ int getDexCodeSizeOfIncludedClassesInBytes();
+
+ int getNumberOfExcludedClassesInInput();
+
+ int getNumberOfIncludedClassesInInput();
+
+ int getNumberOfIncludedClassesInOutput();
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationMetadataImpl.java b/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationMetadataImpl.java
index efbc67a..a5b2787 100644
--- a/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationMetadataImpl.java
+++ b/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationMetadataImpl.java
@@ -9,8 +9,17 @@
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
import com.android.tools.r8.keepanno.annotations.UsedByReflection;
import com.android.tools.r8.metadata.R8PartialCompilationMetadata;
+import com.android.tools.r8.metadata.R8PartialCompilationStatsMetadata;
+import com.android.tools.r8.partial.R8PartialCompilationConfiguration;
+import com.android.tools.r8.partial.predicate.R8PartialPredicate;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
@UsedByReflection(
description = "Keep and preserve @SerializedName for correct (de)serialization",
@@ -21,12 +30,50 @@
fieldAnnotatedByClassConstant = SerializedName.class)
public class R8PartialCompilationMetadataImpl implements R8PartialCompilationMetadata {
- private R8PartialCompilationMetadataImpl() {}
+ @Expose
+ @SerializedName("commonIncludePatterns")
+ private final List<String> commonIncludePatterns;
+
+ @Expose
+ @SerializedName("stats")
+ private final R8PartialCompilationStatsMetadata statsMetadata;
+
+ private R8PartialCompilationMetadataImpl(
+ List<String> commonIncludePatterns, R8PartialCompilationStatsMetadata statsMetadata) {
+ this.commonIncludePatterns = commonIncludePatterns;
+ this.statsMetadata = statsMetadata;
+ }
public static R8PartialCompilationMetadataImpl create(InternalOptions options) {
- if (options.partialSubCompilationConfiguration == null) {
- return null;
+ if (options.partialCompilationConfiguration.isEnabled()) {
+ return new R8PartialCompilationMetadataImpl(
+ createCommonIncludePatterns(options.partialCompilationConfiguration),
+ options.getR8PartialR8SubCompilationOptions().getStatsMetadataBuilder().build());
}
- return new R8PartialCompilationMetadataImpl();
+ return null;
+ }
+
+ private static List<String> createCommonIncludePatterns(
+ R8PartialCompilationConfiguration partialCompilationConfiguration) {
+ Set<String> commonIncludePatterns = ImmutableSet.of("androidx.**", "kotlin.**", "kotlinx.**");
+ List<String> result = new ArrayList<>();
+ for (R8PartialPredicate includePredicate :
+ partialCompilationConfiguration.getIncludePredicates()) {
+ String includePattern = includePredicate.serializeToString();
+ if (commonIncludePatterns.contains(includePattern)) {
+ result.add(includePattern);
+ }
+ }
+ return ListUtils.sort(result, String::compareTo);
+ }
+
+ @Override
+ public List<String> getCommonIncludePatterns() {
+ return commonIncludePatterns;
+ }
+
+ @Override
+ public R8PartialCompilationStatsMetadata getStatsMetadata() {
+ return statsMetadata;
}
}
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationStatsMetadataBuilder.java b/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationStatsMetadataBuilder.java
new file mode 100644
index 0000000..cfb81ae
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationStatsMetadataBuilder.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2025, 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.impl;
+
+import static com.android.tools.r8.utils.AssertionUtils.checkNotNegative;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.partial.R8PartialD8Input;
+import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialR8SubCompilationConfiguration;
+import com.android.tools.r8.utils.IntBox;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+public class R8PartialCompilationStatsMetadataBuilder {
+
+ private final int numberOfExcludedClassesInInput;
+ private final int numberOfIncludedClassesInInput;
+
+ private int dexCodeSizeOfExcludedClassesInBytes = -1;
+ private int dexCodeSizeOfIncludedClassesInBytes = -1;
+ private int numberOfIncludedClassesInOutput = -1;
+
+ private R8PartialCompilationStatsMetadataBuilder(R8PartialD8Input input) {
+ this.numberOfExcludedClassesInInput = input.getD8Classes().size();
+ this.numberOfIncludedClassesInInput = input.getR8Classes().size();
+ }
+
+ public static R8PartialCompilationStatsMetadataBuilder create(
+ R8PartialD8Input input, InternalOptions options) {
+ return options.r8BuildMetadataConsumer != null
+ ? new R8PartialCompilationStatsMetadataBuilder(input)
+ : null;
+ }
+
+ public void finalizeStats(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ R8PartialR8SubCompilationConfiguration subCompilationConfiguration =
+ appView.options().partialSubCompilationConfiguration.asR8();
+ dexCodeSizeOfExcludedClassesInBytes =
+ sumDexCodeSizeInBytes(appView, subCompilationConfiguration::isD8Definition);
+ dexCodeSizeOfIncludedClassesInBytes =
+ sumDexCodeSizeInBytes(appView, subCompilationConfiguration::isR8Definition);
+ numberOfIncludedClassesInOutput =
+ appView.appInfo().classes().size()
+ - subCompilationConfiguration.getDexingOutputClasses().size();
+ }
+
+ private static int sumDexCodeSizeInBytes(
+ AppView<? extends AppInfoWithClassHierarchy> appView, Predicate<DexProgramClass> predicate) {
+ IntBox dexCodeSizeInBytes = new IntBox();
+ for (DexProgramClass clazz : Iterables.filter(appView.appInfo().classes(), predicate)) {
+ clazz.forEachProgramMethodMatching(
+ DexEncodedMethod::hasCode,
+ method ->
+ dexCodeSizeInBytes.increment(
+ method.getDefinition().getCode().asDexWritableCode().codeSizeInBytes()));
+ }
+ return dexCodeSizeInBytes.get();
+ }
+
+ public R8PartialCompilationStatsMetadataImpl build() {
+ // Verify that finalizeStats has been called.
+ return new R8PartialCompilationStatsMetadataImpl(
+ checkNotNegative(dexCodeSizeOfExcludedClassesInBytes),
+ checkNotNegative(dexCodeSizeOfIncludedClassesInBytes),
+ numberOfExcludedClassesInInput,
+ numberOfIncludedClassesInInput,
+ checkNotNegative(numberOfIncludedClassesInOutput));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationStatsMetadataImpl.java b/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationStatsMetadataImpl.java
new file mode 100644
index 0000000..ccc5698
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/metadata/impl/R8PartialCompilationStatsMetadataImpl.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2025, 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.impl;
+
+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.metadata.R8PartialCompilationStatsMetadata;
+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 R8PartialCompilationStatsMetadataImpl implements R8PartialCompilationStatsMetadata {
+
+ @Expose
+ @SerializedName("dexCodeSizeOfExcludedClassesInBytes")
+ private final int dexCodeSizeOfExcludedClassesInBytes;
+
+ @Expose
+ @SerializedName("dexCodeSizeOfIncludedClassesInBytes")
+ private final int dexCodeSizeOfIncludedClassesInBytes;
+
+ @Expose
+ @SerializedName("numberOfExcludedClassesInInput")
+ private final int numberOfExcludedClassesInInput;
+
+ @Expose
+ @SerializedName("numberOfIncludedClassesInInput")
+ private final int numberOfIncludedClassesInInput;
+
+ @Expose
+ @SerializedName("numberOfIncludedClassesInOutput")
+ private final int numberOfIncludedClassesInOutput;
+
+ R8PartialCompilationStatsMetadataImpl(
+ int dexCodeSizeOfExcludedClassesInBytes,
+ int dexCodeSizeOfIncludedClassesInBytes,
+ int numberOfExcludedClassesInInput,
+ int numberOfIncludedClassesInInput,
+ int numberOfIncludedClassesInOutput) {
+ this.dexCodeSizeOfExcludedClassesInBytes = dexCodeSizeOfExcludedClassesInBytes;
+ this.dexCodeSizeOfIncludedClassesInBytes = dexCodeSizeOfIncludedClassesInBytes;
+ this.numberOfExcludedClassesInInput = numberOfExcludedClassesInInput;
+ this.numberOfIncludedClassesInInput = numberOfIncludedClassesInInput;
+ this.numberOfIncludedClassesInOutput = numberOfIncludedClassesInOutput;
+ }
+
+ @Override
+ public int getDexCodeSizeOfExcludedClassesInBytes() {
+ return dexCodeSizeOfExcludedClassesInBytes;
+ }
+
+ @Override
+ public int getDexCodeSizeOfIncludedClassesInBytes() {
+ return dexCodeSizeOfIncludedClassesInBytes;
+ }
+
+ @Override
+ public int getNumberOfExcludedClassesInInput() {
+ return numberOfExcludedClassesInInput;
+ }
+
+ @Override
+ public int getNumberOfIncludedClassesInInput() {
+ return numberOfIncludedClassesInInput;
+ }
+
+ @Override
+ public int getNumberOfIncludedClassesInOutput() {
+ return numberOfIncludedClassesInOutput;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/partial/R8PartialD8Input.java b/src/main/java/com/android/tools/r8/partial/R8PartialD8Input.java
index a48d995..d11e84d 100644
--- a/src/main/java/com/android/tools/r8/partial/R8PartialD8Input.java
+++ b/src/main/java/com/android/tools/r8/partial/R8PartialD8Input.java
@@ -57,12 +57,20 @@
.addLibraryResourceProvider(new InternalClasspathOrLibraryClassProvider<>(libraryClasses));
}
+ public Collection<DexProgramClass> getD8Classes() {
+ return d8Classes;
+ }
+
public Set<DexType> getD8Types() {
// Intentionally not returning d8Classes.keySet(). This allows clearing the map after providing
// the classes to the D8 compilation.
return SetUtils.mapIdentityHashSet(d8Classes, DexClass::getType);
}
+ public Collection<DexProgramClass> getR8Classes() {
+ return r8Classes;
+ }
+
public Set<DexType> getR8Types() {
// Intentionally not returning r8Classes.keySet(). This allows clearing the map after providing
// the classes to the D8 compilation.
diff --git a/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java b/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java
index 1e01437..5906b9f 100644
--- a/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java
+++ b/src/main/java/com/android/tools/r8/partial/R8PartialSubCompilationConfiguration.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.conversion.MethodConversionOptions.Target;
import com.android.tools.r8.ir.desugar.desugaredlibrary.LibraryDesugaringOptions;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
+import com.android.tools.r8.metadata.impl.R8PartialCompilationStatsMetadataBuilder;
import com.android.tools.r8.profile.art.ArtProfile;
import com.android.tools.r8.profile.art.ArtProfileCollection;
import com.android.tools.r8.profile.art.ArtProfileMethodRule;
@@ -198,6 +199,7 @@
private Map<DexType, DexProgramClass> dexingOutputClasses;
private List<KeepDeclaration> keepDeclarations;
private StartupProfile startupProfile;
+ private R8PartialCompilationStatsMetadataBuilder statsMetadataBuilder;
// Stores the missing class references from the D8 compilation unit in R8 partial.
// We use this to ensure that calling AppInfoWithLiveness#definitionFor does not fail
@@ -214,6 +216,7 @@
DexApplicationReadFlags flags,
List<KeepDeclaration> keepDeclarations,
StartupProfile startupProfile,
+ R8PartialCompilationStatsMetadataBuilder statsMetadataBuilder,
Timing timing) {
super(flags, timing);
this.artProfiles = artProfiles;
@@ -222,6 +225,7 @@
MapUtils.transform(dexingOutputClasses, IdentityHashMap::new, DexClass::getType);
this.keepDeclarations = keepDeclarations;
this.startupProfile = startupProfile;
+ this.statsMetadataBuilder = statsMetadataBuilder;
}
public ArtProfileCollection getArtProfiles() {
@@ -249,6 +253,10 @@
return startupProfile;
}
+ public R8PartialCompilationStatsMetadataBuilder getStatsMetadataBuilder() {
+ return statsMetadataBuilder;
+ }
+
public void amendCompleteArtProfile(ArtProfile.Builder artProfileBuilder) {
List<DexProgramClass> dexingOutputClassesSorted =
ListUtils.sort(dexingOutputClasses.values(), Comparator.comparing(DexClass::getType));
@@ -308,6 +316,10 @@
return hasD8DefinitionFor(definition.getReference());
}
+ public boolean isR8Definition(Definition definition) {
+ return !isD8Definition(definition);
+ }
+
@Override
public boolean isR8() {
return true;
diff --git a/src/main/java/com/android/tools/r8/partial/R8PartialUtils.java b/src/main/java/com/android/tools/r8/partial/R8PartialUtils.java
new file mode 100644
index 0000000..bf9a97d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/partial/R8PartialUtils.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2025, 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.partial;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialR8SubCompilationConfiguration;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class R8PartialUtils {
+
+ public static void acceptR8PartialR8SubCompilationConfiguration(
+ AppView<?> appView, Consumer<R8PartialR8SubCompilationConfiguration> fn) {
+ R8PartialSubCompilationConfiguration subCompilationConfiguration =
+ appView.options().partialSubCompilationConfiguration;
+ if (subCompilationConfiguration != null && subCompilationConfiguration.isR8()) {
+ fn.accept(subCompilationConfiguration.asR8());
+ }
+ }
+
+ public static <T> T applyR8PartialR8SubCompilationConfiguration(
+ AppView<?> appView, Function<R8PartialR8SubCompilationConfiguration, T> fn, T defaultValue) {
+ R8PartialSubCompilationConfiguration subCompilationConfiguration =
+ appView.options().partialSubCompilationConfiguration;
+ if (subCompilationConfiguration != null && subCompilationConfiguration.isR8()) {
+ return fn.apply(subCompilationConfiguration.asR8());
+ }
+ return defaultValue;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/partial/predicate/R8PartialPredicateCollection.java b/src/main/java/com/android/tools/r8/partial/predicate/R8PartialPredicateCollection.java
index 2b8339b..7edc478 100644
--- a/src/main/java/com/android/tools/r8/partial/predicate/R8PartialPredicateCollection.java
+++ b/src/main/java/com/android/tools/r8/partial/predicate/R8PartialPredicateCollection.java
@@ -9,9 +9,10 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
-public class R8PartialPredicateCollection {
+public class R8PartialPredicateCollection implements Iterable<R8PartialPredicate> {
private final List<R8PartialPredicate> predicates = new ArrayList<>();
@@ -23,6 +24,11 @@
return predicates.isEmpty();
}
+ @Override
+ public Iterator<R8PartialPredicate> iterator() {
+ return predicates.iterator();
+ }
+
public boolean test(DexProgramClass clazz) {
DexString descriptor = clazz.getType().getDescriptor();
for (R8PartialPredicate predicate : predicates) {
diff --git a/src/main/java/com/android/tools/r8/utils/AssertionUtils.java b/src/main/java/com/android/tools/r8/utils/AssertionUtils.java
index bb1e59f..30a6dad 100644
--- a/src/main/java/com/android/tools/r8/utils/AssertionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AssertionUtils.java
@@ -13,6 +13,13 @@
return true;
}
+ public static int checkNotNegative(int i) {
+ if (i < 0) {
+ throw new AssertionError("Expected integer value to be non-negative");
+ }
+ return i;
+ }
+
public static boolean forTesting(InternalOptions options, Supplier<Boolean> test) {
return options.testing.enableTestAssertions ? test.get() : true;
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 01bc03f..60ee0bd 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -86,6 +86,8 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.partial.R8PartialCompilationConfiguration;
import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration;
+import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialD8SubCompilationConfiguration;
+import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialR8SubCompilationConfiguration;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.profile.art.ArtProfileOptions;
import com.android.tools.r8.profile.startup.StartupOptions;
@@ -1087,6 +1089,18 @@
return libraryDesugaringOptions;
}
+ public R8PartialD8SubCompilationConfiguration getR8PartialD8SubCompilationOptions() {
+ return partialSubCompilationConfiguration != null && partialSubCompilationConfiguration.isD8()
+ ? partialSubCompilationConfiguration.asD8()
+ : null;
+ }
+
+ public R8PartialR8SubCompilationConfiguration getR8PartialR8SubCompilationOptions() {
+ return partialSubCompilationConfiguration != null && partialSubCompilationConfiguration.isR8()
+ ? partialSubCompilationConfiguration.asR8()
+ : null;
+ }
+
/**
* Similar to {@link #getLibraryDesugaringOptions()}, except for the D8 compilation in R8 partial,
* where this deliberately returns the library desugaring options of the outer R8 partial
diff --git a/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java b/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java
index de97ee5..efc5a86 100644
--- a/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/metadata/R8BuildMetadataTest.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.startup.profile.ExternalStartupItem;
import com.android.tools.r8.startup.utils.StartupTestingUtils;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@@ -195,10 +196,24 @@
assertNull(libraryDesugaringMetadata);
}
// Partial compilation metadata.
+ R8PartialCompilationMetadata partialCompilationMetadata =
+ buildMetadata.getPartialCompilationMetadata();
if (isR8Partial) {
- assertNotNull(buildMetadata.getPartialCompilationMetadata());
+ assertNotNull(partialCompilationMetadata);
+ assertEquals(
+ Lists.newArrayList("androidx.**", "kotlin.**", "kotlinx.**"),
+ partialCompilationMetadata.getCommonIncludePatterns());
+
+ R8PartialCompilationStatsMetadata partialCompilationStatsMetadata =
+ partialCompilationMetadata.getStatsMetadata();
+ assertNotNull(partialCompilationStatsMetadata);
+ assertTrue(partialCompilationStatsMetadata.getDexCodeSizeOfExcludedClassesInBytes() > 50);
+ assertEquals(0, partialCompilationStatsMetadata.getDexCodeSizeOfIncludedClassesInBytes());
+ assertEquals(4, partialCompilationStatsMetadata.getNumberOfExcludedClassesInInput());
+ assertEquals(934, partialCompilationStatsMetadata.getNumberOfIncludedClassesInInput());
+ assertEquals(0, partialCompilationStatsMetadata.getNumberOfIncludedClassesInOutput());
} else {
- assertNull(buildMetadata.getPartialCompilationMetadata());
+ assertNull(partialCompilationMetadata);
}
// Resource optimization metadata.
if (parameters.isDexRuntime()) {