blob: 4ffdefab36e3c5ab4338b7c78aea4becb166b28a [file] [log] [blame]
// 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.utils;
import com.android.build.shrinker.NoDebugReporter;
import com.android.build.shrinker.ShrinkerDebugReporter;
import com.android.build.shrinker.r8integration.R8ResourceShrinkerState;
import com.android.tools.r8.AndroidResourceInput;
import com.android.tools.r8.AndroidResourceProvider;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.graph.AppView;
import java.io.InputStream;
import java.util.function.Supplier;
public class ResourceShrinkerUtils {
public static R8ResourceShrinkerState createResourceShrinkerState(AppView<?> appView) {
InternalOptions options = appView.options();
R8ResourceShrinkerState state =
new R8ResourceShrinkerState(
exception -> appView.reporter().fatalError(new ExceptionDiagnostic(exception)),
shrinkerDebugReporterFromStringConsumer(
options.resourceShrinkerConfiguration.getDebugConsumer(), appView.reporter()),
options.enableXmlInlining);
if (options.isOptimizedResourceShrinking()) {
try {
addResources(appView, state, options.androidResourceProvider, FeatureSplit.BASE);
if (options.hasFeatureSplitConfiguration()) {
for (FeatureSplit featureSplit :
options.getFeatureSplitConfiguration().getFeatureSplits()) {
if (featureSplit.getAndroidResourceProvider() != null) {
addResources(appView, state, featureSplit.getAndroidResourceProvider(), featureSplit);
}
}
}
} catch (ResourceException e) {
throw appView.reporter().fatalError("Failed initializing resource table");
}
state.setupReferences();
}
return state;
}
private static void addResources(
AppView<?> appView,
R8ResourceShrinkerState state,
AndroidResourceProvider androidResourceProvider,
FeatureSplit featureSplit)
throws ResourceException {
for (AndroidResourceInput androidResource : androidResourceProvider.getAndroidResources()) {
switch (androidResource.getKind()) {
case MANIFEST:
state.addManifestProvider(
() -> wrapThrowingInputStreamResource(appView, androidResource));
break;
case RESOURCE_TABLE:
state.addResourceTable(androidResource.getByteStream(), featureSplit);
break;
case XML_FILE:
if (androidResource.getPath().location().startsWith("res/raw/")) {
state.addResFileProvider(
() -> wrapThrowingInputStreamResource(appView, androidResource),
androidResource.getPath().location());
} else {
state.addXmlFileProvider(
() -> wrapThrowingInputStreamResource(appView, androidResource),
androidResource.getPath().location());
}
break;
case KEEP_RULE_FILE:
state.addKeepRuleRileProvider(
() -> wrapThrowingInputStreamResource(appView, androidResource));
break;
case RES_FOLDER_FILE:
state.addResFileProvider(
() -> wrapThrowingInputStreamResource(appView, androidResource),
androidResource.getPath().location());
break;
case UNKNOWN:
break;
}
}
}
public static ShrinkerDebugReporter shrinkerDebugReporterFromStringConsumer(
StringConsumer consumer, DiagnosticsHandler diagnosticsHandler) {
if (consumer == null) {
return NoDebugReporter.INSTANCE;
}
return new ShrinkerDebugReporter() {
@Override
public void debug(Supplier<String> logSupplier) {
// The default usage of shrinkerdebug in the legacy resource shrinker does not add
// new lines. Add these to make it consistent with the normal usage of StringConsumer.
consumer.accept(logSupplier.get() + "\n", diagnosticsHandler);
}
@Override
public void info(Supplier<String> logProducer) {
// The default usage of shrinkerdebug in the legacy resource shrinker does not add
// new lines. Add these to make it consistent with the normal usage of StringConsumer.
consumer.accept(logProducer.get() + "\n", diagnosticsHandler);
}
@Override
public void close() throws Exception {
consumer.finished(diagnosticsHandler);
}
};
}
private static InputStream wrapThrowingInputStreamResource(
AppView<?> appView, AndroidResourceInput androidResource) {
try {
return androidResource.getByteStream();
} catch (ResourceException ex) {
throw appView.reporter().fatalError("Failed reading " + androidResource.getPath().location());
}
}
}