git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@942752 13f79535-47bb-0310-9956-ffa450edef68master
@@ -4,6 +4,15 @@ Changes from Ant 1.8.1 TO current SVN version | |||||
Changes that could break older environments: | Changes that could break older environments: | ||||
------------------------------------------- | ------------------------------------------- | ||||
* Prior to Ant 1.8.0 the <copy> task would overwrite read-only | |||||
destination files. Starting with 1.8.0 it would only do so if | |||||
under special circumstances. Ant 1.8.2 now consistently won't | |||||
replace a read-only file by default. The same is true for a number | |||||
of other tasks. | |||||
The <copy> task now has a new force attribute that can be used to | |||||
make the task overwrite read-only destinations. | |||||
Bugzilla Report 49261. | |||||
Fixed bugs: | Fixed bugs: | ||||
----------- | ----------- | ||||
@@ -89,6 +89,12 @@ operation as <a href="../CoreTypes/filterset.html">filtersets</a>. | |||||
files are newer.</td> | files are newer.</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">force</td> | |||||
<td valign="top">Overwrite read-only destination | |||||
files. <em>since Ant 1.8.2</em></td> | |||||
<td valign="top" align="center">No; defaults to false.</td> | |||||
</tr> | |||||
<tr> | <tr> | ||||
<td valign="top">filtering</td> | <td valign="top">filtering</td> | ||||
<td valign="top">Indicates whether token filtering using the <a href="../using.html#filters">global | <td valign="top">Indicates whether token filtering using the <a href="../using.html#filters">global | ||||
@@ -100,6 +100,7 @@ public class Copy extends Task { | |||||
private String inputEncoding = null; | private String inputEncoding = null; | ||||
private String outputEncoding = null; | private String outputEncoding = null; | ||||
private long granularity = 0; | private long granularity = 0; | ||||
private boolean force = false; | |||||
/** | /** | ||||
* Copy task constructor. | * Copy task constructor. | ||||
@@ -228,6 +229,17 @@ public class Copy extends Task { | |||||
this.forceOverwrite = overwrite; | this.forceOverwrite = overwrite; | ||||
} | } | ||||
/** | |||||
* Whether read-only destinations will be overwritten. | |||||
* | |||||
* <p>Defaults to false</p> | |||||
* | |||||
* @since Ant 1.8.2 | |||||
*/ | |||||
public void setForce(boolean f) { | |||||
force = f; | |||||
} | |||||
/** | /** | ||||
* Set whether files copied from directory trees will be "flattened" | * Set whether files copied from directory trees will be "flattened" | ||||
* into a single directory. If there are multiple files with | * into a single directory. If there are multiple files with | ||||
@@ -838,10 +850,13 @@ public class Copy extends Task { | |||||
executionFilters | executionFilters | ||||
.addFilterSet((FilterSet) filterEnum.nextElement()); | .addFilterSet((FilterSet) filterEnum.nextElement()); | ||||
} | } | ||||
fileUtils.copyFile(fromFile, toFile, executionFilters, | |||||
fileUtils.copyFile(new File(fromFile), new File(toFile), | |||||
executionFilters, | |||||
filterChains, forceOverwrite, | filterChains, forceOverwrite, | ||||
preserveLastModified, inputEncoding, | |||||
outputEncoding, getProject()); | |||||
preserveLastModified, | |||||
/* append: */ false, inputEncoding, | |||||
outputEncoding, getProject(), | |||||
force); | |||||
} catch (IOException ioe) { | } catch (IOException ioe) { | ||||
String msg = "Failed to copy " + fromFile + " to " + toFile | String msg = "Failed to copy " + fromFile + " to " + toFile | ||||
+ " due to " + getDueTo(ioe); | + " due to " + getDueTo(ioe); | ||||
@@ -928,9 +943,11 @@ public class Copy extends Task { | |||||
filterChains, | filterChains, | ||||
forceOverwrite, | forceOverwrite, | ||||
preserveLastModified, | preserveLastModified, | ||||
/* append: */ false, | |||||
inputEncoding, | inputEncoding, | ||||
outputEncoding, | outputEncoding, | ||||
getProject()); | |||||
getProject(), | |||||
force); | |||||
} catch (IOException ioe) { | } catch (IOException ioe) { | ||||
String msg = "Failed to copy " + fromResource | String msg = "Failed to copy " + fromResource | ||||
+ " to " + toFile | + " to " + toFile | ||||
@@ -513,12 +513,55 @@ public class FileUtils { | |||||
*/ | */ | ||||
public void copyFile(File sourceFile, File destFile, | public void copyFile(File sourceFile, File destFile, | ||||
FilterSetCollection filters, Vector filterChains, | FilterSetCollection filters, Vector filterChains, | ||||
boolean overwrite, boolean preserveLastModified, boolean append, | |||||
boolean overwrite, boolean preserveLastModified, | |||||
boolean append, | |||||
String inputEncoding, String outputEncoding, | String inputEncoding, String outputEncoding, | ||||
Project project) throws IOException { | Project project) throws IOException { | ||||
ResourceUtils.copyResource(new FileResource(sourceFile), new FileResource(destFile), | |||||
filters, filterChains, overwrite, preserveLastModified, append, inputEncoding, | |||||
outputEncoding, project); | |||||
copyFile(sourceFile, destFile, filters, filterChains, overwrite, | |||||
preserveLastModified, append, inputEncoding, outputEncoding, | |||||
project, /* force: */ false); | |||||
} | |||||
/** | |||||
* Convenience method to copy a file from a source to a | |||||
* destination specifying if token filtering must be used, if | |||||
* filter chains must be used, if source files may overwrite | |||||
* newer destination files and the last modified time of | |||||
* <code>destFile</code> file should be made equal | |||||
* to the last modified time of <code>sourceFile</code>. | |||||
* | |||||
* @param sourceFile the file to copy from. | |||||
* Must not be <code>null</code>. | |||||
* @param destFile the file to copy to. | |||||
* Must not be <code>null</code>. | |||||
* @param filters the collection of filters to apply to this copy. | |||||
* @param filterChains filterChains to apply during the copy. | |||||
* @param overwrite Whether or not the destination file should be | |||||
* overwritten if it already exists. | |||||
* @param preserveLastModified Whether or not the last modified time of | |||||
* the resulting file should be set to that | |||||
* of the source file. | |||||
* @param append whether to append to the destination file. | |||||
* @param inputEncoding the encoding used to read the files. | |||||
* @param outputEncoding the encoding used to write the files. | |||||
* @param project the project instance. | |||||
* @param force whether to overwrite read-only destination files. | |||||
* | |||||
* @throws IOException if the copying fails. | |||||
* | |||||
* @since Ant 1.8.2 | |||||
*/ | |||||
public void copyFile(File sourceFile, File destFile, | |||||
FilterSetCollection filters, Vector filterChains, | |||||
boolean overwrite, boolean preserveLastModified, | |||||
boolean append, | |||||
String inputEncoding, String outputEncoding, | |||||
Project project, boolean force) throws IOException { | |||||
ResourceUtils.copyResource(new FileResource(sourceFile), | |||||
new FileResource(destFile), | |||||
filters, filterChains, overwrite, | |||||
preserveLastModified, append, inputEncoding, | |||||
outputEncoding, project, force); | |||||
} | } | ||||
// CheckStyle:ParameterNumberCheck ON | // CheckStyle:ParameterNumberCheck ON | ||||
@@ -339,10 +339,51 @@ public class ResourceUtils { | |||||
*/ | */ | ||||
public static void copyResource(Resource source, Resource dest, | public static void copyResource(Resource source, Resource dest, | ||||
FilterSetCollection filters, Vector filterChains, | FilterSetCollection filters, Vector filterChains, | ||||
boolean overwrite, boolean preserveLastModified, boolean append, | |||||
boolean overwrite, boolean preserveLastModified, | |||||
boolean append, | |||||
String inputEncoding, String outputEncoding, | String inputEncoding, String outputEncoding, | ||||
Project project) | Project project) | ||||
throws IOException { | throws IOException { | ||||
copyResource(source, dest, filters, filterChains, overwrite, | |||||
preserveLastModified, append, inputEncoding, | |||||
outputEncoding, project, /* force: */ false); | |||||
} | |||||
/** | |||||
* Convenience method to copy content from one Resource to another | |||||
* specifying whether token filtering must be used, whether filter chains | |||||
* must be used, whether newer destination files may be overwritten and | |||||
* whether the last modified time of <code>dest</code> file should be made | |||||
* equal to the last modified time of <code>source</code>. | |||||
* | |||||
* @param source the Resource to copy from. | |||||
* Must not be <code>null</code>. | |||||
* @param dest the Resource to copy to. | |||||
* Must not be <code>null</code>. | |||||
* @param filters the collection of filters to apply to this copy. | |||||
* @param filterChains filterChains to apply during the copy. | |||||
* @param overwrite Whether or not the destination Resource should be | |||||
* overwritten if it already exists. | |||||
* @param preserveLastModified Whether or not the last modified time of | |||||
* the destination Resource should be set to that | |||||
* of the source. | |||||
* @param append Whether to append to an Appendable Resource. | |||||
* @param inputEncoding the encoding used to read the files. | |||||
* @param outputEncoding the encoding used to write the files. | |||||
* @param project the project instance. | |||||
* @param force whether read-only taret files will be overwritten | |||||
* | |||||
* @throws IOException if the copying fails. | |||||
* | |||||
* @since Ant 1.8.2 | |||||
*/ | |||||
public static void copyResource(Resource source, Resource dest, | |||||
FilterSetCollection filters, Vector filterChains, | |||||
boolean overwrite, boolean preserveLastModified, | |||||
boolean append, | |||||
String inputEncoding, String outputEncoding, | |||||
Project project, boolean force) | |||||
throws IOException { | |||||
if (!(overwrite || SelectorUtils.isOutOfDate(source, dest, FileUtils.getFileUtils() | if (!(overwrite || SelectorUtils.isOutOfDate(source, dest, FileUtils.getFileUtils() | ||||
.getFileTimestampGranularity()))) { | .getFileTimestampGranularity()))) { | ||||
return; | return; | ||||
@@ -351,6 +392,21 @@ public class ResourceUtils { | |||||
&& filters.hasFilters()); | && filters.hasFilters()); | ||||
final boolean filterChainsAvailable = (filterChains != null | final boolean filterChainsAvailable = (filterChains != null | ||||
&& filterChains.size() > 0); | && filterChains.size() > 0); | ||||
File destFile = null; | |||||
if (dest.as(FileProvider.class) != null) { | |||||
destFile = ((FileProvider) dest.as(FileProvider.class)).getFile(); | |||||
} | |||||
if (destFile != null && destFile.isFile() && !destFile.canWrite()) { | |||||
if (!force) { | |||||
throw new IOException("can't write to read-only destination " | |||||
+ "file " + destFile); | |||||
} else if (!FILE_UTILS.tryHardToDelete(destFile)) { | |||||
throw new IOException("failed to delete read-only " | |||||
+ "destination file " + destFile); | |||||
} | |||||
} | |||||
if (filterSetsAvailable) { | if (filterSetsAvailable) { | ||||
BufferedReader in = null; | BufferedReader in = null; | ||||
BufferedWriter out = null; | BufferedWriter out = null; | ||||
@@ -444,11 +500,9 @@ public class ResourceUtils { | |||||
FileUtils.close(in); | FileUtils.close(in); | ||||
} | } | ||||
} else if (source.as(FileProvider.class) != null | } else if (source.as(FileProvider.class) != null | ||||
&& dest.as(FileProvider.class) != null) { | |||||
&& destFile != null) { | |||||
File sourceFile = | File sourceFile = | ||||
((FileProvider) source.as(FileProvider.class)).getFile(); | ((FileProvider) source.as(FileProvider.class)).getFile(); | ||||
File destFile = | |||||
((FileProvider) dest.as(FileProvider.class)).getFile(); | |||||
File parent = destFile.getParentFile(); | File parent = destFile.getParentFile(); | ||||
if (parent != null && !parent.isDirectory() | if (parent != null && !parent.isDirectory() | ||||
@@ -314,18 +314,69 @@ public class NullByteStreamResource extends Resource { | |||||
<attrib file="${output}/${file}" readonly="true"/> | <attrib file="${output}/${file}" readonly="true"/> | ||||
</target> | </target> | ||||
<target name="XtestCopyOverReadOnlyFile" depends="makeFileUnwritable"> | |||||
<target name="testCopyOverReadOnlyFile" depends="makeFileUnwritable"> | |||||
<sleep seconds="2"/> | <sleep seconds="2"/> | ||||
<touch file="${input}/${file}"/> | <touch file="${input}/${file}"/> | ||||
<copy toDir="${output}"> | |||||
<au:expectfailure | |||||
expectedMessage="can't write to read-only destination file "> | |||||
<copy toDir="${output}"> | |||||
<fileset dir="${input}"/> | |||||
</copy> | |||||
</au:expectfailure> | |||||
</target> | |||||
<target name="testFilteredCopyOverReadOnlyFile" depends="makeFileUnwritable"> | |||||
<sleep seconds="2"/> | |||||
<touch file="${input}/${file}"/> | |||||
<au:expectfailure | |||||
expectedMessage="can't write to read-only destination file "> | |||||
<copy toDir="${output}"> | |||||
<fileset dir="${input}"/> | |||||
<filterset> | |||||
<filter token="foo" value="bar"/> | |||||
</filterset> | |||||
</copy> | |||||
</au:expectfailure> | |||||
</target> | |||||
<target name="testCopyOverReadOnlyFileWithOverwrite" | |||||
depends="makeFileUnwritable"> | |||||
<touch file="${input}/${file}"/> | |||||
<au:expectfailure | |||||
expectedMessage="can't write to read-only destination file "> | |||||
<copy toDir="${output}" overwrite="true"> | |||||
<fileset dir="${input}"/> | |||||
</copy> | |||||
</au:expectfailure> | |||||
</target> | |||||
<target name="testFilteredCopyOverReadOnlyFileWithOverwrite" | |||||
depends="makeFileUnwritable"> | |||||
<touch file="${input}/${file}"/> | |||||
<au:expectfailure | |||||
expectedMessage="can't write to read-only destination file "> | |||||
<copy toDir="${output}" overwrite="true"> | |||||
<fileset dir="${input}"/> | |||||
<filterset> | |||||
<filter token="foo" value="bar"/> | |||||
</filterset> | |||||
</copy> | |||||
</au:expectfailure> | |||||
</target> | |||||
<target name="testForcedCopyOverReadOnlyFile" depends="makeFileUnwritable"> | |||||
<sleep seconds="2"/> | |||||
<touch file="${input}/${file}"/> | |||||
<copy toDir="${output}" force="true"> | |||||
<fileset dir="${input}"/> | <fileset dir="${input}"/> | ||||
</copy> | </copy> | ||||
</target> | </target> | ||||
<target name="testFilteredCopyOverReadOnlyFile" depends="makeFileUnwritable"> | |||||
<target name="testForcedFilteredCopyOverReadOnlyFile" | |||||
depends="makeFileUnwritable"> | |||||
<sleep seconds="2"/> | <sleep seconds="2"/> | ||||
<touch file="${input}/${file}"/> | <touch file="${input}/${file}"/> | ||||
<copy toDir="${output}"> | |||||
<copy toDir="${output}" force="true"> | |||||
<fileset dir="${input}"/> | <fileset dir="${input}"/> | ||||
<filterset> | <filterset> | ||||
<filter token="foo" value="bar"/> | <filter token="foo" value="bar"/> | ||||
@@ -333,18 +384,18 @@ public class NullByteStreamResource extends Resource { | |||||
</copy> | </copy> | ||||
</target> | </target> | ||||
<target name="XtestCopyOverReadOnlyFileWithOverwrite" | |||||
<target name="testForcedCopyOverReadOnlyFileWithOverwrite" | |||||
depends="makeFileUnwritable"> | depends="makeFileUnwritable"> | ||||
<touch file="${input}/${file}"/> | <touch file="${input}/${file}"/> | ||||
<copy toDir="${output}" overwrite="true"> | |||||
<copy toDir="${output}" overwrite="true" force="true"> | |||||
<fileset dir="${input}"/> | <fileset dir="${input}"/> | ||||
</copy> | </copy> | ||||
</target> | </target> | ||||
<target name="testFilteredCopyOverReadOnlyFileWithOverwrite" | |||||
<target name="testForcedFilteredCopyOverReadOnlyFileWithOverwrite" | |||||
depends="makeFileUnwritable"> | depends="makeFileUnwritable"> | ||||
<touch file="${input}/${file}"/> | <touch file="${input}/${file}"/> | ||||
<copy toDir="${output}" overwrite="true"> | |||||
<copy toDir="${output}" overwrite="true" force="true"> | |||||
<fileset dir="${input}"/> | <fileset dir="${input}"/> | ||||
<filterset> | <filterset> | ||||
<filter token="foo" value="bar"/> | <filter token="foo" value="bar"/> | ||||