/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You 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
 *
 *      https://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 org.apache.tools.ant.taskdefs.optional.depend;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;

import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.taskdefs.rmic.DefaultRmicAdapter;
import org.apache.tools.ant.taskdefs.rmic.WLRmic;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.resources.FileProvider;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.depend.DependencyAnalyzer;

Generates a dependency file for a given set of classes.
/** * Generates a dependency file for a given set of classes. * */
public class Depend extends MatchingTask { private static final int ONE_SECOND = 1000;
A class (struct) user to manage information about a class
/** * A class (struct) user to manage information about a class * */
private static class ClassFileInfo {
The file where the class file is stored in the file system
/** The file where the class file is stored in the file system */
private File absoluteFile;
The Java class name of this class
/** The Java class name of this class */
private String className;
The source File containing this class
/** The source File containing this class */
private File sourceFile;
if user has been warned about this file not having a source file
/** if user has been warned about this file not having a source file */
private boolean isUserWarned = false; }
The path where source files exist
/** The path where source files exist */
private Path srcPath;
The path where compiled class files exist.
/** The path where compiled class files exist. */
private Path destPath;
The directory which contains the dependency cache.
/** The directory which contains the dependency cache. */
private File cache;
A map which gives for every class a list of the class which it affects.
/** * A map which gives for every class a list of the class which it * affects. */
private Map<String, Map<String, ClassFileInfo>> affectedClassMap;
A map which gives information about a class
/** A map which gives information about a class */
private Map<String, ClassFileInfo> classFileInfoMap;
A map which gives the list of jars and classes from the classpath that a class depends upon
/** * A map which gives the list of jars and classes from the classpath * that a class depends upon */
private Map<String, Set<File>> classpathDependencies;
The list of classes which are out of date.
/** The list of classes which are out of date. */
private Map<String, String> outOfDateClasses;
indicates that the dependency relationships should be extended beyond direct dependencies to include all classes. So if A directly affects B and B directly affects C, then A indirectly affects C.
/** * indicates that the dependency relationships should be extended beyond * direct dependencies to include all classes. So if A directly affects * B and B directly affects C, then A indirectly affects C. */
private boolean closure = false;
flag to enable warning if we encounter RMI stubs
/** * flag to enable warning if we encounter RMI stubs */
private boolean warnOnRmiStubs = true;
Flag which controls whether the reversed dependencies should be dumped to the log
/** * Flag which controls whether the reversed dependencies should be * dumped to the log */
private boolean dump = false;
The classpath to look for additional dependencies
/** The classpath to look for additional dependencies */
private Path dependClasspath;
constants used with the cache file
/** constants used with the cache file */
private static final String CACHE_FILE_NAME = "dependencies.txt";
String Used to separate classnames in the dependency file
/** String Used to separate classnames in the dependency file */
private static final String CLASSNAME_PREPEND = "||:";
Set the classpath to be used for this dependency check.
Params:
  • classpath – the classpath to be used when checking for dependencies on elements in the classpath
/** * Set the classpath to be used for this dependency check. * * @param classpath the classpath to be used when checking for * dependencies on elements in the classpath */
public void setClasspath(Path classpath) { if (dependClasspath == null) { dependClasspath = classpath; } else { dependClasspath.append(classpath); } }
Gets the classpath to be used for this dependency check.
Returns:the current dependency classpath
/** * Gets the classpath to be used for this dependency check. * * @return the current dependency classpath */
public Path getClasspath() { return dependClasspath; }
Adds a classpath to be used for this dependency check.
Returns:A path object to be configured by Ant
/** * Adds a classpath to be used for this dependency check. * * @return A path object to be configured by Ant */
public Path createClasspath() { if (dependClasspath == null) { dependClasspath = new Path(getProject()); } return dependClasspath.createPath(); }
Adds a reference to a classpath defined elsewhere.
Params:
  • r – a reference to a path object to be used as the depend classpath
/** * Adds a reference to a classpath defined elsewhere. * * @param r a reference to a path object to be used as the depend * classpath */
public void setClasspathRef(Reference r) { createClasspath().setRefid(r); }
Flag to set to true if you want dependency issues with RMI stubs to appear at warning level.
Params:
  • warnOnRmiStubs – if true set dependency issues to appear at warning level.
Since:Ant1.7
/** * Flag to set to true if you want dependency issues with RMI * stubs to appear at warning level. * @param warnOnRmiStubs if true set dependency issues to appear at warning level. * @since Ant1.7 */
public void setWarnOnRmiStubs(boolean warnOnRmiStubs) { this.warnOnRmiStubs = warnOnRmiStubs; }
Read the dependencies from cache file
Throws:
Returns:a collection of class dependencies
/** * Read the dependencies from cache file * * @return a collection of class dependencies * @exception IOException if the dependency file cannot be read */
private Map<String, List<String>> readCachedDependencies(File depFile) throws IOException { Map<String, List<String>> dependencyMap = new HashMap<>(); int prependLength = CLASSNAME_PREPEND.length(); try (BufferedReader in = new BufferedReader(new FileReader(depFile))) { List<String> dependencyList = null; String line; while ((line = in.readLine()) != null) { if (line.startsWith(CLASSNAME_PREPEND)) { String className = line.substring(prependLength); dependencyList = dependencyMap.computeIfAbsent(className, k -> new ArrayList<>()); } else if (dependencyList != null) { dependencyList.add(line); } } } return dependencyMap; }
Write the dependencies to cache file
Params:
  • dependencyMap – the map of dependencies to be written out.
Throws:
  • IOException – if the dependency file cannot be written out.
/** * Write the dependencies to cache file * * @param dependencyMap the map of dependencies to be written out. * @exception IOException if the dependency file cannot be written out. */
private void writeCachedDependencies(Map<String, List<String>> dependencyMap) throws IOException { if (cache != null) { cache.mkdirs(); File depFile = new File(cache, CACHE_FILE_NAME); try (BufferedWriter pw = new BufferedWriter(new FileWriter(depFile))) { for (Map.Entry<String, List<String>> e : dependencyMap .entrySet()) { pw.write(String.format("%s%s%n", CLASSNAME_PREPEND, e.getKey())); for (String s : e.getValue()) { pw.write(s); pw.newLine(); } } } } }
Get the classpath for dependency checking. This method removes the dest dirs if it is given from the dependency classpath
/** * Get the classpath for dependency checking. * * This method removes the dest dirs if it is given from the dependency classpath */
private Path getCheckClassPath() { if (dependClasspath == null) { return null; } Set<Resource> dependNotInDest = new LinkedHashSet<>(); dependClasspath.forEach(dependNotInDest::add); destPath.forEach(dependNotInDest::remove); Path p; if (dependNotInDest.isEmpty()) { p = null; } else { p = new Path(getProject()); dependNotInDest.forEach(p::add); } log("Classpath without dest dir is " + p, Project.MSG_DEBUG); return p; }
Determine the dependencies between classes. Class dependencies are determined by examining the class references in a class file to other classes. This method sets up the following fields
  • affectedClassMap - the list of classes each class affects
  • classFileInfoMap - information about each class
  • classpathDependencies - the list of jars and classes from the classpath that each class depends upon.
If required, the dependencies are written to the cache.
Throws:
  • IOException – if either the dependencies cache or the class files cannot be read or written
/** * Determine the dependencies between classes. Class dependencies are * determined by examining the class references in a class file to other * classes. * * This method sets up the following fields * <ul> * <li>affectedClassMap - the list of classes each class affects</li> * <li>classFileInfoMap - information about each class</li> * <li>classpathDependencies - the list of jars and classes from the * classpath that each class depends upon.</li> * </ul> * * If required, the dependencies are written to the cache. * * @exception IOException if either the dependencies cache or the class * files cannot be read or written */
private void determineDependencies() throws IOException { affectedClassMap = new HashMap<>(); classFileInfoMap = new HashMap<>(); boolean cacheDirty = false; Map<String, List<String>> dependencyMap = new HashMap<>(); File cacheFile = null; boolean cacheFileExists = true; long cacheLastModified = Long.MAX_VALUE; // read the dependency cache from the disk if (cache != null) { cacheFile = new File(cache, CACHE_FILE_NAME); cacheFileExists = cacheFile.exists(); cacheLastModified = cacheFile.lastModified(); if (cacheFileExists) { dependencyMap = readCachedDependencies(cacheFile); } } for (ClassFileInfo info : getClassFiles()) { log("Adding class info for " + info.className, Project.MSG_DEBUG); classFileInfoMap.put(info.className, info); List<String> dependencyList = null; if (cache != null) { // try to read the dependency info from the map if it is // not out of date if (cacheFileExists && cacheLastModified > info.absoluteFile.lastModified()) { // depFile exists and is newer than the class file // need to get dependency list from the map. dependencyList = dependencyMap.get(info.className); } } if (dependencyList == null) { // not cached - so need to read directly from the class file DependencyAnalyzer analyzer = new AntAnalyzer(); analyzer.addRootClass(info.className); analyzer.addClassPath(destPath); analyzer.setClosure(false); dependencyList = Collections.list(analyzer.getClassDependencies()); dependencyList.forEach(o -> log("Class " + info.className + " depends on " + o, Project.MSG_DEBUG)); cacheDirty = true; 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 (String dependentClass : dependencyList) { affectedClassMap .computeIfAbsent(dependentClass, k -> new HashMap<>()) .put(info.className, info); log(dependentClass + " affects " + info.className, Project.MSG_DEBUG); } } classpathDependencies = null; Path checkPath = getCheckClassPath(); if (checkPath != null) { // now determine which jars each class depends upon classpathDependencies = new HashMap<>(); try (AntClassLoader loader = getProject().createClassLoader(checkPath)) { Map<String, Object> classpathFileCache = new HashMap<>(); Object nullFileMarker = new Object(); for (Map.Entry<String, List<String>> e : dependencyMap.entrySet()) { String className = e.getKey(); log("Determining classpath dependencies for " + className, Project.MSG_DEBUG); List<String> dependencyList = e.getValue(); Set<File> dependencies = new HashSet<>(); classpathDependencies.put(className, dependencies); for (String dependency : dependencyList) { log("Looking for " + dependency, Project.MSG_DEBUG); Object classpathFileObject = classpathFileCache.get(dependency); if (classpathFileObject == null) { classpathFileObject = nullFileMarker; if (!dependency.startsWith("java.") && !dependency.startsWith("javax.")) { URL classURL = loader.getResource(dependency.replace('.', '/') + ".class"); log("URL is " + classURL, Project.MSG_DEBUG); if (classURL != null) { if ("jar".equals(classURL.getProtocol())) { String jarFilePath = classURL.getFile(); int classMarker = jarFilePath.indexOf('!'); jarFilePath = jarFilePath.substring(0, classMarker); if (jarFilePath.startsWith("file:")) { classpathFileObject = new File( FileUtils.getFileUtils() .fromURI(jarFilePath)); } else { throw new IOException( "Bizarre nested path in jar: protocol: " + jarFilePath); } } else if ("file".equals(classURL.getProtocol())) { classpathFileObject = new File( FileUtils.getFileUtils().fromURI( classURL.toExternalForm())); } log("Class " + className + " depends on " + classpathFileObject + " due to " + dependency, Project.MSG_DEBUG); } } else { log("Ignoring base classlib dependency " + dependency, Project.MSG_DEBUG); } classpathFileCache.put(dependency, classpathFileObject); } if (classpathFileObject != nullFileMarker) { // we need to add this jar to the list for this class. File jarFile = (File) classpathFileObject; log("Adding a classpath dependency on " + jarFile, Project.MSG_DEBUG); dependencies.add(jarFile); } } } } } else { log("No classpath to check", Project.MSG_DEBUG); } // write the dependency cache to the disk if (cache != null && cacheDirty) { writeCachedDependencies(dependencyMap); } }
Delete all the class files which are out of date, by way of their dependency on a class which is out of date
Returns:the number of files deleted.
/** * Delete all the class files which are out of date, by way of their * dependency on a class which is out of date * * @return the number of files deleted. */
private int deleteAllAffectedFiles() { int count = 0; for (String className : outOfDateClasses.keySet()) { count += deleteAffectedFiles(className); ClassFileInfo classInfo = classFileInfoMap.get(className); if (classInfo != null && classInfo.absoluteFile.exists()) { if (classInfo.sourceFile == null) { warnOutOfDateButNotDeleted(classInfo, className, className); } else { classInfo.absoluteFile.delete(); count++; } } } return count; }
Delete all the class files of classes which depend on the given class
Params:
  • className – the name of the class whose dependent classes will be deleted
Returns:the number of class files removed
/** * Delete all the class files of classes which depend on the given class * * @param className the name of the class whose dependent classes will be * deleted * @return the number of class files removed */
private int deleteAffectedFiles(String className) { int count = 0; Map<String, ClassFileInfo> affectedClasses = affectedClassMap.get(className); if (affectedClasses == null) { return count; } for (Map.Entry<String, ClassFileInfo> e : affectedClasses.entrySet()) { String affectedClass = e.getKey(); ClassFileInfo affectedClassInfo = e.getValue(); if (!affectedClassInfo.absoluteFile.exists()) { continue; } if (affectedClassInfo.sourceFile == null) { warnOutOfDateButNotDeleted(affectedClassInfo, affectedClass, className); continue; } log("Deleting file " + affectedClassInfo.absoluteFile.getPath() + " since " + className + " out of date", Project.MSG_VERBOSE); affectedClassInfo.absoluteFile.delete(); count++; if (closure) { count += deleteAffectedFiles(affectedClass); } else { // without closure we may delete an inner class but not the // top level class which would not trigger a recompile. if (!affectedClass.contains("$")) { continue; } // need to delete the main class String topLevelClassName = affectedClass.substring(0, affectedClass.indexOf("$")); log("Top level class = " + topLevelClassName, Project.MSG_VERBOSE); ClassFileInfo topLevelClassInfo = classFileInfoMap.get(topLevelClassName); if (topLevelClassInfo != null && topLevelClassInfo.absoluteFile.exists()) { log("Deleting file " + topLevelClassInfo.absoluteFile.getPath() + " since one of its inner classes was removed", Project.MSG_VERBOSE); topLevelClassInfo.absoluteFile.delete(); count++; if (closure) { count += deleteAffectedFiles(topLevelClassName); } } } } return count; }
warn when a class is out of date, but not deleted as its source is unknown. MSG_WARN is the normal level, but we downgrade to MSG_VERBOSE for RMI files if is false
Params:
  • affectedClassInfo – info about the affected class
  • affectedClass – the name of the affected .class file
  • className – the file that is triggering the out of dateness
/** * warn when a class is out of date, but not deleted as its source is unknown. * MSG_WARN is the normal level, but we downgrade to MSG_VERBOSE for RMI files * if {@link #warnOnRmiStubs is false} * @param affectedClassInfo info about the affected class * @param affectedClass the name of the affected .class file * @param className the file that is triggering the out of dateness */
private void warnOutOfDateButNotDeleted( ClassFileInfo affectedClassInfo, String affectedClass, String className) { if (affectedClassInfo.isUserWarned) { return; } int level = Project.MSG_WARN; if (!warnOnRmiStubs) { //downgrade warnings on RMI stublike classes, as they are generated //by rmic, so there is no need to tell the user that their source is //missing. if (isRmiStub(affectedClass, className)) { level = Project.MSG_VERBOSE; } } log("The class " + affectedClass + " in file " + affectedClassInfo.absoluteFile.getPath() + " is out of date due to " + className + " but has not been deleted because its source file could not be determined", level); affectedClassInfo.isUserWarned = true; }
test for being an RMI stub
Params:
  • affectedClass – class being tested
  • className – possible origin of the RMI stub
Returns:whether the class affectedClass is a RMI stub
/** * test for being an RMI stub * @param affectedClass class being tested * @param className possible origin of the RMI stub * @return whether the class affectedClass is a RMI stub */
private boolean isRmiStub(String affectedClass, String className) { return isStub(affectedClass, className, DefaultRmicAdapter.RMI_STUB_SUFFIX) || isStub(affectedClass, className, DefaultRmicAdapter.RMI_SKEL_SUFFIX) || isStub(affectedClass, className, WLRmic.RMI_STUB_SUFFIX) || isStub(affectedClass, className, WLRmic.RMI_SKEL_SUFFIX); } private boolean isStub(String affectedClass, String baseClass, String suffix) { return (baseClass + suffix).equals(affectedClass); }
Dump the dependency information loaded from the classes to the Ant log
/** * Dump the dependency information loaded from the classes to the Ant log */
private void dumpDependencies() { log("Reverse Dependency Dump for " + affectedClassMap.size() + " classes:", Project.MSG_DEBUG); affectedClassMap.forEach((className, affectedClasses) -> { log(" Class " + className + " affects:", Project.MSG_DEBUG); affectedClasses.forEach((affectedClass, info) -> log( " " + affectedClass + " in " + info.absoluteFile.getPath(), Project.MSG_DEBUG)); }); if (classpathDependencies != null) { log("Classpath file dependencies (Forward):", Project.MSG_DEBUG); classpathDependencies.forEach((className, dependencies) -> { log(" Class " + className + " depends on:", Project.MSG_DEBUG); dependencies.forEach(f -> log(" " + f.getPath(), Project.MSG_DEBUG)); }); } } private void determineOutOfDateClasses() { outOfDateClasses = new HashMap<>(); directories(srcPath).forEach(srcDir -> { DirectoryScanner ds = this.getDirectoryScanner(srcDir); scanDir(srcDir, ds.getIncludedFiles()); }); // now check classpath file dependencies if (classpathDependencies == null) { return; } for (Map.Entry<String, Set<File>> e : classpathDependencies.entrySet()) { String className = e.getKey(); if (outOfDateClasses.containsKey(className)) { continue; } ClassFileInfo info = classFileInfoMap.get(className); // if we have no info about the class - it may have been deleted already and we // are using cached info. if (info != null) { for (File classpathFile : e.getValue()) { if (classpathFile.lastModified() > info.absoluteFile .lastModified()) { log("Class " + className + " is out of date with respect to " + classpathFile, Project.MSG_DEBUG); outOfDateClasses.put(className, className); break; } } } } }
Does the work.
Throws:
  • BuildException – Thrown in case of an unrecoverable error.
/** * Does the work. * * @exception BuildException Thrown in case of an unrecoverable error. */
@Override public void execute() throws BuildException { try { long start = System.currentTimeMillis(); if (srcPath == null) { throw new BuildException("srcdir attribute must be set", getLocation()); } if (!directories(srcPath).findAny().isPresent()) { throw new BuildException("srcdir attribute must be non-empty", getLocation()); } if (destPath == null) { destPath = srcPath; } 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(); if (dump) { dumpDependencies(); } determineOutOfDateClasses(); int count = deleteAllAffectedFiles(); long duration = (System.currentTimeMillis() - start) / ONE_SECOND; final int summaryLogLevel; if (count > 0) { summaryLogLevel = Project.MSG_INFO; } else { summaryLogLevel = Project.MSG_DEBUG; } log("Deleted " + count + " out of date files in " + duration + " seconds", summaryLogLevel); } catch (Exception e) { throw new BuildException(e); } }
Scans the directory looking for source files that are newer than their class files. The results are returned in the class variable compileList
Params:
  • srcDir – the source directory
  • files – the names of the files in the source dir which are to be checked.
/** * Scans the directory looking for source files that are newer than * their class files. The results are returned in the class variable * compileList * * @param srcDir the source directory * @param files the names of the files in the source dir which are to be * checked. */
protected void scanDir(File srcDir, String[] files) { for (String f : files) { File srcFile = new File(srcDir, f); if (f.endsWith(".java")) { String filePath = srcFile.getPath(); String className = filePath.substring(srcDir.getPath().length() + 1, filePath.length() - ".java".length()); className = ClassFileUtils.convertSlashName(className); ClassFileInfo info = classFileInfoMap.get(className); if (info == null) { // there was no class file. add this class to the list outOfDateClasses.put(className, className); } else if (srcFile.lastModified() > info.absoluteFile .lastModified()) { outOfDateClasses.put(className, className); } } } }
Get the list of class files we are going to analyse.
Returns:a vector containing the classes to analyse.
/** * Get the list of class files we are going to analyse. * * @return a vector containing the classes to analyse. */
private List<ClassFileInfo> getClassFiles() { // break the classLocations into its components. List<ClassFileInfo> classFileList = new ArrayList<>(); directories(destPath) .forEach(dir -> addClassFiles(classFileList, dir, dir)); return classFileList; }
Find the source file for a given class
Params:
  • classname – the classname in slash format.
  • sourceFileKnownToExist – if not null, a file already known to exist (saves call to .exists())
/** * Find the source file for a given class * * @param classname the classname in slash format. * @param sourceFileKnownToExist if not null, a file already known to exist * (saves call to .exists()) */
private File findSourceFile(String classname, File sourceFileKnownToExist) { String sourceFilename; int innerIndex = classname.indexOf('$'); if (innerIndex != -1) { sourceFilename = classname.substring(0, innerIndex) + ".java"; } else { sourceFilename = classname + ".java"; } // search the various source path entries return directories(srcPath) .map(d -> new File(d, sourceFilename)).filter(Predicate .<File> isEqual(sourceFileKnownToExist).or(File::exists)) .findFirst().orElse(null); }
Add the list of class files from the given directory to the class file vector, including any subdirectories.
Params:
  • classFileList – a list of ClassFileInfo objects for all the files in the directory tree
  • dir – the directory tree to be searched, recursively, for class files
  • root – the root of the source tree. This is used to determine the absolute class name from the relative position in the source tree
/** * Add the list of class files from the given directory to the class * file vector, including any subdirectories. * * @param classFileList a list of ClassFileInfo objects for all the * files in the directory tree * @param dir the directory tree to be searched, recursively, for class * files * @param root the root of the source tree. This is used to determine * the absolute class name from the relative position in the * source tree */
private void addClassFiles(List<ClassFileInfo> classFileList, File dir, File root) { File[] children = dir.listFiles(); if (children == null) { return; } int rootLength = root.getPath().length(); File sourceFileKnownToExist = null; // speed optimization for (File file : children) { if (file.getName().endsWith(".class")) { ClassFileInfo info = new ClassFileInfo(); info.absoluteFile = file; String relativeName = file.getPath().substring(rootLength + 1, file.getPath().length() - ".class".length()); info.className = ClassFileUtils.convertSlashName(relativeName); info.sourceFile = sourceFileKnownToExist = findSourceFile(relativeName, sourceFileKnownToExist); classFileList.add(info); } else { addClassFiles(classFileList, file, root); } } }
Set the directories path to find the Java source files.
Params:
  • srcPath – the source path
/** * Set the directories path to find the Java source files. * * @param srcPath the source path */
public void setSrcdir(Path srcPath) { this.srcPath = srcPath; }
Set the destination directory where the compiled Java files exist.
Params:
  • destPath – the destination areas where build files are written
/** * Set the destination directory where the compiled Java files exist. * * @param destPath the destination areas where build files are written */
public void setDestDir(Path destPath) { this.destPath = destPath; }
Sets the dependency cache file.
Params:
  • cache – the dependency cache file
/** * Sets the dependency cache file. * * @param cache the dependency cache file */
public void setCache(File cache) { this.cache = cache; }
If true, transitive dependencies are followed until the closure of the dependency set if reached. When not set, the depend task will only follow direct dependencies between classes.
Params:
  • closure – indicate if dependency closure is required.
/** * If true, transitive dependencies are followed until the * closure of the dependency set if reached. * When not set, the depend task will only follow * direct dependencies between classes. * * @param closure indicate if dependency closure is required. */
public void setClosure(boolean closure) { this.closure = closure; }
If true, the dependency information will be written to the debug level log.
Params:
  • dump – set to true to dump dependency information to the log
/** * If true, the dependency information will be written * to the debug level log. * * @param dump set to true to dump dependency information to the log */
public void setDump(boolean dump) { this.dump = dump; } private Stream<File> directories(ResourceCollection rc) { return rc.stream().map(r -> r.as(FileProvider.class)) .filter(Objects::nonNull).map(FileProvider::getFile) .filter(File::isDirectory); } }