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]); | ||||