blob: 36a42a6dc0915c5a8c8262f411eea4b8d776a195 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.builder.dexing;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.MessageReceiver;
import com.android.ide.common.blame.parser.DexParser;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.OutputMode;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
import java.io.IOException;
import java.nio.file.Path;
import java.util.concurrent.ForkJoinPool;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
final class D8DexArchiveMerger implements DexArchiveMerger {
@NonNull
private static final Logger LOGGER = Logger.getLogger(D8DexArchiveMerger.class.getName());
private static final String ERROR_MULTIDEX =
"Cannot fit requested classes in a single dex file";
private final int minSdkVersion;
@NonNull private final CompilationMode compilationMode;
@NonNull private final MessageReceiver messageReceiver;
private volatile boolean hintForMultidex = false;
public D8DexArchiveMerger(
@Nonnull MessageReceiver messageReceiver,
int minSdkVersion,
@NonNull CompilationMode compilationMode) {
this.minSdkVersion = minSdkVersion;
this.compilationMode = compilationMode;
this.messageReceiver = messageReceiver;
}
@Override
public void mergeDexArchives(
@NonNull Iterable<Path> inputs,
@NonNull Path outputDir,
@Nullable Path mainDexClasses,
@NonNull DexingType dexingType)
throws DexArchiveMergerException {
LOGGER.log(
Level.INFO,
"Merging to '"
+ outputDir.toAbsolutePath().toString()
+ "' with D8 from "
+ Streams.stream(inputs)
.map(path -> path.toAbsolutePath().toString())
.collect(Collectors.joining(", ")));
if (Iterables.isEmpty(inputs)) {
return;
}
D8DiagnosticsHandler d8DiagnosticsHandler = new InterceptingDiagnosticsHandler();
D8Command.Builder builder = D8Command.builder(d8DiagnosticsHandler);
builder.setDisableDesugaring(true);
for (Path input : inputs) {
try (DexArchive archive = DexArchives.fromInput(input)) {
for (DexArchiveEntry dexArchiveEntry : archive.getFiles()) {
builder.addDexProgramData(
dexArchiveEntry.getDexFileContent(),
D8DiagnosticsHandler.getOrigin(dexArchiveEntry));
}
} catch (IOException e) {
throw getExceptionToRethrow(e, input, d8DiagnosticsHandler);
}
}
try {
if (mainDexClasses != null) {
builder.addMainDexListFiles(mainDexClasses);
}
builder.setMinApiLevel(minSdkVersion)
.setMode(compilationMode)
.setOutput(outputDir, OutputMode.DexIndexed)
.setDisableDesugaring(true)
.setIntermediate(false);
D8.run(builder.build());
} catch (CompilationFailedException e) {
throw getExceptionToRethrow(e, inputs, d8DiagnosticsHandler);
}
}
@NonNull
private DexArchiveMergerException getExceptionToRethrow(
@NonNull Throwable t,
@NonNull Iterable<Path> inputs,
D8DiagnosticsHandler d8DiagnosticsHandler) {
StringBuilder msg = new StringBuilder("Error while merging dex archives: ");
msg.append(Joiner.on(", ").join(inputs));
for (String hint : d8DiagnosticsHandler.getPendingHints()) {
msg.append(System.lineSeparator());
msg.append(hint);
}
return new DexArchiveMergerException(msg.toString(), t);
}
private class InterceptingDiagnosticsHandler extends D8DiagnosticsHandler {
public InterceptingDiagnosticsHandler() {
super(D8DexArchiveMerger.this.messageReceiver);
}
@Override
protected Message convertToMessage(Message.Kind kind, Diagnostic diagnostic) {
if (diagnostic.getDiagnosticMessage().startsWith(ERROR_MULTIDEX)) {
addHint(DexParser.DEX_LIMIT_EXCEEDED_ERROR);
}
return super.convertToMessage(kind, diagnostic);
}
}
}