git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277393 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -61,4 +61,18 @@ | |||||
| <fileset dir="${src}" excludes="**/d"/> | <fileset dir="${src}" excludes="**/d"/> | ||||
| </sync> | </sync> | ||||
| </target> | </target> | ||||
| <target name="copynoremove" depends="setup"> | |||||
| <mkdir dir="${src}/a/b/c"/> | |||||
| <touch file="${src}/a/b/c/d"/> | |||||
| <mkdir dir="${dest}/e"/> | |||||
| <touch file="${dest}/e/f"/> | |||||
| <sync todir="${dest}"> | |||||
| <fileset dir="${src}"/> | |||||
| <deletefromtarget> | |||||
| <exclude name="e/f"/> | |||||
| </deletefromtarget> | |||||
| </sync> | |||||
| </target> | |||||
| </project> | </project> | ||||
| @@ -584,13 +584,7 @@ public class DirectoryScanner | |||||
| } else { | } else { | ||||
| this.includes = new String[includes.length]; | this.includes = new String[includes.length]; | ||||
| for (int i = 0; i < includes.length; i++) { | for (int i = 0; i < includes.length; i++) { | ||||
| String pattern; | |||||
| pattern = includes[i].replace('/', File.separatorChar).replace( | |||||
| '\\', File.separatorChar); | |||||
| if (pattern.endsWith(File.separator)) { | |||||
| pattern += "**"; | |||||
| } | |||||
| this.includes[i] = pattern; | |||||
| this.includes[i] = normalizePattern(includes[i]); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -614,17 +608,60 @@ public class DirectoryScanner | |||||
| } else { | } else { | ||||
| this.excludes = new String[excludes.length]; | this.excludes = new String[excludes.length]; | ||||
| for (int i = 0; i < excludes.length; i++) { | for (int i = 0; i < excludes.length; i++) { | ||||
| String pattern; | |||||
| pattern = excludes[i].replace('/', File.separatorChar).replace( | |||||
| '\\', File.separatorChar); | |||||
| if (pattern.endsWith(File.separator)) { | |||||
| pattern += "**"; | |||||
| this.excludes[i] = normalizePattern(excludes[i]); | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Adds to the list of exclude patterns to use. All '/' and '\' | |||||
| * characters are replaced by <code>File.separatorChar</code>, so | |||||
| * the separator used need not match | |||||
| * <code>File.separatorChar</code>. | |||||
| * <p> | |||||
| * When a pattern ends with a '/' or '\', "**" is appended. | |||||
| * | |||||
| * @param excludes A list of exclude patterns. | |||||
| * May be <code>null</code>, in which case the | |||||
| * exclude patterns don't get changed at all. | |||||
| * | |||||
| * @since Ant 1.7 | |||||
| */ | |||||
| public void addExcludes(String[] excludes) { | |||||
| if (excludes != null) { | |||||
| if (this.excludes != null) { | |||||
| String[] tmp = new String[excludes.length | |||||
| + this.excludes.length]; | |||||
| System.arraycopy(this.excludes, 0, tmp, 0, | |||||
| this.excludes.length); | |||||
| for (int i = 0; i < excludes.length; i++) { | |||||
| tmp[this.excludes.length + i] = | |||||
| normalizePattern(excludes[i]); | |||||
| } | } | ||||
| this.excludes[i] = pattern; | |||||
| this.excludes = tmp; | |||||
| } else { | |||||
| setExcludes(excludes); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * All '/' and '\' characters are replaced by | |||||
| * <code>File.separatorChar</code>, so the separator used need not | |||||
| * match <code>File.separatorChar</code>. | |||||
| * | |||||
| * <p> When a pattern ends with a '/' or '\', "**" is appended. | |||||
| * | |||||
| * @since Ant 1.7 | |||||
| */ | |||||
| private static String normalizePattern(String p) { | |||||
| String pattern = p.replace('/', File.separatorChar) | |||||
| .replace('\\', File.separatorChar); | |||||
| if (pattern.endsWith(File.separator)) { | |||||
| pattern += "**"; | |||||
| } | |||||
| return pattern; | |||||
| } | |||||
| /** | /** | ||||
| * Sets the selectors that will select the filelist. | * Sets the selectors that will select the filelist. | ||||
| @@ -32,6 +32,7 @@ import org.apache.tools.ant.BuildException; | |||||
| import org.apache.tools.ant.DirectoryScanner; | import org.apache.tools.ant.DirectoryScanner; | ||||
| import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
| import org.apache.tools.ant.Task; | import org.apache.tools.ant.Task; | ||||
| import org.apache.tools.ant.types.AbstractFileSet; | |||||
| import org.apache.tools.ant.types.FileSet; | import org.apache.tools.ant.types.FileSet; | ||||
| /** | /** | ||||
| @@ -55,6 +56,9 @@ public class Sync extends Task { | |||||
| // Same as regular <copy> task... see at end-of-file! | // Same as regular <copy> task... see at end-of-file! | ||||
| private MyCopy myCopy; | private MyCopy myCopy; | ||||
| // Similar to a fileset, but doesn't allow dir attribute to be set | |||||
| private SyncTarget syncTarget; | |||||
| // Override Task#init | // Override Task#init | ||||
| /** | /** | ||||
| * @see Task#init() | * @see Task#init() | ||||
| @@ -152,13 +156,21 @@ public class Sync extends Task { | |||||
| */ | */ | ||||
| private int[] removeOrphanFiles(Set nonOrphans, File toDir) { | private int[] removeOrphanFiles(Set nonOrphans, File toDir) { | ||||
| int[] removedCount = new int[] {0, 0}; | int[] removedCount = new int[] {0, 0}; | ||||
| DirectoryScanner ds = new DirectoryScanner(); | |||||
| ds.setBasedir(toDir); | |||||
| String[] excls = | String[] excls = | ||||
| (String[]) nonOrphans.toArray(new String[nonOrphans.size() + 1]); | (String[]) nonOrphans.toArray(new String[nonOrphans.size() + 1]); | ||||
| // want to keep toDir itself | // want to keep toDir itself | ||||
| excls[nonOrphans.size()] = ""; | excls[nonOrphans.size()] = ""; | ||||
| ds.setExcludes(excls); | |||||
| DirectoryScanner ds = null; | |||||
| if (syncTarget != null) { | |||||
| syncTarget.setTargetDir(toDir); | |||||
| ds = syncTarget.getDirectoryScanner(getProject()); | |||||
| } else { | |||||
| ds = new DirectoryScanner(); | |||||
| ds.setBasedir(toDir); | |||||
| } | |||||
| ds.addExcludes(excls); | |||||
| ds.scan(); | ds.scan(); | ||||
| String[] files = ds.getIncludedFiles(); | String[] files = ds.getIncludedFiles(); | ||||
| for (int i = 0; i < files.length; i++) { | for (int i = 0; i < files.length; i++) { | ||||
| @@ -288,6 +300,23 @@ public class Sync extends Task { | |||||
| myCopy.setGranularity(granularity); | myCopy.setGranularity(granularity); | ||||
| } | } | ||||
| /** | |||||
| * A container for patterns and selectors that can be used to | |||||
| * specify files that should be kept in the target even if they | |||||
| * are not present in any source directory. | |||||
| * | |||||
| * <p>You must not invoke this method more than once.</p> | |||||
| * | |||||
| * @since Ant 1.7 | |||||
| */ | |||||
| public void addDeleteFromTarget(SyncTarget s) { | |||||
| if (syncTarget != null) { | |||||
| throw new BuildException("you must not specify multiple " | |||||
| + "deletefromtaget elements."); | |||||
| } | |||||
| syncTarget = s; | |||||
| } | |||||
| /** | /** | ||||
| * Subclass Copy in order to access it's file/dir maps. | * Subclass Copy in order to access it's file/dir maps. | ||||
| */ | */ | ||||
| @@ -336,6 +365,31 @@ public class Sync extends Task { | |||||
| } | } | ||||
| /** | |||||
| * Inner class used to hold exclude patterns and selectors to save | |||||
| * stuff that happens to live in the target directory but should | |||||
| * not get removed. | |||||
| * | |||||
| * @since Ant 1.7 | |||||
| */ | |||||
| public static class SyncTarget extends AbstractFileSet { | |||||
| public SyncTarget() { | |||||
| super(); | |||||
| setDefaultexcludes(false); | |||||
| } | |||||
| public void setDir(File dir) throws BuildException { | |||||
| throw new BuildException("synctarget doesn't support the dir " | |||||
| + "attribute"); | |||||
| } | |||||
| private void setTargetDir(File dir) throws BuildException { | |||||
| super.setDir(dir); | |||||
| } | |||||
| } | |||||
| /** | /** | ||||
| * Pseudo-assert method. | * Pseudo-assert method. | ||||
| */ | */ | ||||
| @@ -1,5 +1,5 @@ | |||||
| /* | /* | ||||
| * Copyright 2004 The Apache Software Foundation | |||||
| * Copyright 2004-2005 The Apache Software Foundation | |||||
| * | * | ||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| * you may not use this file except in compliance with the License. | * you may not use this file except in compliance with the License. | ||||
| @@ -83,6 +83,15 @@ public class SyncTest extends BuildFileTest { | |||||
| assertDebuglogContaining("Removed 2 dangling directories from"); | assertDebuglogContaining("Removed 2 dangling directories from"); | ||||
| } | } | ||||
| public void testCopyNoRemove() { | |||||
| executeTarget("copynoremove"); | |||||
| String d = getProject().getProperty("dest") + "/a/b/c/d"; | |||||
| assertFileIsPresent(d); | |||||
| String f = getProject().getProperty("dest") + "/e/f"; | |||||
| assertFileIsPresent(f); | |||||
| assertTrue(getFullLog().indexOf("Removing orphan file:") == -1); | |||||
| } | |||||
| public void assertFileIsPresent(String f) { | public void assertFileIsPresent(String f) { | ||||
| assertTrue("Expected file " + f, | assertTrue("Expected file " + f, | ||||
| getProject().resolveFile(f).exists()); | getProject().resolveFile(f).exists()); | ||||