| /* |
| * 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; |
| } |
| } |