blob: 9a8d99f1ddc19eda1f767bb8fedaebfae4f484f4 [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.builder.internal.compiler;
import com.android.annotations.NonNull;
import com.android.builder.core.AndroidBuilder;
import com.android.builder.core.DexOptions;
import com.android.ide.common.internal.CommandLineRunner;
import com.android.ide.common.internal.LoggedErrorException;
import com.android.sdklib.BuildToolInfo;
import com.android.sdklib.repository.FullRevision;
import com.android.utils.Pair;
import com.google.common.io.Files;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import java.io.File;
import java.io.IOException;
/**
* Pre Dexing cache.
*
* Since we cannot yet have a single task for each library that needs to be pre-dexed (because
* there is no task-level parallelization), this class allows reusing the output of the pre-dexing
* of a library in a project to write the output of the pre-dexing of the same library in
* a different project.
*
* Because different project could use different build-tools, both the library to pre-dex and the
* version of the build tools are used as keys in the cache.
*
* The API is fairly simple, just call {@link #preDexLibrary(java.io.File, java.io.File, com.android.builder.core.DexOptions, com.android.sdklib.BuildToolInfo, boolean, com.android.ide.common.internal.CommandLineRunner)}
*
* The call will be blocking until the pre-dexing happened, either through actual pre-dexing or
* through copying the output of a previous pre-dex run.
*
* After a build a call to {@link #clear(java.io.File, com.android.utils.ILogger)} with a file
* will allow saving the known pre-dexed libraries for future reuse.
*/
public class PreDexCache extends PreProcessCache<DexKey> {
private static final String ATTR_JUMBO_MODE = "jumboMode";
private static final PreDexCache sSingleton = new PreDexCache();
public static PreDexCache getCache() {
return sSingleton;
}
@Override
@NonNull
protected KeyFactory<DexKey> getKeyFactory() {
return new KeyFactory<DexKey>() {
@Override
public DexKey of(@NonNull File sourceFile, @NonNull FullRevision revision,
@NonNull NamedNodeMap attrMap) {
return DexKey.of(sourceFile, revision,
Boolean.parseBoolean(attrMap.getNamedItem(ATTR_JUMBO_MODE).getNodeValue()));
}
};
}
/**
* Pre-dex a given library to a given output with a specific version of the build-tools.
* @param inputFile the jar to pre-dex
* @param outFile the output file.
* @param dexOptions the dex options to run pre-dex
* @param buildToolInfo the build tools info
* @param verbose verbose flag
* @param commandLineRunner the command line runner.
* @throws IOException
* @throws LoggedErrorException
* @throws InterruptedException
*/
public void preDexLibrary(
@NonNull File inputFile,
@NonNull File outFile,
@NonNull DexOptions dexOptions,
@NonNull BuildToolInfo buildToolInfo,
boolean verbose,
@NonNull CommandLineRunner commandLineRunner)
throws IOException, LoggedErrorException, InterruptedException {
DexKey itemKey = DexKey.of(inputFile, buildToolInfo.getRevision(), dexOptions.getJumboMode());
Pair<Item, Boolean> pair = getItem(itemKey, inputFile, outFile);
// if this is a new item
if (pair.getSecond()) {
try {
// haven't process this file yet so do it and record it.
AndroidBuilder.preDexLibrary(inputFile, outFile, dexOptions, buildToolInfo,
verbose, commandLineRunner);
incrementMisses();
} catch (IOException exception) {
// in case of error, delete (now obsolete) output file
outFile.delete();
// and rethrow the error
throw exception;
} catch (LoggedErrorException exception) {
// in case of error, delete (now obsolete) output file
outFile.delete();
// and rethrow the error
throw exception;
} catch (InterruptedException exception) {
// in case of error, delete (now obsolete) output file
outFile.delete();
// and rethrow the error
throw exception;
} finally {
// enable other threads to use the output of this pre-dex.
// if something was thrown they'll handle the missing output file.
pair.getFirst().getLatch().countDown();
}
} else {
// wait until the file is pre-dexed by the first thread.
pair.getFirst().getLatch().await();
// check that the generated file actually exists
File fromFile = pair.getFirst().getOutputFile();
if (fromFile.isFile()) {
// file already pre-dex, just copy the output.
Files.copy(pair.getFirst().getOutputFile(), outFile);
incrementHits();
}
}
}
@Override
protected Node createItemNode(
@NonNull Document document,
@NonNull DexKey itemKey,
@NonNull BaseItem item) throws IOException {
Node itemNode = super.createItemNode(document, itemKey, item);
Attr attr = document.createAttribute(ATTR_JUMBO_MODE);
attr.setValue(Boolean.toString(itemKey.isJumboMode()));
itemNode.getAttributes().setNamedItem(attr);
return itemNode;
}
}