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> | |||
<td align="center">No - defaults to false.</td> | |||
</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> | |||
<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> | |||
<td align="center">No - defaults to false.</td> | |||
</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> | |||
<h3>Parameters specified as nested elements</h3> | |||
<h4>mapper</h4> | |||
@@ -51,6 +51,16 @@ more filesets.</p> | |||
<td valign="top">Log the files that are being copied.</td> | |||
<td valign="top" align="center">No; defaults to false.</td> | |||
</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> | |||
<h3>Parameters specified as nested elements</h3> | |||
@@ -121,12 +121,14 @@ public class Copy extends Task { | |||
private FileUtils fileUtils; | |||
private String inputEncoding = null; | |||
private String outputEncoding = null; | |||
private long granularity = 0; | |||
/** | |||
* Copy task constructor. | |||
*/ | |||
public Copy() { | |||
fileUtils = FileUtils.newFileUtils(); | |||
granularity = fileUtils.getFileTimestampGranularity(); | |||
} | |||
/** | |||
@@ -370,6 +372,18 @@ public class Copy extends Task { | |||
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. | |||
* @exception BuildException if an error occurs | |||
@@ -397,7 +411,8 @@ public class Copy extends Task { | |||
} | |||
if (forceOverwrite || !destFile.exists() | |||
|| (file.lastModified() > destFile.lastModified())) { | |||
|| (file.lastModified() - granularity | |||
> destFile.lastModified())) { | |||
fileCopyMap.put(file.getAbsolutePath(), | |||
new String[] {destFile.getAbsolutePath()}); | |||
} else { | |||
@@ -583,7 +598,7 @@ public class Copy extends Task { | |||
v.copyInto(toCopy); | |||
} else { | |||
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++) { | |||
@@ -322,6 +322,18 @@ public class Sync extends Task { | |||
_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. | |||
*/ | |||
@@ -58,6 +58,7 @@ import org.apache.tools.ant.ProjectComponent; | |||
import org.apache.tools.ant.taskdefs.condition.Os; | |||
import org.apache.tools.ant.types.Resource; | |||
import org.apache.tools.ant.types.ResourceFactory; | |||
import org.apache.tools.ant.types.selectors.SelectorUtils; | |||
import java.io.File; | |||
import java.util.Vector; | |||
@@ -70,7 +71,7 @@ import java.util.Vector; | |||
*/ | |||
public class ResourceUtils { | |||
/** { | |||
/** | |||
* 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 | |||
@@ -88,19 +89,34 @@ public class ResourceUtils { | |||
Resource[] source, | |||
FileNameMapper mapper, | |||
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(); | |||
for (int counter = 0; counter < source.length; counter++) { | |||
@@ -130,8 +146,10 @@ public class ResourceUtils { | |||
+ " doesn\'t exist.", Project.MSG_VERBOSE); | |||
vresult.addElement(source[counter]); | |||
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 " | |||
+ atarget.getName() | |||
+ " is outdated.", Project.MSG_VERBOSE); | |||
@@ -99,6 +99,27 @@ public class SourceFileScanner implements ResourceFactory { | |||
*/ | |||
public String[] restrict(String[] files, File srcDir, File destDir, | |||
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 | |||
this.destDir = destDir; | |||
Vector v = new Vector(); | |||
@@ -114,7 +135,7 @@ public class SourceFileScanner implements ResourceFactory { | |||
// respect to the target | |||
Resource[] outofdate = | |||
ResourceUtils.selectOutOfDateSources(task, sourceresources, | |||
mapper, this); | |||
mapper, this, granularity); | |||
String[] result = new String[outofdate.length]; | |||
for (int counter = 0; counter < outofdate.length; counter++) { | |||
result[counter] = outofdate[counter].getName(); | |||
@@ -129,7 +150,20 @@ public class SourceFileScanner implements ResourceFactory { | |||
*/ | |||
public File[] restrictAsFiles(String[] files, File srcDir, File destDir, | |||
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]; | |||
for (int i = 0; i < res.length; i++) { | |||
result[i] = new File(srcDir, res[i]); | |||