diff options
Diffstat (limited to 'anttasks/src/com/android/ant/DependencyGraph.java')
-rw-r--r-- | anttasks/src/com/android/ant/DependencyGraph.java | 277 |
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); } - } |