diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java b/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java index 84c480975..68b07e1a0 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java @@ -60,6 +60,7 @@ import org.apache.tools.ant.taskdefs.MatchingTask; import java.util.*; import java.io.*; +import java.net.URL; import org.apache.tools.ant.taskdefs.optional.depend.*; @@ -69,9 +70,18 @@ import org.apache.tools.ant.taskdefs.optional.depend.*; * @author Conor MacNeill */ public class Depend extends MatchingTask { + /** + * A class (struct) user to manage information about a class + */ static private class ClassFileInfo { + /** The file where the class file is stored in the file system */ public File absoluteFile; + + /** The location of the file relative to its base directory - the root + of the package namespace */ public String relativeName; + + /** The Java class name of this class */ public String className; }; @@ -99,11 +109,16 @@ public class Depend extends MatchingTask { * A map which gives information about a class */ 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. */ - private Vector outOfDateClasses; + private Hashtable outOfDateClasses; /** * 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. */ 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 { // new dependencies so need to write them out to the cache 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 { affectedClassMap = new Hashtable(); classFileInfoMap = new Hashtable(); + Hashtable dependencyMap = new Hashtable(); for (Enumeration e = getClassFiles(destPath).elements(); e.hasMoreElements(); ) { ClassFileInfo info = (ClassFileInfo)e.nextElement(); 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 // one of those, add this class into their affected classes list - for (Enumeration depEnum = dependencyList.elements(); depEnum.hasMoreElements(); ) { String dependentClass = (String)depEnum.nextElement(); @@ -212,17 +278,68 @@ public class Depend extends MatchingTask { 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();) { 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); if (affectedClasses != null) { for (Enumeration e = affectedClasses.keys(); e.hasMoreElements(); ) { @@ -232,8 +349,9 @@ public class Depend extends MatchingTask { log("Deleting file " + affectedClassInfo.absoluteFile.getPath() + " since " + className + " out of date", Project.MSG_VERBOSE); affectedClassInfo.absoluteFile.delete(); + count++; if (closure) { - deleteAffectedFiles(affectedClassName); + count += deleteAffectedFiles(affectedClassName); } else { // 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 " + "one of its inner classes was removed", Project.MSG_VERBOSE); topLevelClassInfo.absoluteFile.delete(); + count++; 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) { throw new BuildException("srcdir attribute must be set!", location); } - + if (destPath == null) { destPath = srcPath; } @@ -282,45 +402,80 @@ public class Depend extends MatchingTask { if (cache != null && cache.exists() && !cache.isDirectory()) { throw new BuildException("The cache, if specified, must point to a directory"); } - + if (cache != null && !cache.exists()) { cache.mkdirs(); } - + 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 go through and delete all class files which are affected by these files. - outOfDateClasses = new Vector(); - for (int i=0; i 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 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) { throw new BuildException(e); } @@ -344,11 +499,11 @@ public class Depend extends MatchingTask { ClassFileInfo info = (ClassFileInfo)classFileInfoMap.get(className); if (info == null) { // there was no class file. add this class to the list - outOfDateClasses.addElement(className); + outOfDateClasses.put(className, className); } else { 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) { this.closure = closure; } + + /** + * Flag to indicate whether the reverse dependency list should be dumped to debug + */ + public void setDump(boolean dump) { + this.dump = dump; + } }