aboutsummaryrefslogtreecommitdiffstats
path: root/anttasks/src/com/android/ant/DependencyGraph.java
diff options
context:
space:
mode:
Diffstat (limited to 'anttasks/src/com/android/ant/DependencyGraph.java')
-rw-r--r--anttasks/src/com/android/ant/DependencyGraph.java254
1 files changed, 254 insertions, 0 deletions
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;
+ }
+}