git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277591 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -158,14 +158,6 @@ | |||
| </or> | |||
| </selector> | |||
| <!-- classes that should be present in Sun based JVMs, but not in | |||
| Kaffe for example --> | |||
| <selector id="needs.sun.tools"> | |||
| <or> | |||
| <filename name="${optional.package}/Javah*"/> | |||
| </or> | |||
| </selector> | |||
| <selector id="needs.sun.uue"> | |||
| <filename name="${ant.package}/taskdefs/email/UUMailer*"/> | |||
| </selector> | |||
| @@ -647,7 +639,6 @@ | |||
| <selector refid="needs.jdk1.3+" unless="jdk1.3+"/> | |||
| <selector refid="needs.jdk1.4+" unless="jdk1.4+"/> | |||
| <selector refid="needs.jdk1.5+" unless="jdk1.5+"/> | |||
| <selector refid="needs.sun.tools" unless="sun.tools.present"/> | |||
| <selector refid="needs.sun.uue" unless="sunuue.present"/> | |||
| <selector refid="needs.sun.b64" unless="base64.present"/> | |||
| @@ -1670,4 +1661,4 @@ | |||
| description="--> creates a minimum distribution in ./dist" | |||
| depends="dist-lite"/> | |||
| </project> | |||
| </project> | |||
| @@ -35,6 +35,17 @@ | |||
| </sync> | |||
| </target> | |||
| <target name="copyandremove-emptypreserve" 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}"/> | |||
| <preserveintarget/> | |||
| </sync> | |||
| </target> | |||
| <target name="emptycopy" depends="setup"> | |||
| <mkdir dir="${src}/a/b/c"/> | |||
| <touch file="${src}/a/b/c/d"/> | |||
| @@ -69,9 +80,22 @@ | |||
| <touch file="${dest}/e/f"/> | |||
| <sync todir="${dest}"> | |||
| <fileset dir="${src}"/> | |||
| <deletefromtarget> | |||
| <exclude name="e/f"/> | |||
| </deletefromtarget> | |||
| <preserveintarget> | |||
| <include name="e/f"/> | |||
| </preserveintarget> | |||
| </sync> | |||
| </target> | |||
| <target name="copynoremove-selectors" 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}"/> | |||
| <preserveintarget> | |||
| <filename name="e/f"/> | |||
| </preserveintarget> | |||
| </sync> | |||
| </target> | |||
| @@ -34,6 +34,9 @@ import org.apache.tools.ant.Project; | |||
| 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.PatternSet; | |||
| import org.apache.tools.ant.types.selectors.FileSelector; | |||
| import org.apache.tools.ant.types.selectors.NoneSelector; | |||
| /** | |||
| * Synchronize a local target directory from the files defined | |||
| @@ -163,8 +166,33 @@ public class Sync extends Task { | |||
| DirectoryScanner ds = null; | |||
| if (syncTarget != null) { | |||
| syncTarget.setTargetDir(toDir); | |||
| ds = syncTarget.getDirectoryScanner(getProject()); | |||
| FileSet fs = new FileSet(); | |||
| fs.setDir(toDir); | |||
| fs.setCaseSensitive(syncTarget.isCaseSensitive()); | |||
| fs.setFollowSymlinks(syncTarget.isFollowSymlinks()); | |||
| // preserveInTarget would find all files we want to keep, | |||
| // but we need to find all that we want to delete - so the | |||
| // meaning of all patterns and selectors must be inverted | |||
| PatternSet ps = syncTarget.mergePatterns(getProject()); | |||
| String[] excludes = ps.getExcludePatterns(getProject()); | |||
| fs.appendExcludes(ps.getIncludePatterns(getProject())); | |||
| fs.appendIncludes(ps.getExcludePatterns(getProject())); | |||
| fs.setDefaultexcludes(!syncTarget.getDefaultexcludes()); | |||
| // selectors are implicitly ANDed in DirectoryScanner. To | |||
| // revert their logic we wrap them into a <none> selector | |||
| // instead. | |||
| FileSelector[] s = syncTarget.getSelectors(getProject()); | |||
| if (s.length > 0) { | |||
| NoneSelector ns = new NoneSelector(); | |||
| for (int i = 0; i < s.length; i++) { | |||
| ns.appendSelector(s[i]); | |||
| } | |||
| fs.appendSelector(ns); | |||
| } | |||
| ds = fs.getDirectoryScanner(getProject()); | |||
| } else { | |||
| ds = new DirectoryScanner(); | |||
| ds.setBasedir(toDir); | |||
| @@ -180,7 +208,7 @@ public class Sync extends Task { | |||
| ++removedCount[1]; | |||
| } | |||
| String[] dirs = ds.getIncludedDirectories(); | |||
| // ds returns the directories as it has visited them. | |||
| // ds returns the directories in lexicographic order. | |||
| // iterating through the array backwards means we are deleting | |||
| // leaves before their parent nodes - thus making sure (well, | |||
| // more likely) that the directories are empty when we try to | |||
| @@ -306,13 +334,13 @@ public class Sync extends Task { | |||
| * are not present in any source directory. | |||
| * | |||
| * <p>You must not invoke this method more than once.</p> | |||
| * @param s a deletefromtarget nested element | |||
| * @param s a preserveintarget nested element | |||
| * @since Ant 1.7 | |||
| */ | |||
| public void addDeleteFromTarget(SyncTarget s) { | |||
| public void addPreserveInTarget(SyncTarget s) { | |||
| if (syncTarget != null) { | |||
| throw new BuildException("you must not specify multiple " | |||
| + "deletefromtaget elements."); | |||
| + "preserveintarget elements."); | |||
| } | |||
| syncTarget = s; | |||
| } | |||
| @@ -381,24 +409,19 @@ public class Sync extends Task { | |||
| */ | |||
| public SyncTarget() { | |||
| super(); | |||
| setDefaultexcludes(false); | |||
| } | |||
| /** | |||
| * Override AbstractFileSet#setDir(File) to disallow | |||
| * setting the directory. This is now set by #setTargetDir(File). | |||
| * setting the directory. | |||
| * @param dir ignored | |||
| * @throws BuildException always | |||
| */ | |||
| public void setDir(File dir) throws BuildException { | |||
| throw new BuildException("synctarget doesn't support the dir " | |||
| throw new BuildException("preserveintarget doesn't support the dir " | |||
| + "attribute"); | |||
| } | |||
| private void setTargetDir(File dir) throws BuildException { | |||
| super.setDir(dir); | |||
| } | |||
| } | |||
| /** | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| * Copyright 2002-2004 The Apache Software Foundation | |||
| * Copyright 2002-2005 The Apache Software Foundation | |||
| * | |||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||
| * you may not use this file except in compliance with the License. | |||
| @@ -63,7 +63,7 @@ public abstract class AbstractFileSet extends DataType | |||
| private File dir; | |||
| private boolean useDefaultExcludes = true; | |||
| private boolean isCaseSensitive = true; | |||
| private boolean caseSensitive = true; | |||
| private boolean followSymlinks = true; | |||
| /** | |||
| @@ -84,7 +84,7 @@ public abstract class AbstractFileSet extends DataType | |||
| this.additionalPatterns = fileset.additionalPatterns; | |||
| this.selectors = fileset.selectors; | |||
| this.useDefaultExcludes = fileset.useDefaultExcludes; | |||
| this.isCaseSensitive = fileset.isCaseSensitive; | |||
| this.caseSensitive = fileset.caseSensitive; | |||
| this.followSymlinks = fileset.followSymlinks; | |||
| setProject(fileset.getProject()); | |||
| } | |||
| @@ -215,6 +215,24 @@ public abstract class AbstractFileSet extends DataType | |||
| defaultPatterns.setIncludes(includes); | |||
| } | |||
| /** | |||
| * Appends <code>includes</code> to the current list of include | |||
| * patterns. | |||
| * | |||
| * @param includes array containing the include patterns. | |||
| * @since Ant 1.7 | |||
| */ | |||
| public void appendIncludes(String[] includes) { | |||
| if (isReference()) { | |||
| throw tooManyAttributes(); | |||
| } | |||
| if (includes != null) { | |||
| for (int i = 0; i < includes.length; i++) { | |||
| defaultPatterns.createInclude().setName(includes[i]); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * Appends <code>excludes</code> to the current list of exclude | |||
| * patterns. | |||
| @@ -230,6 +248,24 @@ public abstract class AbstractFileSet extends DataType | |||
| defaultPatterns.setExcludes(excludes); | |||
| } | |||
| /** | |||
| * Appends <code>excludes</code> to the current list of include | |||
| * patterns. | |||
| * | |||
| * @param excludes array containing the exclude patterns. | |||
| * @since Ant 1.7 | |||
| */ | |||
| public void appendExcludes(String[] excludes) { | |||
| if (isReference()) { | |||
| throw tooManyAttributes(); | |||
| } | |||
| if (excludes != null) { | |||
| for (int i = 0; i < excludes.length; i++) { | |||
| defaultPatterns.createExclude().setName(excludes[i]); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * Sets the <code>File</code> containing the includes patterns. | |||
| * | |||
| @@ -266,16 +302,38 @@ public abstract class AbstractFileSet extends DataType | |||
| this.useDefaultExcludes = useDefaultExcludes; | |||
| } | |||
| /** | |||
| * Whether default exclusions should be used or not. | |||
| * @since Ant 1.7 | |||
| */ | |||
| public boolean getDefaultexcludes() { | |||
| return (isReference()) | |||
| ? getRef(getProject()).getDefaultexcludes() : useDefaultExcludes; | |||
| } | |||
| /** | |||
| * Sets case sensitivity of the file system. | |||
| * | |||
| * @param isCaseSensitive <code>boolean</code>. | |||
| */ | |||
| public void setCaseSensitive(boolean isCaseSensitive) { | |||
| public void setCaseSensitive(boolean caseSensitive) { | |||
| if (isReference()) { | |||
| throw tooManyAttributes(); | |||
| } | |||
| this.isCaseSensitive = isCaseSensitive; | |||
| this.caseSensitive = caseSensitive; | |||
| } | |||
| /** | |||
| * Find out if the fileset is case sensitive. | |||
| * | |||
| * @return <code>boolean</code> indicating whether the fileset is | |||
| * case sensitive. | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| public boolean isCaseSensitive() { | |||
| return (isReference()) | |||
| ? getRef(getProject()).isCaseSensitive() : caseSensitive; | |||
| } | |||
| /** | |||
| @@ -365,16 +423,12 @@ public abstract class AbstractFileSet extends DataType | |||
| } | |||
| ds.setBasedir(dir); | |||
| final int count = additionalPatterns.size(); | |||
| for (int i = 0; i < count; i++) { | |||
| Object o = additionalPatterns.elementAt(i); | |||
| defaultPatterns.append((PatternSet) o, p); | |||
| } | |||
| PatternSet ps = mergePatterns(p); | |||
| p.log(getDataTypeName() + ": Setup scanner in dir " + dir | |||
| + " with " + defaultPatterns, Project.MSG_DEBUG); | |||
| + " with " + ps, Project.MSG_DEBUG); | |||
| ds.setIncludes(defaultPatterns.getIncludePatterns(p)); | |||
| ds.setExcludes(defaultPatterns.getExcludePatterns(p)); | |||
| ds.setIncludes(ps.getIncludePatterns(p)); | |||
| ds.setExcludes(ps.getExcludePatterns(p)); | |||
| if (ds instanceof SelectorScanner) { | |||
| SelectorScanner ss = (SelectorScanner) ds; | |||
| ss.setSelectors(getSelectors(p)); | |||
| @@ -382,7 +436,7 @@ public abstract class AbstractFileSet extends DataType | |||
| if (useDefaultExcludes) { | |||
| ds.addDefaultExcludes(); | |||
| } | |||
| ds.setCaseSensitive(isCaseSensitive); | |||
| ds.setCaseSensitive(caseSensitive); | |||
| } | |||
| /** | |||
| @@ -683,4 +737,44 @@ public abstract class AbstractFileSet extends DataType | |||
| } | |||
| } | |||
| /** | |||
| * @return the include patterns of the default pattern set and all | |||
| * nested patternsets. | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| public String[] mergeIncludes(Project p) { | |||
| return mergePatterns(p).getIncludePatterns(p); | |||
| } | |||
| /** | |||
| * @return the exclude patterns of the default pattern set and all | |||
| * nested patternsets. | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| public String[] mergeExcludes(Project p) { | |||
| return mergePatterns(p).getExcludePatterns(p); | |||
| } | |||
| /** | |||
| * @return the default patternset merged with the additional sets | |||
| * in a new PatternSet instance. | |||
| * | |||
| * @since Ant 1.7 | |||
| */ | |||
| public PatternSet mergePatterns(Project p) { | |||
| if (isReference()) { | |||
| return getRef(p).mergePatterns(p); | |||
| } | |||
| PatternSet ps = new PatternSet(); | |||
| ps.append(defaultPatterns, p); | |||
| final int count = additionalPatterns.size(); | |||
| for (int i = 0; i < count; i++) { | |||
| Object o = additionalPatterns.elementAt(i); | |||
| ps.append((PatternSet) o, p); | |||
| } | |||
| return ps; | |||
| } | |||
| } | |||
| @@ -74,7 +74,7 @@ public class CommandlineJava implements Cloneable { | |||
| * Specialized Environment class for System properties | |||
| */ | |||
| public static class SysProperties extends Environment implements Cloneable { | |||
| private Properties sys = null; | |||
| Properties sys = null; | |||
| private Vector propertySets = new Vector(); | |||
| /** | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| * Copyright 2002-2004 The Apache Software Foundation | |||
| * Copyright 2002-2005 The Apache Software Foundation | |||
| * | |||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||
| * you may not use this file except in compliance with the License. | |||
| @@ -34,6 +34,16 @@ public class NotSelector extends NoneSelector { | |||
| public NotSelector() { | |||
| } | |||
| /** | |||
| * Constructor that inverts the meaning of its argument. | |||
| * @param other the selector to invert | |||
| * @since Ant 1.7 | |||
| */ | |||
| public NotSelector(FileSelector other) { | |||
| this(); | |||
| appendSelector(other); | |||
| } | |||
| /** | |||
| * @return a string representation of the selector | |||
| */ | |||
| @@ -28,6 +28,7 @@ import org.apache.tools.ant.Project; | |||
| /** | |||
| * Special <CODE>PipedInputStream</CODE> that will not die | |||
| * when the writing <CODE>Thread</CODE> is no longer alive. | |||
| * @since Ant 1.6.2 | |||
| */ | |||
| public class LeadPipeInputStream extends PipedInputStream { | |||
| private ProjectComponent managingPc; | |||
| @@ -39,6 +40,16 @@ public class LeadPipeInputStream extends PipedInputStream { | |||
| super(); | |||
| } | |||
| /** | |||
| * Construct a new <CODE>LeadPipeInputStream</CODE> | |||
| * with the specified buffer size. | |||
| * @param size the size of the circular buffer. | |||
| */ | |||
| public LeadPipeInputStream(int size) { | |||
| super(); | |||
| setBufferSize(size); | |||
| } | |||
| /** | |||
| * Construct a new <CODE>LeadPipeInputStream</CODE> to pull | |||
| * from the specified <CODE>PipedOutputStream</CODE>. | |||
| @@ -49,6 +60,18 @@ public class LeadPipeInputStream extends PipedInputStream { | |||
| super(src); | |||
| } | |||
| /** | |||
| * Construct a new <CODE>LeadPipeInputStream</CODE> to pull | |||
| * from the specified <CODE>PipedOutputStream</CODE>, using a | |||
| * circular buffer of the specified size. | |||
| * @param src the <CODE>PipedOutputStream</CODE> source. | |||
| * @param size the size of the circular buffer. | |||
| */ | |||
| public LeadPipeInputStream(PipedOutputStream src, int size) throws IOException { | |||
| super(src); | |||
| setBufferSize(size); | |||
| } | |||
| //inherit doc | |||
| public synchronized int read() throws IOException { | |||
| int result = -1; | |||
| @@ -68,6 +91,28 @@ public class LeadPipeInputStream extends PipedInputStream { | |||
| return result; | |||
| } | |||
| /** | |||
| * Set the size of the buffer. | |||
| * @param size the new buffer size. Ignored if <= current size. | |||
| */ | |||
| public synchronized void setBufferSize(int size) { | |||
| if (size > buffer.length) { | |||
| byte[] newBuffer = new byte[size]; | |||
| if (in >= 0) { | |||
| if (in > out) { | |||
| System.arraycopy(buffer, out, newBuffer, out, in - out); | |||
| } else { | |||
| int outlen = buffer.length - out; | |||
| System.arraycopy(buffer, out, newBuffer, 0, outlen); | |||
| System.arraycopy(buffer, 0, newBuffer, outlen, in); | |||
| in+= outlen; | |||
| out = 0; | |||
| } | |||
| } | |||
| buffer = newBuffer; | |||
| } | |||
| } | |||
| /** | |||
| * Set a managing <CODE>Task</CODE> for | |||
| * this <CODE>LeadPipeInputStream</CODE>. | |||
| @@ -34,7 +34,7 @@ import junit.framework.TestSuite; | |||
| /** | |||
| * Very limited test class for Project. Waiting to be extended. | |||
| * | |||
| */ | |||
| */ | |||
| public class ProjectTest extends TestCase { | |||
| private Project p; | |||
| @@ -70,6 +70,17 @@ public class SyncTest extends BuildFileTest { | |||
| assertDebuglogContaining("Removed 1 dangling directory from"); | |||
| } | |||
| public void testCopyAndRemoveEmptyPreserve() { | |||
| executeTarget("copyandremove-emptypreserve"); | |||
| String d = getProject().getProperty("dest") + "/a/b/c/d"; | |||
| assertFileIsPresent(d); | |||
| String f = getProject().getProperty("dest") + "/e/f"; | |||
| assertFileIsNotPresent(f); | |||
| assertTrue(getFullLog().indexOf("Removing orphan file:") > -1); | |||
| assertDebuglogContaining("Removed 1 dangling file from"); | |||
| assertDebuglogContaining("Removed 1 dangling directory from"); | |||
| } | |||
| public void testEmptyDirCopyAndRemove() { | |||
| executeTarget("emptydircopyandremove"); | |||
| String d = getProject().getProperty("dest") + "/a/b/c/d"; | |||
| @@ -92,6 +103,15 @@ public class SyncTest extends BuildFileTest { | |||
| assertTrue(getFullLog().indexOf("Removing orphan file:") == -1); | |||
| } | |||
| public void testCopyNoRemoveSelectors() { | |||
| executeTarget("copynoremove-selectors"); | |||
| 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) { | |||
| assertTrue("Expected file " + f, | |||
| getProject().resolveFile(f).exists()); | |||