Browse Source

Upgrade <depend> to check dependencies on jars in the classpath. You

need to provide a classpath to <depend> to trigger this behaviour.

Doesn't currently handle classes in the classpath.

PR:	2663


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@269384 13f79535-47bb-0310-9956-ffa450edef68
master
Conor MacNeill 24 years ago
parent
commit
798eedafd9
1 changed files with 194 additions and 32 deletions
  1. +194
    -32
      src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java

+ 194
- 32
src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java View File

@@ -60,6 +60,7 @@ import org.apache.tools.ant.taskdefs.MatchingTask;


import java.util.*; import java.util.*;
import java.io.*; import java.io.*;
import java.net.URL;


import org.apache.tools.ant.taskdefs.optional.depend.*; import org.apache.tools.ant.taskdefs.optional.depend.*;


@@ -69,9 +70,18 @@ import org.apache.tools.ant.taskdefs.optional.depend.*;
* @author Conor MacNeill * @author Conor MacNeill
*/ */
public class Depend extends MatchingTask { public class Depend extends MatchingTask {
/**
* A class (struct) user to manage information about a class
*/
static private class ClassFileInfo { static private class ClassFileInfo {
/** The file where the class file is stored in the file system */
public File absoluteFile; public File absoluteFile;
/** The location of the file relative to its base directory - the root
of the package namespace */
public String relativeName; public String relativeName;
/** The Java class name of this class */
public String className; public String className;
}; };


@@ -99,11 +109,16 @@ public class Depend extends MatchingTask {
* A map which gives information about a class * A map which gives information about a class
*/ */
private Hashtable classFileInfoMap; private Hashtable classFileInfoMap;
/**
* A map which gives the list of jars a class depends upon
*/
private Hashtable classJarDependencies;


/** /**
* The list of classes which are out of date. * The list of classes which are out of date.
*/ */
private Vector outOfDateClasses;
private Hashtable outOfDateClasses;
/** /**
* indicates that the dependency relationships should be extended * indicates that the dependency relationships should be extended
@@ -111,7 +126,51 @@ public class Depend extends MatchingTask {
* affects B abd B directly affects C, then A indirectly affects C. * affects B abd B directly affects C, then A indirectly affects C.
*/ */
private boolean closure = false; private boolean closure = false;
/**
* Flag which controls whether the reversed dependencies should be dumped
* to the log
*/
private boolean dump = false;

private Path compileClasspath;

/**
* Set the classpath to be used for this compilation.
*/
public void setClasspath(Path classpath) {
if (compileClasspath == null) {
compileClasspath = classpath;
} else {
compileClasspath.append(classpath);
}
}

/** Gets the classpath to be used for this compilation. */
public Path getClasspath() {
return compileClasspath;
}

/**
* Maybe creates a nested classpath element.
*/
public Path createClasspath() {
if (compileClasspath == null) {
compileClasspath = new Path(project);
}
return compileClasspath.createPath();
}

/**
* Adds a reference to a CLASSPATH defined elsewhere.
*/
public void setClasspathRef(Reference r) {
createClasspath().setRefid(r);
}


private void writeDependencyList(File depFile, Vector dependencyList) throws IOException { private void writeDependencyList(File depFile, Vector dependencyList) throws IOException {
// new dependencies so need to write them out to the cache // new dependencies so need to write them out to the cache
PrintWriter pw = null; PrintWriter pw = null;
@@ -154,9 +213,16 @@ public class Depend extends MatchingTask {
} }




/**
* Determine the dependencies between classes.
*
* Class dependencies are determined by examining the class references in a class file
* to other classes
*/
private void determineDependencies() throws IOException { private void determineDependencies() throws IOException {
affectedClassMap = new Hashtable(); affectedClassMap = new Hashtable();
classFileInfoMap = new Hashtable(); classFileInfoMap = new Hashtable();
Hashtable dependencyMap = new Hashtable();
for (Enumeration e = getClassFiles(destPath).elements(); e.hasMoreElements(); ) { for (Enumeration e = getClassFiles(destPath).elements(); e.hasMoreElements(); ) {
ClassFileInfo info = (ClassFileInfo)e.nextElement(); ClassFileInfo info = (ClassFileInfo)e.nextElement();
log("Adding class info for " + info.className, Project.MSG_DEBUG); log("Adding class info for " + info.className, Project.MSG_DEBUG);
@@ -197,9 +263,9 @@ public class Depend extends MatchingTask {
} }
} }
dependencyMap.put(info.className, dependencyList);
// This class depends on each class in the dependency list. For each // This class depends on each class in the dependency list. For each
// one of those, add this class into their affected classes list // one of those, add this class into their affected classes list
for (Enumeration depEnum = dependencyList.elements(); depEnum.hasMoreElements(); ) { for (Enumeration depEnum = dependencyList.elements(); depEnum.hasMoreElements(); ) {
String dependentClass = (String)depEnum.nextElement(); String dependentClass = (String)depEnum.nextElement();
@@ -212,17 +278,68 @@ public class Depend extends MatchingTask {
affectedClasses.put(info.className, info); affectedClasses.put(info.className, info);
} }
} }
classJarDependencies = null;
if (compileClasspath != null) {
// now determine which jars each class depends upon
classJarDependencies = new Hashtable();
AntClassLoader loader = new AntClassLoader(getProject(), compileClasspath);
Hashtable jarFileCache = new Hashtable();
Object nullJarFile = new Object();
for (Enumeration e = dependencyMap.keys(); e.hasMoreElements();) {
String className = (String)e.nextElement();
Vector dependencyList = (Vector)dependencyMap.get(className);
Hashtable jarDependencies = new Hashtable();
classJarDependencies.put(className, jarDependencies);
for (Enumeration e2 = dependencyList.elements(); e2.hasMoreElements();) {
String dependency =(String)e2.nextElement();
Object jarFileObject = jarFileCache.get(dependency);
if (jarFileObject == null) {
jarFileObject = nullJarFile;
if (!dependency.startsWith("java.") && !dependency.startsWith("javax.")) {
URL classURL = loader.getResource(dependency.replace('.', '/') + ".class");
if (classURL != null) {
String jarFilePath = classURL.getFile();
if (jarFilePath.startsWith("file:")) {
int classMarker = jarFilePath.indexOf('!');
jarFilePath = jarFilePath.substring(5, classMarker);
}
jarFileObject = new File(jarFilePath);
log("Class " + className +
" depends on " + jarFileObject +
" due to " + dependency, Project.MSG_DEBUG);
}
}
jarFileCache.put(dependency, jarFileObject);
}
if (jarFileObject != null && jarFileObject != nullJarFile) {
// we need to add this jar to the list for this class.
File jarFile = (File)jarFileObject;
jarDependencies.put(jarFile, jarFile);
}
}
}
}
} }
private void deleteAllAffectedFiles() {
private int deleteAllAffectedFiles() {
int count = 0;
for (Enumeration e = outOfDateClasses.elements(); e.hasMoreElements();) { for (Enumeration e = outOfDateClasses.elements(); e.hasMoreElements();) {
String className = (String)e.nextElement(); String className = (String)e.nextElement();
deleteAffectedFiles(className);
}
count += deleteAffectedFiles(className);
ClassFileInfo classInfo = (ClassFileInfo)classFileInfoMap.get(className);
if (classInfo != null && classInfo.absoluteFile.exists()) {
classInfo.absoluteFile.delete();
count++;
}
}
return count;
} }
private void deleteAffectedFiles(String className) {
private int deleteAffectedFiles(String className) {
int count = 0;

Hashtable affectedClasses = (Hashtable)affectedClassMap.get(className); Hashtable affectedClasses = (Hashtable)affectedClassMap.get(className);
if (affectedClasses != null) { if (affectedClasses != null) {
for (Enumeration e = affectedClasses.keys(); e.hasMoreElements(); ) { for (Enumeration e = affectedClasses.keys(); e.hasMoreElements(); ) {
@@ -232,8 +349,9 @@ public class Depend extends MatchingTask {
log("Deleting file " + affectedClassInfo.absoluteFile.getPath() + " since " + log("Deleting file " + affectedClassInfo.absoluteFile.getPath() + " since " +
className + " out of date", Project.MSG_VERBOSE); className + " out of date", Project.MSG_VERBOSE);
affectedClassInfo.absoluteFile.delete(); affectedClassInfo.absoluteFile.delete();
count++;
if (closure) { if (closure) {
deleteAffectedFiles(affectedClassName);
count += deleteAffectedFiles(affectedClassName);
} }
else { else {
// without closure we may delete an inner class but not the // without closure we may delete an inner class but not the
@@ -251,8 +369,9 @@ public class Depend extends MatchingTask {
log("Deleting file " + topLevelClassInfo.absoluteFile.getPath() + " since " + log("Deleting file " + topLevelClassInfo.absoluteFile.getPath() + " since " +
"one of its inner classes was removed", Project.MSG_VERBOSE); "one of its inner classes was removed", Project.MSG_VERBOSE);
topLevelClassInfo.absoluteFile.delete(); topLevelClassInfo.absoluteFile.delete();
count++;
if (closure) { if (closure) {
deleteAffectedFiles(topLevelClassName);
count += deleteAffectedFiles(topLevelClassName);
} }
} }
} }
@@ -260,6 +379,7 @@ public class Depend extends MatchingTask {
} }
} }
} }
return count;
} }


/** /**
@@ -274,7 +394,7 @@ public class Depend extends MatchingTask {
if (srcPathList.length == 0) { if (srcPathList.length == 0) {
throw new BuildException("srcdir attribute must be set!", location); throw new BuildException("srcdir attribute must be set!", location);
} }
if (destPath == null) { if (destPath == null) {
destPath = srcPath; destPath = srcPath;
} }
@@ -282,45 +402,80 @@ public class Depend extends MatchingTask {
if (cache != null && cache.exists() && !cache.isDirectory()) { if (cache != null && cache.exists() && !cache.isDirectory()) {
throw new BuildException("The cache, if specified, must point to a directory"); throw new BuildException("The cache, if specified, must point to a directory");
} }
if (cache != null && !cache.exists()) { if (cache != null && !cache.exists()) {
cache.mkdirs(); cache.mkdirs();
} }
determineDependencies(); determineDependencies();
/*
for (Enumeration e = affectedClassMap.keys(); e.hasMoreElements(); ) {
String className = (String)e.nextElement();
log("Class " + className + " affects:", Project.MSG_DEBUG);
Hashtable affectedClasses = (Hashtable)affectedClassMap.get(className);
for (Enumeration e2 = affectedClasses.keys(); e2.hasMoreElements(); ) {
String affectedClass = (String)e2.nextElement();
ClassFileInfo info = (ClassFileInfo)affectedClasses.get(affectedClass);
log(" " + affectedClass + " in " + info.absoluteFile.getPath(), Project.MSG_DEBUG);
if (dump) {
log("Reverse Dependency Dump for " + affectedClassMap.size() +
" classes:", Project.MSG_DEBUG);
for (Enumeration e = affectedClassMap.keys(); e.hasMoreElements(); ) {
String className = (String)e.nextElement();
log(" Class " + className + " affects:", Project.MSG_DEBUG);
Hashtable affectedClasses = (Hashtable)affectedClassMap.get(className);
for (Enumeration e2 = affectedClasses.keys(); e2.hasMoreElements(); ) {
String affectedClass = (String)e2.nextElement();
ClassFileInfo info = (ClassFileInfo)affectedClasses.get(affectedClass);
log(" " + affectedClass + " in " + info.absoluteFile.getPath(), Project.MSG_DEBUG);
}
} }
if (classJarDependencies != null) {
log("Jar dependencies (Forward):", Project.MSG_DEBUG);
for (Enumeration e = classJarDependencies.keys(); e.hasMoreElements();) {
String className = (String)e.nextElement();
log(" Class " + className + " depends on:", Project.MSG_DEBUG);
Hashtable jarDependencies = (Hashtable)classJarDependencies.get(className);
for (Enumeration e2 = jarDependencies.elements(); e2.hasMoreElements();) {
File jarFile = (File)e2.nextElement();
log(" " + jarFile.getPath(), Project.MSG_DEBUG);
}
}
}
} }
*/
// we now need to scan for out of date files. When we have the list // we now need to scan for out of date files. When we have the list
// we go through and delete all class files which are affected by these files. // we go through and delete all class files which are affected by these files.
outOfDateClasses = new Vector();
for (int i=0; i<srcPathList.length; i++) {
outOfDateClasses = new Hashtable();
for (int i=0; i < srcPathList.length; i++) {
File srcDir = (File)project.resolveFile(srcPathList[i]); File srcDir = (File)project.resolveFile(srcPathList[i]);
if (srcDir.exists()) { if (srcDir.exists()) {
DirectoryScanner ds = this.getDirectoryScanner(srcDir); DirectoryScanner ds = this.getDirectoryScanner(srcDir);
String[] files = ds.getIncludedFiles(); String[] files = ds.getIncludedFiles();
scanDir(srcDir, files); scanDir(srcDir, files);
} }
} }

// now check jar dependencies
if (classJarDependencies != null) {
for (Enumeration e = classJarDependencies.keys(); e.hasMoreElements();) {
String className = (String)e.nextElement();
if (!outOfDateClasses.containsKey(className)) {
ClassFileInfo info = (ClassFileInfo)classFileInfoMap.get(className);
Hashtable jarDependencies = (Hashtable)classJarDependencies.get(className);
for (Enumeration e2 = jarDependencies.elements(); e2.hasMoreElements();) {
File jarFile = (File)e2.nextElement();
if (jarFile.lastModified() > info.absoluteFile.lastModified()) {
log("Class " + className +
" is out of date with respect to " + jarFile, Project.MSG_DEBUG);
outOfDateClasses.put(className, className);
break;
}
}
}
}
}
// we now have a complete list of classes which are out of date // we now have a complete list of classes which are out of date
// We scan through the affected classes, deleting any affected classes. // We scan through the affected classes, deleting any affected classes.
deleteAllAffectedFiles();
int count = deleteAllAffectedFiles();
log("Duration = " + (System.currentTimeMillis() - start));
long duration = (System.currentTimeMillis() - start) / 1000;
log("Deleted " + count + " out of date files in " + duration + " seconds");
} catch (Exception e) { } catch (Exception e) {
throw new BuildException(e); throw new BuildException(e);
} }
@@ -344,11 +499,11 @@ public class Depend extends MatchingTask {
ClassFileInfo info = (ClassFileInfo)classFileInfoMap.get(className); ClassFileInfo info = (ClassFileInfo)classFileInfoMap.get(className);
if (info == null) { if (info == null) {
// there was no class file. add this class to the list // there was no class file. add this class to the list
outOfDateClasses.addElement(className);
outOfDateClasses.put(className, className);
} }
else { else {
if (srcFile.lastModified() > info.absoluteFile.lastModified()) { if (srcFile.lastModified() > info.absoluteFile.lastModified()) {
outOfDateClasses.addElement(className);
outOfDateClasses.put(className, className);
} }
} }
} }
@@ -432,5 +587,12 @@ public class Depend extends MatchingTask {
public void setClosure(boolean closure) { public void setClosure(boolean closure) {
this.closure = closure; this.closure = closure;
} }

/**
* Flag to indicate whether the reverse dependency list should be dumped to debug
*/
public void setDump(boolean dump) {
this.dump = dump;
}
} }



Loading…
Cancel
Save