diff options
Diffstat (limited to 'anttasks')
-rw-r--r-- | anttasks/src/com/android/ant/AaptExecTask.java | 28 | ||||
-rw-r--r-- | anttasks/src/com/android/ant/AidlExecTask.java | 5 | ||||
-rw-r--r-- | anttasks/src/com/android/ant/BaseTask.java | 68 | ||||
-rw-r--r-- | anttasks/src/com/android/ant/DependencyGraph.java | 277 | ||||
-rw-r--r-- | anttasks/src/com/android/ant/DexExecTask.java | 176 |
5 files changed, 440 insertions, 114 deletions
diff --git a/anttasks/src/com/android/ant/AaptExecTask.java b/anttasks/src/com/android/ant/AaptExecTask.java index 3e2350f..2c242d3 100644 --- a/anttasks/src/com/android/ant/AaptExecTask.java +++ b/anttasks/src/com/android/ant/AaptExecTask.java @@ -323,32 +323,40 @@ public final class AaptExecTask extends BaseTask { // Get whether we have libraries Object libResRef = taskProject.getReference(mProjectLibrariesResName); - // Set up our folders to check for changed files - ArrayList<File> watchPaths = new ArrayList<File>(); - // We need to watch for changes in the main project res folder + // Set up our input paths that matter for dependency checks + ArrayList<File> inputPaths = new ArrayList<File>(); + + // the project res folder is an input path of course for (Path pathList : mResources) { for (String path : pathList.list()) { - watchPaths.add(new File(path)); + inputPaths.add(new File(path)); } } - // and if libraries exist, in their res folders + + // as is its AndroidManifest.xml + if (mManifest != null) { + inputPaths.add(new File(mManifest)); + } + + // and if libraries exist, their res folders folders too. if (libResRef instanceof Path) { for (String path : ((Path)libResRef).list()) { - watchPaths.add(new File(path)); + inputPaths.add(new File(path)); } } - // If we're here to generate a .ap_ file we need to watch assets as well + + // If we're here to generate a .ap_ file we need to use assets as an input path as well. if (!generateRClass) { File assetsDir = new File(mAssets); if (mAssets != null && assetsDir.isDirectory()) { - watchPaths.add(assetsDir); + inputPaths.add(assetsDir); } } // Now we figure out what we need to do if (generateRClass) { // Check to see if our dependencies have changed. If not, then skip - if (initDependencies(mRFolder + File.separator + "R.java.d", watchPaths) + if (initDependencies(mRFolder + File.separator + "R.java.d", inputPaths) && dependenciesHaveChanged() == false) { System.out.println("No changed resources. R.java and Manifest.java untouched."); return; @@ -362,7 +370,7 @@ public final class AaptExecTask extends BaseTask { dependencyFilePath += ".d"; // Check to see if our dependencies have changed - if (initDependencies(dependencyFilePath , watchPaths) + if (initDependencies(dependencyFilePath, inputPaths) && dependenciesHaveChanged() == false) { System.out.println("No changed resources or assets. " + mApkName + " remains untouched"); diff --git a/anttasks/src/com/android/ant/AidlExecTask.java b/anttasks/src/com/android/ant/AidlExecTask.java index 98cac9b..1fd5a61 100644 --- a/anttasks/src/com/android/ant/AidlExecTask.java +++ b/anttasks/src/com/android/ant/AidlExecTask.java @@ -114,8 +114,7 @@ public class AidlExecTask extends Task { DependencyGraph graph = new DependencyGraph(depFile, null /*watchPaths*/); // get the source file. it's the first item in the pre-reqs - List<File> preReqs = graph.getPrereqs(); - File sourceFile = preReqs.get(0); + File sourceFile = graph.getFirstPrereq(); String sourceFilePath = sourceFile.getAbsolutePath(); // The gen folder may contain other dependency files not generated by aidl. @@ -126,7 +125,7 @@ public class AidlExecTask extends Task { if (sourceFiles.remove(sourceFilePath) == false) { // looks like the source file does not exist anymore! // we'll have to remove the output! - List<File> outputFiles = graph.getTargets(); + Set<File> outputFiles = graph.getTargets(); toRemove.addAll(outputFiles); // also need to remove the dep file. diff --git a/anttasks/src/com/android/ant/BaseTask.java b/anttasks/src/com/android/ant/BaseTask.java index 68dd6b5..4eb954b 100644 --- a/anttasks/src/com/android/ant/BaseTask.java +++ b/anttasks/src/com/android/ant/BaseTask.java @@ -20,8 +20,10 @@ import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import java.io.File; -import java.util.ArrayList; +import java.io.FileNotFoundException; +import java.io.PrintStream; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -59,18 +61,14 @@ public abstract class BaseTask extends Task { } } - @Override - public void execute() throws BuildException { - - } - /** - * Set up the dependency graph by passing it the location of the ".d" file + * Set up the dependency graph by passing it the location of the ".d" file, and the new input + * paths. * @param dependencyFile path to the dependency file to use - * @param watchPaths a list of folders to watch for new files + * @param the new input paths for this new compilation. * @return true if the dependency graph was successfully initialized */ - protected boolean initDependencies(String dependencyFile, ArrayList<File> watchPaths) { + protected boolean initDependencies(String dependencyFile, List<File> inputPaths) { if (mBuildType != null && mBuildType.equals(mPreviousBuildType) == false) { // we don't care about deps, we need to execute the task no matter what. return true; @@ -78,7 +76,7 @@ public abstract class BaseTask extends Task { File depFile = new File(dependencyFile); if (depFile.exists()) { - mDependencies = new DependencyGraph(dependencyFile, watchPaths); + mDependencies = new DependencyGraph(dependencyFile, inputPaths); return true; } else { return false; @@ -94,9 +92,12 @@ public abstract class BaseTask extends Task { if (mBuildType != null && mBuildType.equals(mPreviousBuildType) == false) { String execName = getExecTaskName(); if (execName == null) { - System.out.println("Current build type is different than previous build: forced task run."); + System.out.println( + "Current build type is different than previous build: forced task run."); } else { - System.out.println("Current build type is different than previous build: forced " + execName + " run."); + System.out.println( + "Current build type is different than previous build: forced " + + execName + " run."); } return true; } @@ -105,4 +106,47 @@ public abstract class BaseTask extends Task { return mDependencies.dependenciesHaveChanged(mRestrictTouchedExtensionsTo, true /*printStatus*/); } + + protected void generateDependencyFile(String depFilePath, + List<File> inputs, String outputFile) { + File file = new File(depFilePath); + + try { + PrintStream ps = new PrintStream(file); + + // write the output file. + ps.print(outputFile); + ps.println(" : \\"); + + //write the input files + int count = inputs.size(); + for (int i = 0 ; i < count ; i++) { + File input = inputs.get(i); + if (input.isDirectory()) { + writeContent(ps, input); + } else { + ps.print(input.getAbsolutePath()); + ps.println(" \\"); + } + } + + ps.close(); + } catch (FileNotFoundException e) { + new BuildException(e); + } + } + + private void writeContent(PrintStream ps, File input) { + File[] files = input.listFiles(); + if (files != null) { + for (File f : files) { + if (f.isDirectory()) { + writeContent(ps, f); + } else { + ps.print(f.getAbsolutePath()); + ps.println(" \\"); + } + } + } + } } diff --git a/anttasks/src/com/android/ant/DependencyGraph.java b/anttasks/src/com/android/ant/DependencyGraph.java index 11c09e4..7f9c65a 100644 --- a/anttasks/src/com/android/ant/DependencyGraph.java +++ b/anttasks/src/com/android/ant/DependencyGraph.java @@ -16,13 +16,15 @@ package com.android.ant; +import org.apache.tools.ant.BuildException; + import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -33,15 +35,20 @@ import java.util.Set; */ public class DependencyGraph { + private static enum DependencyStatus { + NONE, NEW_FILE, UPDATED_FILE, MISSING_FILE, ERROR; + } + // Files that we know about from the dependency file - private List<File> mTargets = Collections.emptyList(); - private List<File> mPrereqs = mTargets; + private Set<File> mTargets = Collections.emptySet(); + private Set<File> mPrereqs = mTargets; + private File mFirstPrereq = null; private boolean mMissingDepFile = false; private long mDepFileLastModified; - private final ArrayList<File> mWatchPaths; + private final List<File> mNewInputs; - public DependencyGraph(String dependencyFilePath, ArrayList<File> watchPaths) { - mWatchPaths = watchPaths; + public DependencyGraph(String dependencyFilePath, List<File> newInputPaths) { + mNewInputs = newInputPaths; parseDependencyFile(dependencyFilePath); } @@ -55,40 +62,70 @@ public class DependencyGraph { * prerequisite files have been modified since the last target generation. */ public boolean dependenciesHaveChanged(Set<String> extensionsToCheck, boolean printStatus) { - boolean missingPrereq = missingPrereqFile(); - boolean newPrereq = newPrereqFile(); - boolean missingTarget = missingTargetFile(); - boolean modPrereq = modifiedPrereq(extensionsToCheck); - - if (printStatus) { - if (mMissingDepFile) { - System.out.println("No Dependency File Found"); - } - if (missingPrereq) { - System.out.println("Found Deleted Prereq File"); - } - if (newPrereq) { - System.out.println("Found New Prereq File"); - } - if (missingTarget) { + // If no dependency file has been set up, then we'll just return true + // if we have a dependency file, we'll check to see what's been changed + if (mMissingDepFile) { + System.out.println("No Dependency File Found"); + return true; + } + + // check for missing output first + if (missingTargetFile()) { + if (printStatus) { System.out.println("Found Deleted Target File"); } - if (modPrereq) { - System.out.println("Found Modified Prereq File"); - } + return true; } - // If no dependency file has been set up, then we'll just return true - // if we have a dependency file, we'll check to see what's been changed - return mMissingDepFile || missingPrereq || newPrereq || missingTarget || modPrereq; + // get the timestamp of the oldest target. + long oldestTarget = getOutputLastModified(); + + // first look through the input folders and look for new files or modified files. + DependencyStatus status = checkInputs(extensionsToCheck, oldestTarget); + + // this can't find missing files. This is done later. + switch (status) { + case ERROR: + throw new BuildException(); + case NEW_FILE: + if (printStatus) { + System.out.println("Found new input file"); + } + return true; + case UPDATED_FILE: + if (printStatus) { + System.out.println("Found modified input file"); + } + return true; + } + + // now do a full check on the remaining files. + status = checkPrereqFiles(extensionsToCheck, oldestTarget); + // this can't find new input files. This is done above. + switch (status) { + case ERROR: + throw new BuildException(); + case MISSING_FILE: + if (printStatus) { + System.out.println("Found deleted input file"); + } + return true; + case UPDATED_FILE: + if (printStatus) { + System.out.println("Found modified input file"); + } + return true; + } + + return false; } - public List<File> getTargets() { - return mTargets; + public Set<File> getTargets() { + return Collections.unmodifiableSet(mTargets); } - public List<File> getPrereqs() { - return mPrereqs; + public File getFirstPrereq() { + return mFirstPrereq; } /** @@ -144,79 +181,157 @@ public class DependencyGraph { prereqs = files[1].trim().split(" "); } - mTargets = new ArrayList<File>(targets.length); + mTargets = new HashSet<File>(targets.length); for (String path : targets) { if (path.length() > 0) { mTargets.add(new File(path)); } } - mTargets = Collections.unmodifiableList(mTargets); - mPrereqs = new ArrayList<File>(prereqs.length); + + mPrereqs = new HashSet<File>(prereqs.length); for (String path : prereqs) { if (path.length() > 0) { - mPrereqs.add(new File(path)); + File f = new File(path); + if (mFirstPrereq == null) { + mFirstPrereq = f; + } + mPrereqs.add(f); } } - mPrereqs = Collections.unmodifiableList(mPrereqs); } /** - * Check all the folders we know about to see if there have been new - * files added to them. - * @return true if a new file is encountered in the dependency folders + * Check all the input files and folders to see if there have been new + * files added to them or if any of the existing files have been modified. + * + * This looks at the input paths, not at the list of known prereq. Therefore this + * will not find missing files. It will however remove processed files from the + * prereq file list so that we can process those in a 2nd step. + * + * This should be followed by a call to {@link #checkPrereqFiles(long)} which + * will process the remaining files in the prereq list. + * + * If a change is found, this will return immediatly with either + * {@link DependencyStatus#NEW_FILE} or {@link DependencyStatus#UPDATED_FILE}. + * + * @param extensionsToCheck a set of extensions. Only files with an extension in this set will + * be considered for a modification check. All deleted/created files will still be + * checked. If this is null, all files will be checked for modification date + * @param oldestTarget the timestamp of the oldest output file to compare against. + * + * @return the status of the file in the watched folders. + * */ - private boolean newPrereqFile() { - if (mWatchPaths != null) { - for (File dir : mWatchPaths) { - if (newFileInTree(dir)) { - return true; + private DependencyStatus checkInputs(Set<String> extensionsToCheck, long oldestTarget) { + if (mNewInputs != null) { + for (File input : mNewInputs) { + if (input.isDirectory()) { + DependencyStatus status = checkInputFolder(input, extensionsToCheck, + oldestTarget); + if (status != DependencyStatus.NONE) { + return status; + } + } else if (input.isFile()) { + DependencyStatus status = checkInputFile(input, extensionsToCheck, + oldestTarget); + if (status != DependencyStatus.NONE) { + return status; + } } } } + // If we make it all the way through our directories we're good. - return false; + return DependencyStatus.NONE; } /** * Check all the files in the tree under root and check to see if the files are - * listed under the dependencies. Recurses into subdirs. - * @param root the root of the file tree to search through - * @return true if a file is encountered in the tree that is not in our list of prereqs + * listed under the dependencies, or if they have been modified. Recurses into subdirs. + * + * @param rootFolder the folder to search through. + * @param extensionsToCheck a set of extensions. Only files with an extension in this set will + * be considered for a modification check. All deleted/created files will still be + * checked. If this is null, all files will be checked for modification date + * @param oldestTarget the timestamp of the oldest output file to compare against. + * + * @return the status of the file in the folder. */ - private boolean newFileInTree(File root) { - File[] files = root.listFiles(); + private DependencyStatus checkInputFolder(File rootFolder, Set<String> extensionsToCheck, + long oldestTarget) { + File[] files = rootFolder.listFiles(); if (files == null) { - System.err.println("ERROR " + root.toString() + " is not a dir or can't be read"); - return false; + System.err.println("ERROR " + rootFolder.toString() + " is not a dir or can't be read"); + return DependencyStatus.ERROR; } // Loop through files in this folder for (File file : files) { // If this is a directory, recurse into it if (file.isDirectory()) { - if (newFileInTree(file)) { - return true; + DependencyStatus status = checkInputFolder(file, extensionsToCheck, oldestTarget); + if (status != DependencyStatus.NONE) { + return status; + } + } else if (file.isFile()) { + DependencyStatus status = checkInputFile(file, extensionsToCheck, oldestTarget); + if (status != DependencyStatus.NONE) { + return status; } - } else if (file.isFile() && mPrereqs.contains(file) == false) { - return true; } } // If we got to here then we didn't find anything interesting - return false; + return DependencyStatus.NONE; + } + + private DependencyStatus checkInputFile(File file, Set<String> extensionsToCheck, + long oldestTarget) { + // if it's a file, remove it from the list of prereqs. + // This way if files in this folder don't trigger a build we'll have less + // files to go through manually + if (mPrereqs.remove(file) == false) { + // turns out this is a new file! + return DependencyStatus.NEW_FILE; + } else { + // check the time stamp on this file if it's a file we care about based on the + // list of extensions to check. + if (extensionsToCheck == null || extensionsToCheck.contains(getExtension(file))) { + if (file.lastModified() > oldestTarget) { + return DependencyStatus.UPDATED_FILE; + } + } + } + + return DependencyStatus.NONE; } /** - * Check all the prereq files we know about to make sure they're still there - * @return true if any of the prereq files are missing. + * Check all the prereq files we know about to make sure they're still there, or that they + * haven't been modified since the last build. + * + * @param extensionsToCheck a set of extensions. Only files with an extension in this set will + * be considered for a modification check. All deleted/created files will still be + * checked. If this is null, all files will be checked for modification date + * + * @return the status of the files */ - private boolean missingPrereqFile() { + private DependencyStatus checkPrereqFiles(Set<String> extensionsToCheck, long oldestTarget) { // Loop through our prereq files and make sure they still exist for (File prereq : mPrereqs) { if (prereq.exists() == false) { - return true; + return DependencyStatus.MISSING_FILE; + } + + // check the time stamp on this file if it's a file we care about based on the + // list of extensions to check. + if (extensionsToCheck == null || extensionsToCheck.contains(getExtension(prereq))) { + if (prereq.lastModified() > oldestTarget) { + return DependencyStatus.UPDATED_FILE; + } } } - // If we get this far, then all our targets are okay - return false; + + // If we get this far, then all our prereq are okay + return DependencyStatus.NONE; } /** @@ -235,12 +350,10 @@ public class DependencyGraph { } /** - * Check to see if any of the prerequisite files have been modified since - * the targets were last updated. - * @return true if the latest prerequisite modification is after the oldest - * target modification. + * Returns the earliest modification time stamp from all the output targets. If there + * are no known target, the dependency file time stamp is returned. */ - private boolean modifiedPrereq(Set<String> extensionsToCheck) { + private long getOutputLastModified() { // Find the oldest target long oldestTarget = Long.MAX_VALUE; // if there's no output, then compare to the time of the dependency file. @@ -254,20 +367,7 @@ public class DependencyGraph { } } - // Find the newest prerequisite - long newestPrereq = 0; - for (File prereq : mPrereqs) { - // If we have a list of extensions that we need to restrict ourselves to, only - // consider this file if it has that extension. - if (extensionsToCheck == null || extensionsToCheck.contains(getExtension(prereq))) { - if (prereq.lastModified() > newestPrereq) { - newestPrereq = prereq.lastModified(); - } - } - } - - // And return the comparison - return newestPrereq > oldestTarget; + return oldestTarget; } /** @@ -296,11 +396,11 @@ public class DependencyGraph { } /** - * Gets the extension (if present) on a file by looking at the filename - * @param file the file to get the extension of - * @return the extension if present, or the empty string if the filename doesn't have - * and extension. - */ + * Gets the extension (if present) on a file by looking at the filename + * @param file the file to get the extension of + * @return the extension if present, or the empty string if the filename doesn't have + * and extension. + */ private static String getExtension(File file) { String filename = file.getName(); if (filename.lastIndexOf('.') == -1) { @@ -309,5 +409,4 @@ public class DependencyGraph { // Don't include the leading '.' in the extension return filename.substring(filename.lastIndexOf('.') + 1); } - } diff --git a/anttasks/src/com/android/ant/DexExecTask.java b/anttasks/src/com/android/ant/DexExecTask.java new file mode 100644 index 0000000..182ef84 --- /dev/null +++ b/anttasks/src/com/android/ant/DexExecTask.java @@ -0,0 +1,176 @@ +/* + * 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 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.Iterator; +import java.util.List; + +/** + * Custom task to execute dx while handling dependencies. + */ +public class DexExecTask extends BaseTask { + + private String mExecutable; + private String mOutput; + private boolean mVerbose = false; + private boolean mNoLocals = 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); + } + + /** + * Sets the value of the "nolocals" attribute. + * @param verbose the value. + */ + public void setNoLocals(boolean nolocals) { + mNoLocals = nolocals; + } + + /** + * 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; + } + + + @Override + public void execute() throws BuildException { + + // get all input paths + List<File> inputPaths = new ArrayList<File>(); + if (mPathInputs != null) { + for (Path pathList : mPathInputs) { + for (String path : pathList.list()) { + inputPaths.add(new File(path)); + } + } + } + + if (mFileSetInputs != null) { + for (FileSet fs : mFileSetInputs) { + Iterator<?> iter = fs.iterator(); + while (iter.hasNext()) { + FileResource fr = (FileResource) iter.next(); + inputPaths.add(fr.getFile()); + } + } + } + + // figure out the path to the dependency file. + String depFile = mOutput + ".d"; + + 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)); + + 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"); + } + + task.createArg().setValue("--output"); + task.createArg().setValue(mOutput); + + + for (File f :inputPaths) { + task.createArg().setValue(f.getAbsolutePath()); + } + + // execute it. + task.execute(); + + // generate the dependency file. + generateDependencyFile(depFile, inputPaths, mOutput); + } + + @Override + protected String getExecTaskName() { + return "dx"; + } +} |