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"/> | ||||