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: | |||
------------------------------------------- | |||
* 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: | |||
----------- | |||
@@ -89,6 +89,12 @@ operation as <a href="../CoreTypes/filterset.html">filtersets</a>. | |||
files are newer.</td> | |||
<td valign="top" align="center">No; defaults to false.</td> | |||
</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> | |||
<td valign="top">filtering</td> | |||
<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 outputEncoding = null; | |||
private long granularity = 0; | |||
private boolean force = false; | |||
/** | |||
* Copy task constructor. | |||
@@ -228,6 +229,17 @@ public class Copy extends Task { | |||
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" | |||
* into a single directory. If there are multiple files with | |||
@@ -838,10 +850,13 @@ public class Copy extends Task { | |||
executionFilters | |||
.addFilterSet((FilterSet) filterEnum.nextElement()); | |||
} | |||
fileUtils.copyFile(fromFile, toFile, executionFilters, | |||
fileUtils.copyFile(new File(fromFile), new File(toFile), | |||
executionFilters, | |||
filterChains, forceOverwrite, | |||
preserveLastModified, inputEncoding, | |||
outputEncoding, getProject()); | |||
preserveLastModified, | |||
/* append: */ false, inputEncoding, | |||
outputEncoding, getProject(), | |||
force); | |||
} catch (IOException ioe) { | |||
String msg = "Failed to copy " + fromFile + " to " + toFile | |||
+ " due to " + getDueTo(ioe); | |||
@@ -928,9 +943,11 @@ public class Copy extends Task { | |||
filterChains, | |||
forceOverwrite, | |||
preserveLastModified, | |||
/* append: */ false, | |||
inputEncoding, | |||
outputEncoding, | |||
getProject()); | |||
getProject(), | |||
force); | |||
} catch (IOException ioe) { | |||
String msg = "Failed to copy " + fromResource | |||
+ " to " + toFile | |||
@@ -513,12 +513,55 @@ public class FileUtils { | |||
*/ | |||
public void copyFile(File sourceFile, File destFile, | |||
FilterSetCollection filters, Vector filterChains, | |||
boolean overwrite, boolean preserveLastModified, boolean append, | |||
boolean overwrite, boolean preserveLastModified, | |||
boolean append, | |||
String inputEncoding, String outputEncoding, | |||
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 | |||
@@ -339,10 +339,51 @@ public class ResourceUtils { | |||
*/ | |||
public static void copyResource(Resource source, Resource dest, | |||
FilterSetCollection filters, Vector filterChains, | |||
boolean overwrite, boolean preserveLastModified, boolean append, | |||
boolean overwrite, boolean preserveLastModified, | |||
boolean append, | |||
String inputEncoding, String outputEncoding, | |||
Project project) | |||
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() | |||
.getFileTimestampGranularity()))) { | |||
return; | |||
@@ -351,6 +392,21 @@ public class ResourceUtils { | |||
&& filters.hasFilters()); | |||
final boolean filterChainsAvailable = (filterChains != null | |||
&& 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) { | |||
BufferedReader in = null; | |||
BufferedWriter out = null; | |||
@@ -444,11 +500,9 @@ public class ResourceUtils { | |||
FileUtils.close(in); | |||
} | |||
} else if (source.as(FileProvider.class) != null | |||
&& dest.as(FileProvider.class) != null) { | |||
&& destFile != null) { | |||
File sourceFile = | |||
((FileProvider) source.as(FileProvider.class)).getFile(); | |||
File destFile = | |||
((FileProvider) dest.as(FileProvider.class)).getFile(); | |||
File parent = destFile.getParentFile(); | |||
if (parent != null && !parent.isDirectory() | |||
@@ -314,18 +314,69 @@ public class NullByteStreamResource extends Resource { | |||
<attrib file="${output}/${file}" readonly="true"/> | |||
</target> | |||
<target name="XtestCopyOverReadOnlyFile" depends="makeFileUnwritable"> | |||
<target name="testCopyOverReadOnlyFile" depends="makeFileUnwritable"> | |||
<sleep seconds="2"/> | |||
<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}"/> | |||
</copy> | |||
</target> | |||
<target name="testFilteredCopyOverReadOnlyFile" depends="makeFileUnwritable"> | |||
<target name="testForcedFilteredCopyOverReadOnlyFile" | |||
depends="makeFileUnwritable"> | |||
<sleep seconds="2"/> | |||
<touch file="${input}/${file}"/> | |||
<copy toDir="${output}"> | |||
<copy toDir="${output}" force="true"> | |||
<fileset dir="${input}"/> | |||
<filterset> | |||
<filter token="foo" value="bar"/> | |||
@@ -333,18 +384,18 @@ public class NullByteStreamResource extends Resource { | |||
</copy> | |||
</target> | |||
<target name="XtestCopyOverReadOnlyFileWithOverwrite" | |||
<target name="testForcedCopyOverReadOnlyFileWithOverwrite" | |||
depends="makeFileUnwritable"> | |||
<touch file="${input}/${file}"/> | |||
<copy toDir="${output}" overwrite="true"> | |||
<copy toDir="${output}" overwrite="true" force="true"> | |||
<fileset dir="${input}"/> | |||
</copy> | |||
</target> | |||
<target name="testFilteredCopyOverReadOnlyFileWithOverwrite" | |||
<target name="testForcedFilteredCopyOverReadOnlyFileWithOverwrite" | |||
depends="makeFileUnwritable"> | |||
<touch file="${input}/${file}"/> | |||
<copy toDir="${output}" overwrite="true"> | |||
<copy toDir="${output}" overwrite="true" force="true"> | |||
<fileset dir="${input}"/> | |||
<filterset> | |||
<filter token="foo" value="bar"/> | |||