blob: 7896d57befd54ec590fc1ae7876ac8eeeab81a74 [file] [log] [blame]
// Copyright (c) 2018, 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 static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
import com.android.tools.r8.DexIndexedConsumer.DirectoryConsumer;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.FeatureClassMapping;
import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@Keep
public final class DexSplitterHelper {
public static void run(
D8Command command, FeatureClassMapping featureClassMapping, String output, String proguardMap)
throws CompilationFailedException {
ExecutorService executor = ThreadUtils.getExecutorService(ThreadUtils.NOT_SPECIFIED);
try {
ExceptionUtils.withCompilationHandler(
command.getReporter(),
() -> run(command, featureClassMapping, output, proguardMap, executor));
} finally {
executor.shutdown();
}
}
public static void run(
D8Command command,
FeatureClassMapping featureClassMapping,
String output,
String proguardMap,
ExecutorService executor)
throws IOException {
InternalOptions options = command.getInternalOptions();
options.desugarState = DesugarState.OFF;
options.enableMainDexListCheck = false;
options.ignoreMainDexMissingClasses = true;
options.minimalMainDex = false;
assert !options.isMinifying();
options.enableInlining = false;
options.outline.enabled = false;
try {
Timing timing = new Timing("DexSplitter");
ApplicationReader applicationReader =
new ApplicationReader(command.getInputApp(), options, timing);
DexApplication app = applicationReader.read(executor);
MainDexInfo mainDexInfo = applicationReader.readMainDexClasses(app);
List<Marker> markers = app.dexItemFactory.extractMarkers();
ClassNameMapper mapper = null;
if (proguardMap != null) {
mapper = ClassNameMapper.mapperFromFile(Paths.get(proguardMap));
}
Map<String, LazyLoadedDexApplication.Builder> applications =
getDistribution(app, featureClassMapping, mapper);
for (Entry<String, LazyLoadedDexApplication.Builder> entry : applications.entrySet()) {
String feature = entry.getKey();
timing.begin("Feature " + feature);
DexApplication featureApp = entry.getValue().build();
assert !options.hasMethodsFilter();
// If this is the base, we add the main dex list.
AppInfo appInfo =
feature.equals(featureClassMapping.getBaseName())
? AppInfo.createInitialAppInfo(featureApp, mainDexInfo)
: AppInfo.createInitialAppInfo(featureApp);
AppView<AppInfo> appView = AppView.createForD8(appInfo);
// Run d8 optimize to ensure jumbo strings are handled.
D8.optimize(appView, options, timing, executor);
// We create a specific consumer for each split.
Path outputDir = Paths.get(output).resolve(entry.getKey());
if (!Files.exists(outputDir)) {
Files.createDirectory(outputDir);
}
DexIndexedConsumer consumer = new DirectoryConsumer(outputDir);
try {
new ApplicationWriter(
appView,
markers,
GraphLens.getIdentityLens(),
InitClassLens.getDefault(),
NamingLens.getIdentityLens(),
null,
consumer)
.write(executor);
options.printWarnings();
} finally {
consumer.finished(options.reporter);
}
timing.end();
}
} catch (ExecutionException e) {
throw unwrapExecutionException(e);
} catch (FeatureMappingException e) {
options.reporter.error(e.getMessage());
} finally {
options.signalFinishedToConsumers();
}
}
private static Map<String, LazyLoadedDexApplication.Builder> getDistribution(
DexApplication app, FeatureClassMapping featureClassMapping, ClassNameMapper mapper)
throws FeatureMappingException {
Map<String, LazyLoadedDexApplication.Builder> applications = new HashMap<>();
for (DexProgramClass clazz : app.classes()) {
String clazzName =
mapper != null ? mapper.deobfuscateClassName(clazz.toString()) : clazz.toString();
String feature = featureClassMapping.featureForClass(clazzName);
LazyLoadedDexApplication.Builder featureApplication = applications.get(feature);
if (featureApplication == null) {
featureApplication = DexApplication.builder(app.options, app.timing);
applications.put(feature, featureApplication);
}
featureApplication.addProgramClass(clazz);
}
return applications;
}
public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
throws CompilationFailedException {
InternalOptions options = command.getInternalOptions();
options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
D8.runForTesting(command.getInputApp(), options);
}
}