diff options
Diffstat (limited to 'anttasks/src/com/android/ant')
-rw-r--r-- | anttasks/src/com/android/ant/AaptExecLoopTask.java | 36 | ||||
-rw-r--r-- | anttasks/src/com/android/ant/BaseTask.java | 70 | ||||
-rw-r--r-- | anttasks/src/com/android/ant/DependencyGraph.java | 254 |
3 files changed, 355 insertions, 5 deletions
diff --git a/anttasks/src/com/android/ant/AaptExecLoopTask.java b/anttasks/src/com/android/ant/AaptExecLoopTask.java index 725f6b6..ed0277a 100644 --- a/anttasks/src/com/android/ant/AaptExecLoopTask.java +++ b/anttasks/src/com/android/ant/AaptExecLoopTask.java @@ -18,7 +18,6 @@ package com.android.ant; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; -import org.apache.tools.ant.Task; import org.apache.tools.ant.taskdefs.ExecTask; import org.apache.tools.ant.types.Path; @@ -51,7 +50,7 @@ import java.util.ArrayList; * <tr><td></td><td></td><td></td></tr> * </table> */ -public final class AaptExecLoopTask extends Task { +public final class AaptExecLoopTask extends BaseTask { /** * Class representing a <nocompress> node in the main task XML. @@ -259,8 +258,10 @@ public final class AaptExecLoopTask extends Task { // more R classes need to be created for libraries. if (mRFolder != null && new File(mRFolder).isDirectory()) { libPkgProp = taskProject.getProperty(AntConstants.PROP_PROJECT_LIBS_PKG); - // Replace ";" with ":" since that's what aapt expects - libPkgProp = libPkgProp.replace(';', ':'); + if (libPkgProp != null) { + // Replace ";" with ":" since that's what aapt expects + libPkgProp = libPkgProp.replace(';', ':'); + } } // Call aapt. If there are libraries, we'll pass a non-null string of libs. callAapt(libPkgProp); @@ -278,7 +279,31 @@ public final class AaptExecLoopTask extends Task { final boolean generateRClass = mRFolder != null && new File(mRFolder).isDirectory(); + // Get whether we have libraries + Object libResRef = taskProject.getReference(AntConstants.PROP_PROJECT_LIBS_RES_REF); + if (generateRClass) { + // If the only reason we're here is to generate R.java and that doesn't need updating + // we can skip what comes next. First we grab the dependency file. + // Then query to see if an update is needed. + ArrayList<File> watchPaths = new ArrayList<File>(); + // We need to watch for changes in the main project res folder + for (Path pathList : mResources) { + for (String path : pathList.list()) { + watchPaths.add(new File(path)); + } + } + // and if libraries exist, in their res folders + if (libResRef instanceof Path) { + for (String path : ((Path)libResRef).list()) { + watchPaths.add(new File(path)); + } + } + if (initDependencies(mRFolder + File.separator + "R.d", watchPaths) + && dependenciesHaveChanged() == false) { + System.out.println("No changed resources. R.java and Manifest.java untouched."); + return; + } } else if (mResourceFilter == null) { System.out.println("Creating full resource package..."); } else { @@ -347,7 +372,6 @@ public final class AaptExecLoopTask extends Task { } // if the project contains libraries, force auto-add-overlay - Object libResRef = taskProject.getReference(AntConstants.PROP_PROJECT_LIBS_RES_REF); if (libResRef != null) { task.createArg().setValue("--auto-add-overlay"); } @@ -417,6 +441,8 @@ public final class AaptExecLoopTask extends Task { if (generateRClass) { task.createArg().setValue("-J"); task.createArg().setValue(mRFolder); + // Use dependency generation + task.createArg().setValue("--generate-dependencies"); } // final setup of the task diff --git a/anttasks/src/com/android/ant/BaseTask.java b/anttasks/src/com/android/ant/BaseTask.java new file mode 100644 index 0000000..2126d3f --- /dev/null +++ b/anttasks/src/com/android/ant/BaseTask.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2009 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.Task; + +import java.io.File; +import java.util.ArrayList; + +/** + * A base class for the ant task that contains logic for handling dependency files + */ +public class BaseTask extends Task { + + private DependencyGraph mDependencies; + + /* + * (non-Javadoc) + * + * Executes the loop. Based on the values inside default.properties, this will + * create alternate temporary ap_ files. + * + * @see org.apache.tools.ant.Task#execute() + */ + @Override + public void execute() throws BuildException { + + } + + /** + * Set up the dependency graph by passing it the location of the ".d" file + * @param dependencyFile path to the dependency file to use + * @param watchPaths a list of folders to watch for new files + * @return true if the dependency graph was sucessfully initialized + */ + protected boolean initDependencies(String dependencyFile, ArrayList<File> watchPaths) { + File depFile = new File(dependencyFile); + if (depFile.exists()) { + mDependencies = new DependencyGraph(dependencyFile, watchPaths); + return true; + } else { + return false; + } + } + + /** + * Wrapper check to see if we need to execute this task at all + * @return true if the DependencyGraph reports that our prereqs or targets + * have changed since the last run + */ + protected boolean dependenciesHaveChanged() { + assert mDependencies != null : "Dependencies have not been initialized"; + return mDependencies.dependenciesHaveChanged(); + } +} diff --git a/anttasks/src/com/android/ant/DependencyGraph.java b/anttasks/src/com/android/ant/DependencyGraph.java new file mode 100644 index 0000000..5939a92 --- /dev/null +++ b/anttasks/src/com/android/ant/DependencyGraph.java @@ -0,0 +1,254 @@ +/* + * 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 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.Set; + +/** + * This class takes care of dependency tracking for all targets and prerequisites listed in + * a single dependency file. A dependency graph always has a dependency file associated with it + * for the duration of its lifetime + */ +public class DependencyGraph { + + // Files that we know about from the dependency file + private Set<File> mTargets = Collections.emptySet(); + private Set<File> mPrereqs = mTargets; + private ArrayList<File> mWatchPaths; + + public DependencyGraph(String dependencyFilePath, ArrayList<File> watchPaths) { + mWatchPaths = watchPaths; + parseDependencyFile(dependencyFilePath); + } + + /** + * Check all the dependencies to see if anything has changed. + * @return true if new prerequisites have appeared, target files are missing or if + * prerequisite files have been modified since the last target generation. + */ + public boolean dependenciesHaveChanged() { + boolean noFile = (mTargets.size() == 0); + boolean missingPrereq = missingPrereqFile(); + boolean newPrereq = newPrereqFile(); + boolean missingTarget = missingTargetFile(); + boolean modPrereq = modifiedPrereq(); + + if (noFile) { + 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) { + System.out.println("Found Deleted Target File"); + } + if (modPrereq) { + System.out.println("Found Modified Prereq File"); + } + // 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 noFile || missingPrereq || newPrereq || missingTarget || modPrereq; + } + + /** + * Parses the given dependency file and stores the file paths + * + * @param dependencyFilePath the dependency file + */ + private void parseDependencyFile(String dependencyFilePath) { + // Read in our dependency file + String content = readFile(dependencyFilePath); + if (content == null) { + System.err.println("ERROR: Couldn't read " + dependencyFilePath); + return; + } + + // The format is something like: + // output1 output2 [...]: dep1 dep2 [...] + // expect it's likely split on several lines. So let's move it back on a single line + // first + String[] lines = content.toString().split("\n"); + StringBuilder sb = new StringBuilder(content.length()); + for (String line : lines) { + line = line.trim(); + if (line.endsWith("\\")) { + line = line.substring(0, line.length() - 1); + } + sb.append(line); + } + + // split the left and right part + String[] files = sb.toString().split(":"); + + // get the target files: + String[] targets = files[0].trim().split(" "); + + String[] prereqs = {}; + // Check to make sure our dependency file is okay + if (files.length < 1) { + System.err.println( + "Warning! Dependency file does not list any prerequisites after ':' "); + } else { + // and the prerequisite files: + prereqs = files[1].trim().split(" "); + } + + mTargets = new HashSet<File>(targets.length); + for (String path : targets) { + mTargets.add(new File(path)); + } + mPrereqs = new HashSet<File>(prereqs.length); + for (String path : prereqs) { + mPrereqs.add(new File(path)); + } + } + + /** + * 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 + */ + private boolean newPrereqFile() { + for (File dir : mWatchPaths) { + if (newFileInTree(dir)) { + return true; + } + } + // If we make it all the way through our directories we're good. + return false; + } + + /** + * 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 + */ + private boolean newFileInTree(File root) { + File[] files = root.listFiles(); + if (files == null) { + System.err.println("ERROR " + root.toString() + " is not a dir or can't be read"); + return false; + } + // 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; + } + } else if (file.isFile() && mPrereqs.contains(file) == false) { + return true; + } + } + // If we got to here then we didn't find anything interesting + return false; + } + + /** + * 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. + */ + private boolean missingPrereqFile() { + // Loop through our prereq files and make sure they still exist + for (File prereq : mPrereqs) { + if (prereq.exists() == false) { + return true; + } + } + // If we get this far, then all our targets are okay + return false; + } + + /** + * Check all the target files we know about to make sure they're still there + * @return true if any of the target files are missing. + */ + private boolean missingTargetFile() { + // Loop through our target files and make sure they still exist + for (File target : mTargets) { + if (target.exists() == false) { + return true; + } + } + // If we get this far, then all our targets are okay + return false; + } + + /** + * 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. + */ + private boolean modifiedPrereq() { + // Find the oldest target + long oldestTarget = Long.MAX_VALUE; + for (File target : mTargets) { + if (target.lastModified() < oldestTarget) { + oldestTarget = target.lastModified(); + } + } + + // Find the newest prerequisite + long newestPrereq = 0; + for (File prereq : mPrereqs) { + if (prereq.lastModified() > newestPrereq) { + newestPrereq = prereq.lastModified(); + } + } + + // And return the comparison + return newestPrereq > oldestTarget; + } + + /** + * Reads and returns the content of a text file. + * @param filepath the file path to the text file + * @return null if the file could not be read + */ + private static String readFile(String filepath) { + try { + FileInputStream fStream = new FileInputStream(filepath); + if (fStream != null) { + BufferedReader reader = new BufferedReader(new InputStreamReader(fStream)); + + String line; + StringBuilder total = new StringBuilder(reader.readLine()); + while ((line = reader.readLine()) != null) { + total.append('\n'); + total.append(line); + } + return total.toString(); + } + } catch (IOException e) { + // we'll just return null + } + return null; + } +} |