blob: 5939a92a03465aca504033c97d5d37d83ab15f41 [file] [log] [blame]
/*
* 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;
}
}