blob: c5d5c62360fe2ce5654f54c24447fad715908f3d [file] [log] [blame]
/*
* Copyright (C) 2014 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.jack.backend.dex;
import com.android.jack.Jack;
import com.android.jack.Options;
import com.android.jack.backend.dex.rop.CodeItemBuilder;
import com.android.jack.dx.dex.DexOptions;
import com.android.jack.dx.dex.file.DexFile;
import com.android.jack.dx.io.DexBuffer;
import com.android.jack.ir.ast.JDefinedClassOrInterface;
import com.android.jack.ir.formatter.BinaryQualifiedNameFormatter;
import com.android.jack.ir.formatter.TypePackageAndMethodFormatter;
import com.android.jack.ir.formatter.UserFriendlyFormatter;
import com.android.jack.library.FileType;
import com.android.jack.library.FileTypeDoesNotExistException;
import com.android.jack.library.InputJackLibrary;
import com.android.jack.library.InputLibrary;
import com.android.jack.library.OutputJackLibrary;
import com.android.jack.library.TypeInInputLibraryLocation;
import com.android.jack.tools.merger.JackMerger;
import com.android.jack.tools.merger.MergingOverflowException;
import com.android.sched.util.codec.VariableName;
import com.android.sched.util.config.ThreadConfig;
import com.android.sched.util.file.CannotCreateFileException;
import com.android.sched.util.file.CannotReadException;
import com.android.sched.util.location.Location;
import com.android.sched.util.log.LoggerFactory;
import com.android.sched.vfs.InputVFile;
import com.android.sched.vfs.OutputVFS;
import com.android.sched.vfs.OutputVFile;
import com.android.sched.vfs.VPath;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
/**
* A helper to write dex files.
*/
@VariableName("writer")
public abstract class DexWritingTool {
@Nonnull
private static final TypePackageAndMethodFormatter FORMATTER = Jack.getLookupFormatter();
@Nonnull
private static Logger logger = LoggerFactory.getLogger();
private final boolean forceJumbo = ThreadConfig.get(CodeItemBuilder.FORCE_JUMBO).booleanValue();
private final int apiLevel = ThreadConfig.get(Options.ANDROID_MIN_API_LEVEL).intValue();
private final boolean usePrebuilts =
ThreadConfig.get(Options.USE_PREBUILT_FROM_LIBRARY).booleanValue();
@Nonnull
protected DexFile createDexFile() {
DexOptions options = new DexOptions();
options.forceJumbo = forceJumbo;
options.targetApiLevel = apiLevel;
return new DexFile(options);
}
public abstract void write(@Nonnull OutputVFS outputVDir) throws DexWritingException;
protected void finishMerge(@Nonnull JackMerger merger, @Nonnull OutputVFile out)
throws DexWritingException {
OutputStream os = null;
try {
try {
os = new BufferedOutputStream(out.getOutputStream());
merger.finish(os);
} finally {
if (os != null) {
os.close();
}
}
} catch (IOException e) {
throw new DexWritingException(e);
}
}
protected void mergeDex(@Nonnull JackMerger merger, InputVFile inputDex)
throws MergingOverflowException, DexWritingException {
InputStream inputStream = null;
try {
inputStream = inputDex.getInputStream();
merger.addDexFile(new DexBuffer(inputStream));
} catch (IOException e) {
throw new DexWritingException(new CannotReadException(inputDex, e));
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
logger.log(
Level.WARNING, "Failed to close ''{0}''", inputDex.getLocation().getDescription());
}
}
}
}
@Nonnull
protected OutputVFile getOutputDex(@Nonnull OutputVFS outputVfs, int dexCount)
throws DexWritingException {
assert dexCount >= 1;
String dexName;
if (dexCount == 1) {
dexName = DexFileWriter.DEX_FILENAME;
} else {
dexName = DexFileWriter.DEX_PREFIX + dexCount + DexFileWriter.DEX_FILE_EXTENSION;
}
try {
return outputVfs.getRootOutputVDir().createOutputVFile(new VPath(dexName, '/'));
} catch (CannotCreateFileException e) {
throw new DexWritingException(e);
}
}
protected void fillDexLists(@Nonnull List<InputVFile> mainDexList,
@Nonnull List<InputVFile> anyDexList) {
final OutputJackLibrary jackOutputLibrary = Jack.getSession().getJackOutputLibrary();
Collection<JDefinedClassOrInterface> typesToEmit = Jack.getSession().getTypesToEmit();
List<JDefinedClassOrInterface> anyTypeList = new ArrayList<JDefinedClassOrInterface>(
typesToEmit.size());
for (JDefinedClassOrInterface type : typesToEmit) {
if (type.containsMarker(MainDexMarker.class)) {
mainDexList.add(getDexInputVFileOfType(jackOutputLibrary, type));
} else {
anyTypeList.add(type);
}
}
Collections.sort(anyTypeList, new Comparator<JDefinedClassOrInterface>() {
@Override
public int compare(@Nonnull JDefinedClassOrInterface first,
@Nonnull JDefinedClassOrInterface second) {
return FORMATTER.getName(first).compareTo(FORMATTER.getName(second));
}});
for (JDefinedClassOrInterface type : anyTypeList) {
anyDexList.add(getDexInputVFileOfType(jackOutputLibrary, type));
}
}
@Nonnull
protected InputVFile getDexInputVFileOfType(@Nonnull OutputJackLibrary jackOutputLibrary,
@Nonnull JDefinedClassOrInterface type) {
InputVFile inputVFile = null;
Location location = type.getLocation();
try {
if (location instanceof TypeInInputLibraryLocation) {
InputLibrary inputLibrary =
((TypeInInputLibraryLocation) location).getInputLibraryLocation().getInputLibrary();
if (inputLibrary.containsFileType(FileType.PREBUILT)) {
inputVFile = inputLibrary.getFile(FileType.PREBUILT,
new VPath(BinaryQualifiedNameFormatter.getFormatter().getName(type), '/'));
}
}
if (inputVFile == null) {
inputVFile = jackOutputLibrary.getFile(FileType.PREBUILT,
new VPath(BinaryQualifiedNameFormatter.getFormatter().getName(type), '/'));
}
} catch (FileTypeDoesNotExistException e) {
// this was created by Jack, so this should not happen
throw new AssertionError(
UserFriendlyFormatter.getFormatter().getName(type) + " does not exist");
}
return inputVFile;
}
/**
* Orphan dex file is a dex file without an associated Jayce file.
* @return a list of orphan dex files.
*/
@Nonnull
protected List<InputVFile> getOrphanDexFiles() {
List<InputVFile> orphanDexFiles = new ArrayList<InputVFile>();
if (usePrebuilts) {
for (InputLibrary inputLibrary : Jack.getSession().getImportedLibraries()) {
if (inputLibrary instanceof InputJackLibrary) {
InputJackLibrary inputJackLibrary = (InputJackLibrary) inputLibrary;
Iterator<InputVFile> dexFileIt = inputJackLibrary.iterator(FileType.PREBUILT);
while (dexFileIt.hasNext()) {
InputVFile dexFile = dexFileIt.next();
String dexFilePath = dexFile.getPathFromRoot().getPathAsString('/');
int indexOfDexExtension = dexFilePath.indexOf(DexFileWriter.DEX_FILE_EXTENSION);
// Prebuilt section of library does not contains only dex files
if (indexOfDexExtension != -1) {
String type =
dexFilePath.substring(0, dexFilePath.indexOf(DexFileWriter.DEX_FILE_EXTENSION));
try {
inputJackLibrary.getFile(FileType.JAYCE, new VPath(type, '/'));
} catch (FileTypeDoesNotExistException e) {
orphanDexFiles.add(dexFile);
}
}
}
}
}
}
return orphanDexFiles;
}
}