aboutsummaryrefslogtreecommitdiffstats
path: root/anttasks/src/com/android/ant/DependencyGraph.java
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2011-09-01 15:44:41 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2011-09-01 15:44:41 -0700
commit4f033cc9ca1b81ce8d2a7ea4bd448cbd40c3bcbc (patch)
tree14bcf114c7ab72528edc2325231088bc7552be07 /anttasks/src/com/android/ant/DependencyGraph.java
parent82313c095440e82157f2894744da6bc32b59f5b0 (diff)
parent891cbf7552f98af2f0baaed069b5eb64de50c556 (diff)
downloadsdk-4f033cc9ca1b81ce8d2a7ea4bd448cbd40c3bcbc.zip
sdk-4f033cc9ca1b81ce8d2a7ea4bd448cbd40c3bcbc.tar.gz
sdk-4f033cc9ca1b81ce8d2a7ea4bd448cbd40c3bcbc.tar.bz2
Merge "Add proper dependency support in the dex step of the Ant-based build."
Diffstat (limited to 'anttasks/src/com/android/ant/DependencyGraph.java')
-rw-r--r--anttasks/src/com/android/ant/DependencyGraph.java277
1 files changed, 188 insertions, 89 deletions
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);
}
-
}