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()); | ||||