the same way as the attribute of the <depend> selector. PR: 22150 git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@275307 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -135,6 +135,16 @@ operation as <a href="../CoreTypes/filterset.html">filtersets</a> | |||||
| <em>since Ant 1.6</em>.</td> | <em>since Ant 1.6</em>.</td> | ||||
| <td align="center">No - defaults to false.</td> | <td align="center">No - defaults to false.</td> | ||||
| </tr> | </tr> | ||||
| <tr> | |||||
| <td valign="top">granularity</td> | |||||
| <td valign="top">The number of milliseconds leeway to give before | |||||
| deciding a file is out of date. This is needed because not every | |||||
| file system supports tracking the last modified time to the | |||||
| millisecond level. Default is 0 milliseconds, or 2 seconds on DOS | |||||
| systems. This can also be useful if source and target files live | |||||
| on separate machines with clocks being out of sync. <em>since Ant | |||||
| 1.6</em>.</td> | |||||
| </tr> | |||||
| </table> | </table> | ||||
| <h3>Parameters specified as nested elements</h3> | <h3>Parameters specified as nested elements</h3> | ||||
| @@ -113,6 +113,16 @@ to move to the <var>todir</var> directory.</p> | |||||
| <em>since Ant 1.6</em>.</td> | <em>since Ant 1.6</em>.</td> | ||||
| <td align="center">No - defaults to false.</td> | <td align="center">No - defaults to false.</td> | ||||
| </tr> | </tr> | ||||
| <tr> | |||||
| <td valign="top">granularity</td> | |||||
| <td valign="top">The number of milliseconds leeway to give before | |||||
| deciding a file is out of date. This is needed because not every | |||||
| file system supports tracking the last modified time to the | |||||
| millisecond level. Default is 0 milliseconds, or 2 seconds on DOS | |||||
| systems. This can also be useful if source and target files live | |||||
| on separate machines with clocks being out of sync. <em>since Ant | |||||
| 1.6</em>.</td> | |||||
| </tr> | |||||
| </table> | </table> | ||||
| <h3>Parameters specified as nested elements</h3> | <h3>Parameters specified as nested elements</h3> | ||||
| <h4>mapper</h4> | <h4>mapper</h4> | ||||
| @@ -51,6 +51,16 @@ more filesets.</p> | |||||
| <td valign="top">Log the files that are being copied.</td> | <td valign="top">Log the files that are being copied.</td> | ||||
| <td valign="top" align="center">No; defaults to false.</td> | <td valign="top" align="center">No; defaults to false.</td> | ||||
| </tr> | </tr> | ||||
| <tr> | |||||
| <td valign="top">granularity</td> | |||||
| <td valign="top">The number of milliseconds leeway to give before | |||||
| deciding a file is out of date. This is needed because not every | |||||
| file system supports tracking the last modified time to the | |||||
| millisecond level. Default is 0 milliseconds, or 2 seconds on DOS | |||||
| systems. This can also be useful if source and target files live | |||||
| on separate machines with clocks being out of sync. <em>since Ant | |||||
| 1.6</em>.</td> | |||||
| </tr> | |||||
| </table> | </table> | ||||
| <h3>Parameters specified as nested elements</h3> | <h3>Parameters specified as nested elements</h3> | ||||
| @@ -121,12 +121,14 @@ public class Copy extends Task { | |||||
| private FileUtils fileUtils; | private FileUtils fileUtils; | ||||
| private String inputEncoding = null; | private String inputEncoding = null; | ||||
| private String outputEncoding = null; | private String outputEncoding = null; | ||||
| private long granularity = 0; | |||||
| /** | /** | ||||
| * Copy task constructor. | * Copy task constructor. | ||||
| */ | */ | ||||
| public Copy() { | public Copy() { | ||||
| fileUtils = FileUtils.newFileUtils(); | fileUtils = FileUtils.newFileUtils(); | ||||
| granularity = fileUtils.getFileTimestampGranularity(); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -370,6 +372,18 @@ public class Copy extends Task { | |||||
| return outputEncoding; | return outputEncoding; | ||||
| } | } | ||||
| /** | |||||
| * The number of milliseconds leeway to give before deciding a | |||||
| * target is out of date. | |||||
| * | |||||
| * <p>Default is 0 milliseconds, or 2 seconds on DOS systems.</p> | |||||
| * | |||||
| * @since Ant 1.6 | |||||
| */ | |||||
| public void setGranularity(long granularity) { | |||||
| this.granularity = granularity; | |||||
| } | |||||
| /** | /** | ||||
| * Performs the copy operation. | * Performs the copy operation. | ||||
| * @exception BuildException if an error occurs | * @exception BuildException if an error occurs | ||||
| @@ -397,7 +411,8 @@ public class Copy extends Task { | |||||
| } | } | ||||
| if (forceOverwrite || !destFile.exists() | if (forceOverwrite || !destFile.exists() | ||||
| || (file.lastModified() > destFile.lastModified())) { | |||||
| || (file.lastModified() - granularity | |||||
| > destFile.lastModified())) { | |||||
| fileCopyMap.put(file.getAbsolutePath(), | fileCopyMap.put(file.getAbsolutePath(), | ||||
| new String[] {destFile.getAbsolutePath()}); | new String[] {destFile.getAbsolutePath()}); | ||||
| } else { | } else { | ||||
| @@ -583,7 +598,7 @@ public class Copy extends Task { | |||||
| v.copyInto(toCopy); | v.copyInto(toCopy); | ||||
| } else { | } else { | ||||
| SourceFileScanner ds = new SourceFileScanner(this); | SourceFileScanner ds = new SourceFileScanner(this); | ||||
| toCopy = ds.restrict(names, fromDir, toDir, mapper); | |||||
| toCopy = ds.restrict(names, fromDir, toDir, mapper, granularity); | |||||
| } | } | ||||
| for (int i = 0; i < toCopy.length; i++) { | for (int i = 0; i < toCopy.length; i++) { | ||||
| @@ -322,6 +322,18 @@ public class Sync extends Task { | |||||
| _copy.addFileset(set); | _copy.addFileset(set); | ||||
| } | } | ||||
| /** | |||||
| * The number of milliseconds leeway to give before deciding a | |||||
| * target is out of date. | |||||
| * | |||||
| * <p>Default is 0 milliseconds, or 2 seconds on DOS systems.</p> | |||||
| * | |||||
| * @since Ant 1.6 | |||||
| */ | |||||
| public void setGranularity(long granularity) { | |||||
| _copy.setGranularity(granularity); | |||||
| } | |||||
| /** | /** | ||||
| * Subclass Copy in order to access it's file/dir maps. | * Subclass Copy in order to access it's file/dir maps. | ||||
| */ | */ | ||||
| @@ -58,6 +58,7 @@ import org.apache.tools.ant.ProjectComponent; | |||||
| import org.apache.tools.ant.taskdefs.condition.Os; | import org.apache.tools.ant.taskdefs.condition.Os; | ||||
| import org.apache.tools.ant.types.Resource; | import org.apache.tools.ant.types.Resource; | ||||
| import org.apache.tools.ant.types.ResourceFactory; | import org.apache.tools.ant.types.ResourceFactory; | ||||
| import org.apache.tools.ant.types.selectors.SelectorUtils; | |||||
| import java.io.File; | import java.io.File; | ||||
| import java.util.Vector; | import java.util.Vector; | ||||
| @@ -70,7 +71,7 @@ import java.util.Vector; | |||||
| */ | */ | ||||
| public class ResourceUtils { | public class ResourceUtils { | ||||
| /** { | |||||
| /** | |||||
| * tells which source files should be reprocessed based on the | * tells which source files should be reprocessed based on the | ||||
| * last modification date of target files | * last modification date of target files | ||||
| * @param logTo where to send (more or less) interesting output | * @param logTo where to send (more or less) interesting output | ||||
| @@ -88,19 +89,34 @@ public class ResourceUtils { | |||||
| Resource[] source, | Resource[] source, | ||||
| FileNameMapper mapper, | FileNameMapper mapper, | ||||
| ResourceFactory targets) { | ResourceFactory targets) { | ||||
| long now = (new java.util.Date()).getTime(); | |||||
| return selectOutOfDateSources(logTo, source, mapper, targets, | |||||
| FileUtils.newFileUtils() | |||||
| .getFileTimestampGranularity()); | |||||
| } | |||||
| /* | |||||
| If we're on Windows, we have to munge the time up to 2 secs to | |||||
| be able to check file modification times. | |||||
| (Windows has a max resolution of two secs for modification times) | |||||
| Actually this is a feature of the FAT file system, NTFS does | |||||
| not have it, so if we could reliably passively test for an NTFS | |||||
| file systems we could turn this off... | |||||
| */ | |||||
| if (Os.isFamily("windows")) { | |||||
| now += 2000; | |||||
| } | |||||
| /** | |||||
| * tells which source files should be reprocessed based on the | |||||
| * last modification date of target files | |||||
| * @param logTo where to send (more or less) interesting output | |||||
| * @param source array of resources bearing relative path and last | |||||
| * modification date | |||||
| * @param mapper filename mapper indicating how to find the target | |||||
| * files | |||||
| * @param targets object able to map as a resource a relative path | |||||
| * at <b>destination</b> | |||||
| * @param granularity The number of milliseconds leeway to give | |||||
| * before deciding a target is out of date. | |||||
| * @return array containing the source files which need to be | |||||
| * copied or processed, because the targets are out of date or do | |||||
| * not exist | |||||
| * @since Ant 1.6 | |||||
| */ | |||||
| public static Resource[] selectOutOfDateSources(ProjectComponent logTo, | |||||
| Resource[] source, | |||||
| FileNameMapper mapper, | |||||
| ResourceFactory targets, | |||||
| long granularity) { | |||||
| long now = (new java.util.Date()).getTime() + granularity; | |||||
| Vector vresult = new Vector(); | Vector vresult = new Vector(); | ||||
| for (int counter = 0; counter < source.length; counter++) { | for (int counter = 0; counter < source.length; counter++) { | ||||
| @@ -130,8 +146,10 @@ public class ResourceUtils { | |||||
| + " doesn\'t exist.", Project.MSG_VERBOSE); | + " doesn\'t exist.", Project.MSG_VERBOSE); | ||||
| vresult.addElement(source[counter]); | vresult.addElement(source[counter]); | ||||
| added = true; | added = true; | ||||
| } else if (!atarget.isDirectory() && atarget.getLastModified() | |||||
| < source[counter].getLastModified()) { | |||||
| } else if (!atarget.isDirectory() && | |||||
| SelectorUtils.isOutOfDate(source[counter], | |||||
| atarget, | |||||
| (int) granularity)) { | |||||
| logTo.log(source[counter].getName() + " added as " | logTo.log(source[counter].getName() + " added as " | ||||
| + atarget.getName() | + atarget.getName() | ||||
| + " is outdated.", Project.MSG_VERBOSE); | + " is outdated.", Project.MSG_VERBOSE); | ||||
| @@ -99,6 +99,27 @@ public class SourceFileScanner implements ResourceFactory { | |||||
| */ | */ | ||||
| public String[] restrict(String[] files, File srcDir, File destDir, | public String[] restrict(String[] files, File srcDir, File destDir, | ||||
| FileNameMapper mapper) { | FileNameMapper mapper) { | ||||
| return restrict(files, srcDir, destDir, mapper, | |||||
| fileUtils.getFileTimestampGranularity()); | |||||
| } | |||||
| /** | |||||
| * Restrict the given set of files to those that are newer than | |||||
| * their corresponding target files. | |||||
| * | |||||
| * @param files the original set of files | |||||
| * @param srcDir all files are relative to this directory | |||||
| * @param destDir target files live here. if null file names | |||||
| * returned by the mapper are assumed to be absolute. | |||||
| * @param mapper knows how to construct a target file names from | |||||
| * source file names. | |||||
| * @param granularity The number of milliseconds leeway to give | |||||
| * before deciding a target is out of date. | |||||
| * | |||||
| * @since Ant 1.6 | |||||
| */ | |||||
| public String[] restrict(String[] files, File srcDir, File destDir, | |||||
| FileNameMapper mapper, long granularity) { | |||||
| // record destdir for later use in getResource | // record destdir for later use in getResource | ||||
| this.destDir = destDir; | this.destDir = destDir; | ||||
| Vector v = new Vector(); | Vector v = new Vector(); | ||||
| @@ -114,7 +135,7 @@ public class SourceFileScanner implements ResourceFactory { | |||||
| // respect to the target | // respect to the target | ||||
| Resource[] outofdate = | Resource[] outofdate = | ||||
| ResourceUtils.selectOutOfDateSources(task, sourceresources, | ResourceUtils.selectOutOfDateSources(task, sourceresources, | ||||
| mapper, this); | |||||
| mapper, this, granularity); | |||||
| String[] result = new String[outofdate.length]; | String[] result = new String[outofdate.length]; | ||||
| for (int counter = 0; counter < outofdate.length; counter++) { | for (int counter = 0; counter < outofdate.length; counter++) { | ||||
| result[counter] = outofdate[counter].getName(); | result[counter] = outofdate[counter].getName(); | ||||
| @@ -129,7 +150,20 @@ public class SourceFileScanner implements ResourceFactory { | |||||
| */ | */ | ||||
| public File[] restrictAsFiles(String[] files, File srcDir, File destDir, | public File[] restrictAsFiles(String[] files, File srcDir, File destDir, | ||||
| FileNameMapper mapper) { | FileNameMapper mapper) { | ||||
| String[] res = restrict(files, srcDir, destDir, mapper); | |||||
| return restrictAsFiles(files, srcDir, destDir, mapper, | |||||
| fileUtils.getFileTimestampGranularity()); | |||||
| } | |||||
| /** | |||||
| * Convinience layer on top of restrict that returns the source | |||||
| * files as File objects (containing absolute paths if srcDir is | |||||
| * absolute). | |||||
| * | |||||
| * @since Ant 1.6 | |||||
| */ | |||||
| public File[] restrictAsFiles(String[] files, File srcDir, File destDir, | |||||
| FileNameMapper mapper, long granularity) { | |||||
| String[] res = restrict(files, srcDir, destDir, mapper, granularity); | |||||
| File[] result = new File[res.length]; | File[] result = new File[res.length]; | ||||
| for (int i = 0; i < res.length; i++) { | for (int i = 0; i < res.length; i++) { | ||||
| result[i] = new File(srcDir, res[i]); | result[i] = new File(srcDir, res[i]); | ||||