git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@943056 13f79535-47bb-0310-9956-ffa450edef68master
@@ -4,13 +4,13 @@ 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. | |||||
* Prior to Ant 1.8.0 the <copy> task and several other tasks 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> and <move> task now have a new force attribute that can | |||||
be used to make the task overwrite read-only destinations. | |||||
Bugzilla Report 49261. | Bugzilla Report 49261. | ||||
Fixed bugs: | Fixed bugs: | ||||
@@ -85,6 +85,12 @@ there is a directory by the same name in <i>todir</i>, the action will fail. | |||||
files are newer (default is "true")</td> | files are newer (default is "true")</td> | ||||
<td valign="top" align="center">No</td> | <td valign="top" align="center">No</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 should take place during | <td valign="top">indicates whether token filtering should take place during | ||||
@@ -240,6 +240,15 @@ public class Copy extends Task { | |||||
force = f; | force = f; | ||||
} | } | ||||
/** | |||||
* Whether read-only destinations will be overwritten. | |||||
* | |||||
* @since Ant 1.8.2 | |||||
*/ | |||||
public boolean getForce() { | |||||
return force; | |||||
} | |||||
/** | /** | ||||
* 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 | ||||
@@ -856,7 +865,7 @@ public class Copy extends Task { | |||||
preserveLastModified, | preserveLastModified, | ||||
/* append: */ false, inputEncoding, | /* append: */ false, inputEncoding, | ||||
outputEncoding, getProject(), | outputEncoding, getProject(), | ||||
force); | |||||
getForce()); | |||||
} 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); | ||||
@@ -947,7 +956,7 @@ public class Copy extends Task { | |||||
inputEncoding, | inputEncoding, | ||||
outputEncoding, | outputEncoding, | ||||
getProject(), | getProject(), | ||||
force); | |||||
getForce()); | |||||
} catch (IOException ioe) { | } catch (IOException ioe) { | ||||
String msg = "Failed to copy " + fromResource | String msg = "Failed to copy " + fromResource | ||||
+ " to " + toFile | + " to " + toFile | ||||
@@ -233,9 +233,10 @@ public class Move extends Copy { | |||||
getFilterChains(), | getFilterChains(), | ||||
forceOverwrite, | forceOverwrite, | ||||
getPreserveLastModified(), | getPreserveLastModified(), | ||||
/* append: */ false, | |||||
getEncoding(), | getEncoding(), | ||||
getOutputEncoding(), | getOutputEncoding(), | ||||
getProject()); | |||||
getProject(), getForce()); | |||||
} catch (IOException ioe) { | } catch (IOException ioe) { | ||||
String msg = "Failed to copy " + fromFile | String msg = "Failed to copy " + fromFile | ||||
+ " to " + toFile + " due to " + ioe.getMessage(); | + " to " + toFile + " due to " + ioe.getMessage(); | ||||
@@ -329,6 +330,18 @@ public class Move extends Copy { | |||||
|| getFilterChains().size() > 0) { | || getFilterChains().size() > 0) { | ||||
return false; | return false; | ||||
} | } | ||||
// identical logic lives in ResourceUtils.copyResource(): | |||||
if (destFile.isFile() && !destFile.canWrite()) { | |||||
if (!getForce()) { | |||||
throw new IOException("can't replace read-only destination " | |||||
+ "file " + destFile); | |||||
} else if (!getFileUtils().tryHardToDelete(destFile)) { | |||||
throw new IOException("failed to delete read-only " | |||||
+ "destination file " + destFile); | |||||
} | |||||
} | |||||
// identical logic lives in FileUtils.rename(): | // identical logic lives in FileUtils.rename(): | ||||
File parent = destFile.getParentFile(); | File parent = destFile.getParentFile(); | ||||
if (parent != null && !parent.exists()) { | if (parent != null && !parent.exists()) { | ||||
@@ -124,4 +124,111 @@ | |||||
value="Y"/> | value="Y"/> | ||||
</target> | </target> | ||||
<!-- stolen from ../types/readwrite-test.xml - create a read-only file --> | |||||
<property name="file" value="testfile"/> | |||||
<condition property="unix"> | |||||
<os family="unix"/> | |||||
</condition> | |||||
<target name="createTestdir"> | |||||
<mkdir dir="${output}"/> | |||||
<mkdir dir="${input}"/> | |||||
<touch file="${output}/${file}"/> | |||||
</target> | |||||
<target name="makeFileUnwritable" | |||||
depends="createTestdir,makeFileUnwritable-Unix,makeFileUnwritable-Windows"/> | |||||
<target name="makeFileUnwritable-Unix" id="unix"> | |||||
<chmod file="${output}/${file}" perm="444"/> | |||||
</target> | |||||
<target name="makeFileUnwritable-Windows" unless="unix"> | |||||
<attrib file="${output}/${file}" readonly="true"/> | |||||
</target> | |||||
<target name="testMoveOverReadOnlyFile" depends="makeFileUnwritable"> | |||||
<sleep seconds="2"/> | |||||
<touch file="${input}/${file}"/> | |||||
<au:expectfailure | |||||
expectedMessage="can't replace read-only destination file "> | |||||
<move toDir="${output}"> | |||||
<fileset dir="${input}"/> | |||||
</move> | |||||
</au:expectfailure> | |||||
</target> | |||||
<target name="testFilteredMoveOverReadOnlyFile" depends="makeFileUnwritable"> | |||||
<sleep seconds="2"/> | |||||
<touch file="${input}/${file}"/> | |||||
<au:expectfailure | |||||
expectedMessage="can't write to read-only destination file "> | |||||
<move toDir="${output}"> | |||||
<fileset dir="${input}"/> | |||||
<filterset> | |||||
<filter token="foo" value="bar"/> | |||||
</filterset> | |||||
</move> | |||||
</au:expectfailure> | |||||
</target> | |||||
<target name="testMoveOverReadOnlyFileWithOverwrite" | |||||
depends="makeFileUnwritable"> | |||||
<touch file="${input}/${file}"/> | |||||
<au:expectfailure | |||||
expectedMessage="can't replace read-only destination file "> | |||||
<move toDir="${output}" overwrite="true"> | |||||
<fileset dir="${input}"/> | |||||
</move> | |||||
</au:expectfailure> | |||||
</target> | |||||
<target name="testFilteredMoveOverReadOnlyFileWithOverwrite" | |||||
depends="makeFileUnwritable"> | |||||
<touch file="${input}/${file}"/> | |||||
<au:expectfailure | |||||
expectedMessage="can't write to read-only destination file "> | |||||
<move toDir="${output}" overwrite="true"> | |||||
<fileset dir="${input}"/> | |||||
<filterset> | |||||
<filter token="foo" value="bar"/> | |||||
</filterset> | |||||
</move> | |||||
</au:expectfailure> | |||||
</target> | |||||
<target name="testForcedMoveOverReadOnlyFile" depends="makeFileUnwritable"> | |||||
<sleep seconds="2"/> | |||||
<touch file="${input}/${file}"/> | |||||
<move toDir="${output}" force="true"> | |||||
<fileset dir="${input}"/> | |||||
</move> | |||||
</target> | |||||
<target name="testForcedFilteredMoveOverReadOnlyFile" | |||||
depends="makeFileUnwritable"> | |||||
<sleep seconds="2"/> | |||||
<touch file="${input}/${file}"/> | |||||
<move toDir="${output}" force="true"> | |||||
<fileset dir="${input}"/> | |||||
<filterset> | |||||
<filter token="foo" value="bar"/> | |||||
</filterset> | |||||
</move> | |||||
</target> | |||||
<target name="testForcedMoveOverReadOnlyFileWithOverwrite" | |||||
depends="makeFileUnwritable"> | |||||
<touch file="${input}/${file}"/> | |||||
<move toDir="${output}" overwrite="true" force="true"> | |||||
<fileset dir="${input}"/> | |||||
</move> | |||||
</target> | |||||
<target name="testForcedFilteredMoveOverReadOnlyFileWithOverwrite" | |||||
depends="makeFileUnwritable"> | |||||
<touch file="${input}/${file}"/> | |||||
<move toDir="${output}" overwrite="true" force="true"> | |||||
<fileset dir="${input}"/> | |||||
<filterset> | |||||
<filter token="foo" value="bar"/> | |||||
</filterset> | |||||
</move> | |||||
</target> | |||||
</project> | </project> |