/*
* 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.process.JavaProcessExecutor;
import com.android.ide.common.process.ProcessException;
import com.android.ide.common.process.ProcessOutputHandler;
import com.android.sdklib.BuildToolInfo;
import com.android.sdklib.repository.FullRevision;
import com.android.utils.ILogger;
import com.android.utils.Pair;
import com.google.common.io.Files;
import org.w3c.dom.NamedNodeMap;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* Cache for jar -> jack conversion, using the Jill tool.
*
* Since we cannot yet have a single task for each library that needs to be run through Jill
* (because there is no task-level parallelization), this class allows reusing the output of
* the jill process for a library in a project in other projects.
*
* Because different project could use different build-tools, both the library to be converted
* and the version of the build tools are used as keys in the cache.
*
* The API is fairly simple, just call {@link #convertLibrary(File, File, DexOptions, BuildToolInfo, boolean, JavaProcessExecutor, ProcessOutputHandler)}
*
* The call will be blocking until the conversion happened, either through actually running Jill or
* through copying the output of a previous Jill run.
*
* After a build a call to {@link #clear(java.io.File, com.android.utils.ILogger)} with a file
* will allow saving the known converted libraries for future reuse.
*/
public class JackConversionCache extends PreProcessCache<PreProcessCache.Key> {
private static final JackConversionCache sSingleton = new JackConversionCache();
public static JackConversionCache getCache() {
return sSingleton;
}
@NonNull
@Override
protected KeyFactory<Key> getKeyFactory() {
return new KeyFactory<Key>() {
@Override
public Key of(@NonNull File sourceFile, @NonNull FullRevision revision,
@NonNull NamedNodeMap attrMap) {
return Key.of(sourceFile, revision);
}
};
}
/**
* Converts a given library to a given output with Jill, using 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 processExecutor the java process executor.
* @throws ProcessException
*/
public void convertLibrary(
@NonNull File inputFile,
@NonNull File outFile,
@NonNull DexOptions dexOptions,
@NonNull BuildToolInfo buildToolInfo,
boolean verbose,
@NonNull JavaProcessExecutor processExecutor,
@NonNull ProcessOutputHandler processOutputHandler,
@NonNull ILogger logger)
throws ProcessException, InterruptedException, IOException {
Key itemKey = Key.of(inputFile, buildToolInfo.getRevision());
Pair<PreProcessCache.Item, Boolean> pair = getItem(itemKey);
Item item = pair.getFirst();
// if this is a new item
if (pair.getSecond()) {
try {
// haven't process this file yet so do it and record it.
List<File> files = AndroidBuilder.convertLibaryToJackUsingApis(
inputFile,
outFile,
dexOptions,
buildToolInfo,
verbose,
processExecutor,
processOutputHandler,
logger);
item.getOutputFiles().addAll(files);
incrementMisses();
} catch (ProcessException 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.
item.getLatch().countDown();
}
} else {
// wait until the file is pre-dexed by the first thread.
item.getLatch().await();
// check that the generated file actually exists
// while the api allow for 2+ files, there's only ever one in this case.
File fromFile = item.getOutputFiles().get(0);
if (fromFile.isFile()) {
// file already pre-dex, just copy the output.
// while the api allow for 2+ files, there's only ever one in this case.
Files.copy(fromFile, outFile);
incrementHits();
}
}
}
}