blob: ca0087f55e72cbe41d610d733fd385d840db1971 [file] [log] [blame]
/*
* Copyright (C) 2011 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.ant;
import com.google.common.base.Charsets;
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.ExecTask;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.resources.FileResource;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* Custom task to execute dx while handling dependencies.
*/
public class DexExecTask extends SingleDependencyTask {
private String mExecutable;
private String mOutput;
private String mDexedLibs;
private boolean mVerbose = false;
private boolean mNoLocals = false;
private boolean mForceJumbo = false;
private boolean mDisableDexMerger = false;
private List<Path> mPathInputs;
private List<FileSet> mFileSetInputs;
/**
* Sets the value of the "executable" attribute.
* @param executable the value.
*/
public void setExecutable(Path executable) {
mExecutable = TaskHelper.checkSinglePath("executable", executable);
}
/**
* Sets the value of the "verbose" attribute.
* @param verbose the value.
*/
public void setVerbose(boolean verbose) {
mVerbose = verbose;
}
/**
* Sets the value of the "output" attribute.
* @param output the value.
*/
public void setOutput(Path output) {
mOutput = TaskHelper.checkSinglePath("output", output);
}
public void setDexedLibs(Path dexedLibs) {
mDexedLibs = TaskHelper.checkSinglePath("dexedLibs", dexedLibs);
}
/**
* Sets the value of the "nolocals" attribute.
* @param nolocals the value.
*/
public void setNoLocals(boolean nolocals) {
mNoLocals = nolocals;
}
public void setForceJumbo(boolean forceJumbo) {
mForceJumbo = forceJumbo;
}
public void setDisableDexMerger(boolean disableMerger) {
mDisableDexMerger = disableMerger;
}
/**
* Returns an object representing a nested <var>path</var> element.
*/
public Object createPath() {
if (mPathInputs == null) {
mPathInputs = new ArrayList<Path>();
}
Path path = new Path(getProject());
mPathInputs.add(path);
return path;
}
/**
* Returns an object representing a nested <var>path</var> element.
*/
public Object createFileSet() {
if (mFileSetInputs == null) {
mFileSetInputs = new ArrayList<FileSet>();
}
FileSet fs = new FileSet();
fs.setProject(getProject());
mFileSetInputs.add(fs);
return fs;
}
private void preDexLibraries(List<File> inputs) {
if (mDisableDexMerger || inputs.size() == 1) {
// only one input, no need to put a pre-dexed version, even if this path is
// just a jar file (case for proguard'ed builds)
return;
}
final int count = inputs.size();
for (int i = 0 ; i < count; i++) {
File input = inputs.get(i);
if (input.isFile()) {
// check if this libs needs to be pre-dexed
String fileName = getDexFileName(input);
File dexedLib = new File(mDexedLibs, fileName);
String dexedLibPath = dexedLib.getAbsolutePath();
if (dexedLib.isFile() == false ||
dexedLib.lastModified() < input.lastModified()) {
System.out.println(
String.format("Pre-Dexing %1$s -> %2$s",
input.getAbsolutePath(), fileName));
if (dexedLib.isFile()) {
dexedLib.delete();
}
runDx(input, dexedLibPath, false /*showInput*/);
} else {
System.out.println(
String.format("Using Pre-Dexed %1$s <- %2$s",
fileName, input.getAbsolutePath()));
}
// replace the input with the pre-dex libs.
inputs.set(i, dexedLib);
}
}
}
private String getDexFileName(File inputFile) {
// get the filename
String name = inputFile.getName();
// remove the extension
int pos = name.lastIndexOf('.');
if (pos != -1) {
name = name.substring(0, pos);
}
// add a hash of the original file path
HashFunction hashFunction = Hashing.md5();
HashCode hashCode = hashFunction.hashString(inputFile.getAbsolutePath(), Charsets.UTF_16LE);
return name + "-" + hashCode.toString() + ".jar";
}
@Override
public void execute() throws BuildException {
// get all input paths
List<File> paths = new ArrayList<File>();
if (mPathInputs != null) {
for (Path pathList : mPathInputs) {
for (String path : pathList.list()) {
System.out.println("input: " + path);
paths.add(new File(path));
}
}
}
if (mFileSetInputs != null) {
for (FileSet fs : mFileSetInputs) {
Iterator<?> iter = fs.iterator();
while (iter.hasNext()) {
FileResource fr = (FileResource) iter.next();
System.out.println("input: " + fr.getFile().toString());
paths.add(fr.getFile());
}
}
}
// pre dex libraries if needed
preDexLibraries(paths);
// figure out the path to the dependency file.
String depFile = mOutput + ".d";
// get InputPath with no extension restrictions
List<InputPath> inputPaths = getInputPaths(paths, null /*extensionsToCheck*/,
null /*factory*/);
if (initDependencies(depFile, inputPaths) && dependenciesHaveChanged() == false) {
System.out.println(
"No new compiled code. No need to convert bytecode to dalvik format.");
return;
}
System.out.println(String.format(
"Converting compiled files and external libraries into %1$s...", mOutput));
runDx(paths, mOutput, mVerbose /*showInputs*/);
// generate the dependency file.
generateDependencyFile(depFile, inputPaths, mOutput);
}
private void runDx(File input, String output, boolean showInputs) {
runDx(Collections.singleton(input), output, showInputs);
}
private void runDx(Collection<File> inputs, String output, boolean showInputs) {
ExecTask task = new ExecTask();
task.setProject(getProject());
task.setOwningTarget(getOwningTarget());
task.setExecutable(mExecutable);
task.setTaskName(getExecTaskName());
task.setFailonerror(true);
task.createArg().setValue("--dex");
if (mNoLocals) {
task.createArg().setValue("--no-locals");
}
if (mVerbose) {
task.createArg().setValue("--verbose");
}
if (mForceJumbo) {
task.createArg().setValue("--force-jumbo");
}
task.createArg().setValue("--output");
task.createArg().setValue(output);
for (File input : inputs) {
String absPath = input.getAbsolutePath();
if (showInputs) {
System.out.println("Input: " + absPath);
}
task.createArg().setValue(absPath);
}
// execute it.
task.execute();
}
@Override
protected String getExecTaskName() {
return "dx";
}
}