From 991d9d68708265b3c6981411ddf4bdc2682d858d Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Wed, 6 Nov 2002 15:07:23 +0000 Subject: [PATCH] New optional task . PR: 9808 Submitted by: Gus Heck git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@273502 13f79535-47bb-0310-9956-ffa450edef68 --- WHATSNEW | 2 + docs/manual/OptionalTasks/symlink.html | 124 +++ docs/manual/optionaltasklist.html | 1 + .../testcases/taskdefs/optional/symlink.xml | 277 ++++++ .../tools/ant/taskdefs/defaults.properties | 1 + .../ant/taskdefs/optional/unix/Symlink.java | 814 ++++++++++++++++++ .../taskdefs/optional/unix/SymlinkTest.java | 224 +++++ 7 files changed, 1443 insertions(+) create mode 100644 docs/manual/OptionalTasks/symlink.html create mode 100644 src/etc/testcases/taskdefs/optional/symlink.xml create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java create mode 100644 src/testcases/org/apache/tools/ant/taskdefs/optional/unix/SymlinkTest.java diff --git a/WHATSNEW b/WHATSNEW index 13342b8d1..63bba9b2a 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -85,6 +85,8 @@ Other changes: Checkouts now have the option of using repository timestamps, instead of current. +* new task that creates and maintains symbolic links. + Changes from Ant 1.5.1Beta1 to 1.5.1 ==================================== diff --git a/docs/manual/OptionalTasks/symlink.html b/docs/manual/OptionalTasks/symlink.html new file mode 100644 index 000000000..e7a840ca4 --- /dev/null +++ b/docs/manual/OptionalTasks/symlink.html @@ -0,0 +1,124 @@ + + + + +Symlink Task + + + + +

Symlink

+

Description

+

Manages symbolic links on Unix based platforms. Can be used to +make an individual link, delete a link, create multiple links from properties files, +or create properties files describing links in the specified directories. +Existing links are not overwritten by default. + +

FileSets are used to select a +set of links to record, or a set of property files to create links from.

+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AttributeDescriptionRequired
actionThe type of action to perform, may be "single", + "record", "recreate" or "delete".No, defaults to single.
linkThe name of the link to be created or deleted.required for + action="single" or "delete". Ignored in other actions.
resourceThe resource the link should point to.required for action="single". Ignored in other actions.
linkfilenameThe name of the properties file to create in + each included directory.required for action="record". + Ignored in other actions.
overwriteOverwrite existing links or not.No; defaults to false.
failonerrorStop build if true, log a warning message, but do not stop the build, + when the an error occurs if false. + No; defaults to true.
+

Parameters specified as nested elements

+ +

fileset

+

FileSets + are used when action = "record" to select directories and linknames to be recorded. + They are also used when action = "recreate" to specify both the name of the property + files to be processed, and the directories in which they can be found. At least one + fileset is required for each case.

+ +

Examples

+ +

Make a link named "foo" to a resource named "bar.foo" in subdir:

+
+  <symlink link="${dir.top}/foo" resource="${dir.top}/subdir/bar.foo"/>
+  
+ +

Record all links in subdir and it's descendants in files named + "dir.links"

+
+  <symlink action="record" linkfilename="dir.links">
+     <fileset dir="${dir.top}" includes="subdir/**" />
+  </symlink>
+  
+ +

Recreate the links recorded in the previous example:

+
+  <symlink action="recreate">
+     <fileset dir="${dir.top}" includes="subdir/**/dir.links" />  
+  </symlink>
+  
+ +

Delete a link named "foo": +

+ <symlink action="delete" link="${dir.top}/foo"/>
+ 
+ +

Java 1.2 and earlier: Due to limitations on executing system + level comands in Java versions earlier than 1.3 this task may have difficulty + operating with a relative path in ANT_HOME. The typical symptom is an + IOException where ant can't find /some/working/directory${ANT_HOME}/bin/antRun + or something similar. The workaround is to change your ANT_HOME environment + variable to an absolute path, which will remove the /some/working/directory portion + of the above path and allow ant to find the correct commandline execution script. + +

LIMITATIONS: Because Java has no direct support for + handling symlinks this task divines them by comparing canonical and + absolute paths. On non-unix systems this may cause false positives. + Furthermore, any operating system on which the command + ln -s <linkname> <resourcename> is not a valid + command on the comand line will not be able to use action="single" or + action="recreate". Action="record" and action=delete should still work. Finally, + the lack of support for symlinks in Java means that all links are recorded as + links to the canonical resource name. Therefore the link: + link --> subdir/dir/../foo.bar will be recorded as + link=subdir/foo.bar and restored as + link --> subdir/foo.bar

+ +

Copyright © 2002 Apache Software +Foundation. All rights Reserved.

+ + + diff --git a/docs/manual/optionaltasklist.html b/docs/manual/optionaltasklist.html index c984f73d9..ff3eeb625 100644 --- a/docs/manual/optionaltasklist.html +++ b/docs/manual/optionaltasklist.html @@ -58,6 +58,7 @@ Splash
Starteam Tasks
Stylebook
+Symlink
Telnet
Test
Translate
diff --git a/src/etc/testcases/taskdefs/optional/symlink.xml b/src/etc/testcases/taskdefs/optional/symlink.xml new file mode 100644 index 000000000..257361894 --- /dev/null +++ b/src/etc/testcases/taskdefs/optional/symlink.xml @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/org/apache/tools/ant/taskdefs/defaults.properties b/src/main/org/apache/tools/ant/taskdefs/defaults.properties index 20ef94d56..f07d56284 100644 --- a/src/main/org/apache/tools/ant/taskdefs/defaults.properties +++ b/src/main/org/apache/tools/ant/taskdefs/defaults.properties @@ -171,6 +171,7 @@ jarlib-available=org.apache.tools.ant.taskdefs.optional.extension.JarLibAvailabl jarlib-resolve=org.apache.tools.ant.taskdefs.optional.extension.JarLibResolveTask setproxy=org.apache.tools.ant.taskdefs.optional.net.SetProxy vbc=org.apache.tools.ant.taskdefs.optional.dotnet.VisualBasicCompile +symlink=org.apache.tools.ant.taskdefs.optional.unix.Symlink # deprecated ant tasks (kept for back compatibility) starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java b/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java new file mode 100644 index 000000000..93078d9da --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java @@ -0,0 +1,814 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +/* + * Since the initial version of this file was deveolped on the clock on + * an NSF grant I should say the following boilerplate: + * + * This material is based upon work supported by the National Science + * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and + * conclusions or recommendations expressed in this material are those + * of the author and do not necessarily reflect the views of the + * National Science Foundation. + */ + +package org.apache.tools.ant.taskdefs.optional.unix; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.io.OutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.BufferedOutputStream; +import java.io.FileNotFoundException; + +import java.util.Vector; +import java.util.Properties; +import java.util.Enumeration; +import java.util.Hashtable; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; + +import org.apache.tools.ant.util.FileUtils; + +import org.apache.tools.ant.types.FileSet; + +import org.apache.tools.ant.taskdefs.Delete; +import org.apache.tools.ant.taskdefs.Execute; + +/** + * Creates, Records and Restores Symlinks. + * + *

This task performs several related operations. In the most trivial, + * and default usage, it creates a link specified in the link atribute to + * a resource specified in the resource atribute. The second usage of this + * task is to traverses a directory structure specified by a fileset, + * and write a properties file in each included directory describing the + * links found in that directory. The third usage is to traverse a + * directory structure specified by a fileset, looking for properties files + * (also specified as included in the fileset) and recreate the links + * that have been previously recorded for each directory. Finally, it can be + * used to remove a symlink without deleting the file or directory it points + * to. + * + *

Examples of use: + * + *

Make a link named "foo" to a resource named "bar.foo" in subdir: + *

+ * <symlink link="${dir.top}/foo" resource="${dir.top}/subdir/bar.foo"/>
+ * 
+ * + *

Record all links in subdir and it's descendants in files named + * "dir.links" + *

+ * <symlink action="record" linkfilename="dir.links">
+ *    <fileset dir="${dir.top}" includes="subdir/**" />
+ * </symlink>
+ * 
+ * + *

Recreate the links recorded in the previous example: + *

+ * <symlink action="recreate">
+ *    <fileset dir="${dir.top}" includes="subdir/**/dir.links" />
+ * </symlink>
+ * 
+ * + *

Delete a link named "foo" to a resource named "bar.foo" in subdir: + *

+ * <symlink action="delete" link="${dir.top}/foo"/>
+ * 
+ * + *

LIMITATIONS: Because Java has no direct support for + * handling symlinks this task divines them by comparing canoniacal and + * absolute paths. On non-unix systems this may cause false positives. + * Furthermore, any operating system on which the command + * ln -s link resource is not a valid command on the comandline + * will not be able to use action="single" or action="multi" action="record" + * should still work. Finally, the lack of support for symlinks in Java + * means that all links are recorded as links to the + * canonical resource name. Therefore the link: + * link --> subdir/dir/../foo.bar will be recorded as + * link=subdir/foo.bar and restored as + * link --> subdir/foo.bar + * + * @version $Revision$ + * @author Patrick G. Heck + */ + +public class Symlink extends Task { + + // Atributes with setter methods + private String resource; + private String link; + private String action; + private Vector fileSets = new Vector(); + private String linkFileName; + private boolean overwrite; + private boolean failonerror; + + /** Initialize the task. */ + + public void init() throws BuildException + { + super.init(); + failonerror = true; // default behavior is to fail on an error + overwrite = false; // devault behavior is to not overwrite + action = "single"; // default behavior is make a single link + fileSets = new Vector(); + } + + /** The standard method for executing any task. */ + + public void execute() throws BuildException { + try { + if (action.equals("single")) { + doLink(resource, link); + } else if (action.equals("delete")) { + try { + log("Removing symlink: " + link); + Symlink.deleteSymlink(link); + } catch (FileNotFoundException fnfe) { + handleError(fnfe.toString()); + } catch (IOException ioe) { + handleError(ioe.toString()); + } + } else if (action.equals("recreate")) { + Properties listOfLinks; + Enumeration keys; + + if (fileSets.size() == 0){ + handleError("File set identifying link file(s) " + + "required for action recreate"); + return; + } + + listOfLinks = loadLinks(fileSets); + + keys = listOfLinks.keys(); + + while(keys.hasMoreElements()) { + link = (String) keys.nextElement(); + resource = listOfLinks.getProperty(link); + doLink(resource, link); + } + } else if (action.equals("record")) { + Vector vectOfLinks; + Hashtable byDir = new Hashtable(); + Enumeration links, dirs; + + + if (fileSets.size() == 0) { + handleError("File set identifying links to " + + "record required"); + return; + } + + if (linkFileName == null) { + handleError("Name of file to record links in " + + "required"); + return; + } + + // fill our vector with file objects representing + // links (canonical) + vectOfLinks = findLinks(fileSets); + + // create a hashtable to group them by parent directory + links = vectOfLinks.elements(); + while (links.hasMoreElements()) { + File thisLink = (File) links.nextElement(); + String parent = thisLink.getParent(); + if (byDir.containsKey(parent)) { + ((Vector) byDir.get(parent)).addElement(thisLink); + } else { + byDir.put(parent, new Vector()); + ((Vector) byDir.get(parent)).addElement(thisLink); + } + } + + // write a Properties file in each directory + dirs = byDir.keys(); + while (dirs.hasMoreElements()) { + String dir = (String) dirs.nextElement(); + Vector linksInDir = (Vector) byDir.get(dir); + Properties linksToStore = new Properties(); + Enumeration eachlink = linksInDir.elements(); + File writeTo; + + // fill up a Properties object with link and resource + // names + while(eachlink.hasMoreElements()) { + File alink = (File) eachlink.nextElement(); + try { + linksToStore.put(alink.getName(), + alink.getCanonicalPath()); + } catch (IOException ioe) { + handleError("Couldn't get canonical "+ + "name of a parent link"); + } + } + + + // Get a place to record what we are about to write + writeTo = new File(dir + File.separator + + linkFileName); + + writePropertyFile(linksToStore, writeTo, + "Symlinks from " + writeTo.getParent()); + + } + + } + else { + handleError("Invalid action specified in symlink"); + } + } finally { + // return all variables to their default state, + // ready for the next invocation. + resource = null; + link = null; + action = "single"; + fileSets = new Vector(); + linkFileName = null; + overwrite = false; + failonerror = true; + } + } + + /* ********************************************************** * + * Begin Atribute Setter Methods * + * ********************************************************** */ + + /** + * The setter for the overwrite atribute. If set to false (default) + * the task will not overwrite existing links, and may stop the build + * if a link already exists depending on the setting of failonerror. + * + * @param owrite If true overwrite existing links. + */ + public void setOverwrite(boolean owrite) { + this.overwrite = owrite; + } + + /** + * The setter for the failonerror atribute. If set to true (default) + * the entire build fails upon error. If set to false, the error is + * logged and the build will continue. + * + * @param foe If true throw build exception on error else log it. + */ + public void setFailOnError(boolean foe) { + this.failonerror = foe; + } + + + /** + * The setter for the "action" attribute. May be "single" "multi" + * or "record" + * + * @param typ The action of action to perform + */ + public void setAction(String typ) { + this.action = typ; + } + + /** + * The setter for the "link" attribute. Only used for action = single. + * + * @param lnk The name for the link + */ + public void setLink(String lnk) { + this.link = lnk; + } + + /** + * The setter for the "resource" attribute. Only used for action = single. + * + * @param src The source of the resource to be linked. + */ + public void setResource(String src) { + this.resource = src; + } + + /** + * The setter for the "linkfilename" attribute. Only used for action=record. + * + * @param lf The name of the file to write links to. + */ + public void setLinkfilename(String lf) { + this.linkFileName = lf; + } + + /** + * Adds a fileset to this task. + * + * @param set The fileset to add. + */ + public void addFileset(FileSet set) { + fileSets.addElement(set); + } + + /* ********************************************************** * + * Begin Public Utility Methods * + * ********************************************************** */ + + /** + * Deletes a symlink without deleteing the resource it points to. + * + *

This is a convenience method that simply invokes + * deleteSymlink(java.io.File) + * + * @param path A string containing the path of the symlink to delete + * + * @throws FileNotFoundException When the path results in a + * File that doesn't exist. + * @throws IOException If calls to File.rename + * or File.delete fail. + */ + + public static void deleteSymlink(String path) + throws IOException, FileNotFoundException { + + File linkfil = new File(path); + deleteSymlink(linkfil); + } + + /** + * Deletes a symlink without deleteing the resource it points to. + * + *

This is a utility method that removes a unix symlink without removing + * the resource that the symlink points to. If it is accidentally invoked + * on a real file, the real file will not be harmed, but an exception + * will be thrown when the deletion is attempted. This method works by + * getting the canonical path of the link, using the canonical path to + * rename the resource (breaking the link) and then deleting the link. + * The resource is then returned to it's original name inside a finally + * block to ensure that the resource is unharmed even in the event of + * an exception. + * + * @param linkfil A File object of the symlink to delete + * + * @throws FileNotFoundException When the path results in a + * File that doesn't exist. + * @throws IOException If calls to File.rename, + * File.delete or + * File.getCanonicalPath + * fail. + */ + + public static void deleteSymlink(File linkfil) + throws IOException, FileNotFoundException { + + if (!linkfil.exists()) { + throw new FileNotFoundException("No such symlink: " + linkfil); + } + + // find the resource of the existing link + String canstr = linkfil.getCanonicalPath(); + File canfil = new File(canstr); + + // rename the resource, thus breaking the link + String parentStr = canfil.getParent(); + File parentDir = new File(parentStr); + FileUtils fu = FileUtils.newFileUtils(); + File temp = fu.createTempFile("symlink",".tmp", parentDir); + try { + if (!canfil.renameTo(temp)) { + throw new IOException("Couldn't rename resource when " + + "attempting to delete " + linkfil); + } + + // delete the (now) broken link + if(!linkfil.delete()) { + throw new IOException("Couldn't delete symlink: " + linkfil + + " (was it a real file? is this not a " + + "UNIX system?)"); + } + } finally { + File old = new File(canstr); + + // return the resource to its original name. + if (!temp.renameTo(canfil)) { + throw new IOException("Couldn't return resource " + temp + + " its original name: " + canstr + + "\n THE RESOURCE'S NAME ON DISK HAS " + + "BEEN CHANGED BY THIS ERROR!\n"); + } + } + } + + + /* ********************************************************** * + * Begin Private Methods * + * ********************************************************** */ + + /** + * Writes a properties file. + * + * In jdk 1.2+ this method will use Properties.store + * and thus report exceptions that occur while writing the file. + * In jdk 1.1 we are forced to use Properties.save + * and therefore all exceptions are masked. This method was lifted + * directly from the Proertyfile task with only slight editing. + * sticking something like this in FileUtils might be + * a good idea to avoid duplication. + * + * @param properties The properties object to be written. + * @param propertyfile The File to write to. + * @param comment The comment to place at the head of the file. + */ + + private void writePropertyFile(Properties properties, + File propertyfile, + String comment) + throws BuildException { + + BufferedOutputStream bos = null; + try { + bos = new BufferedOutputStream(new FileOutputStream(propertyfile)); + + // Properties.store is not available in JDK 1.1 + Method m = + Properties.class.getMethod("store", + new Class[] { + OutputStream.class, + String.class}); + m.invoke(properties, new Object[] {bos, comment}); + + } catch (NoSuchMethodException nsme) { + properties.save(bos, comment); + } catch (InvocationTargetException ite) { + Throwable t = ite.getTargetException(); + throw new BuildException(t, location); + } catch (IllegalAccessException iae) { + // impossible + throw new BuildException(iae, location); + } catch (IOException ioe) { + throw new BuildException(ioe, location); + } finally { + if (bos != null) { + try { + bos.close(); + } catch (IOException ioex) {} + } + } + } + + /** + * Handles errors correctly based on the setting of failonerror. + * + * @param msg The message to log, or include in the + * BuildException + */ + + private void handleError(String msg) { + if (failonerror) { + throw new BuildException(msg); + } else { + log(msg); + } + } + + + /** + * Conducts the actual construction of a link. + * + *

The link is constructed by calling Execute.runCommand. + * + * @param resource The path of the resource we are linking to. + * @param link The name of the link we wish to make + */ + + private void doLink(String resource, String link) throws BuildException { + + if (resource == null) { + handleError("Must define the resource to symlink to!"); + return; + } + if (link == null) { + handleError("Must define the link " + + "name for symlink!"); + return; + } + + File linkfil = new File(link); + + String[] cmd = new String[4]; + cmd[0] = "ln"; + cmd[1] = "-s"; + cmd[2] = resource; + cmd[3] = link; + + try { + if (overwrite && linkfil.exists()) { + deleteSymlink(linkfil); + } + } catch (FileNotFoundException fnfe) { + handleError("Symlink dissapeared before it was deleted:" + link); + } catch (IOException ioe) { + handleError("Unable to overwrite preexisting link " + link); + } + + log(cmd[0]+" "+cmd[1]+" "+cmd[2]+" "+cmd[3]); + Execute.runCommand(this,cmd); + } + + + /** + * Simultaneously get included directories and included files. + * + * @param ds The scanner with which to get the files and directories. + * @return A vector of String objects containing the + * included file names and directory names. + */ + + private Vector scanDirsAndFiles(DirectoryScanner ds) { + String[] files, dirs; + Vector list = new Vector(); + + ds.scan(); + + files = ds.getIncludedFiles(); + dirs = ds.getIncludedDirectories(); + + for (int i=0; i This method is invoked when the action atribute is is "record". + * This means that filesets are interpreted as the directories in + * which links may be found. + * + *

The basic method follwed here is, for each file set: + *

    + *
  1. Compile a list of all matches
  2. + *
  3. Convert matches to File objects
  4. + *
  5. Remove all non-symlinks using + * FileUtils.isSymbolicLink
  6. + *
  7. Convert all parent directories to the canonical form
  8. + *
  9. Add the remaining links from each file set to a + * master list of links unless the link is already recorded + * in the list
  10. + *
+ * + * @param fileSets The filesets specified by the user. + * @return A vector of File objects containing the + * links (with canonical parent directories) + */ + + private Vector findLinks(Vector fileSets) { + Vector result = new Vector(); + + // loop through the supplied file sets + FSLoop: for (int i=0; i This method is only invoked when the action atribute is set to + * "multi". The filesets passed in are assumed to specify the names + * of the property files with the link information and the + * subdirectories in which to look for them. + * + *

The basic method follwed here is, for each file set: + *

    + *
  1. Get the canonical version of the dir atribute
  2. + *
  3. Scan for properties files
  4. + *
  5. load the contents of each properties file found.
  6. + *
+ * + * @param fileSets The FileSets for this task + * @return The links to be made. + */ + + private Properties loadLinks(Vector fileSets) { + Properties finalList = new Properties(); + Enumeration keys; + String key, value; + String[] includedFiles; + + // loop through the supplied file sets + FSLoop: for (int i=0; i. + */ + +/* + * Since the initial version of this file was deveolped on the clock on + * an NSF grant I should say the following boilerplate: + * + * This material is based upon work supported by the National Science + * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and + * conclusions or recommendations expressed in this material are those + * of the author and do not necessarily reflect the views of the + * National Science Foundation. + */ + +package org.apache.tools.ant.taskdefs.optional.unix; + +import org.apache.tools.ant.taskdefs.condition.Os; + +import org.apache.tools.ant.BuildFileTest; +import org.apache.tools.ant.Project; + +/** + * Test cases for the Symlink task. Link creation, link deletion, recording + * of links in multiple directories, and restoration of links recorded are + * all tested. A separate test for the utility method Symlink.deleteSymlink + * is not included because action="delete" only prints a message and calls + * Symlink.deleteSymlink, making a separate test redundant. + * + * @version $Revision$ + * @author Patrick G. Heck + */ + +public class SymlinkTest extends BuildFileTest { + + private Project p; + private boolean supportsSymlinks = Os.isFamily("unix"); + private boolean testfail = false; + + public SymlinkTest(String name) { + super(name); + } + + public void setUp() { + if (supportsSymlinks) { + configureProject("src/etc/testcases/taskdefs/optional/symlink.xml"); + executeTarget("setup"); + } + } + + + public void testSingle() { + testfail = true; + if (supportsSymlinks) { + executeTarget("test-single"); + p = getProject(); + assertNotNull("Failed to create file", + p.getProperty("test.single.file.created")); + assertNotNull("Failed to create link", + p.getProperty("test.single.link.created")); + } + testfail = false; + } + + public void testDelete() { + testfail = true; + if (supportsSymlinks) { + executeTarget("test-delete"); + p = getProject(); + String linkDeleted = p.getProperty("test.delete.link.still.there"); + assertNotNull("Actual file deleted by symlink", + p.getProperty("test.delete.file.still.there")); + if (linkDeleted != null) { + fail(linkDeleted); + } + } + testfail = false; + } + + public void testRecord() { + testfail = true; + if (supportsSymlinks) { + executeTarget("test-record"); + p = getProject(); + + assertNotNull("Failed to create dir1", + p.getProperty("test.record.dir1.created")); + + assertNotNull("Failed to create dir2", + p.getProperty("test.record.dir2.created")); + + assertNotNull("Failed to create file1", + p.getProperty("test.record.file1.created")); + + assertNotNull("Failed to create file2", + p.getProperty("test.record.file2.created")); + + assertNotNull("Failed to create fileA", + p.getProperty("test.record.fileA.created")); + + assertNotNull("Failed to create fileB", + p.getProperty("test.record.fileB.created")); + + assertNotNull("Failed to create fileC", + p.getProperty("test.record.fileC.created")); + + assertNotNull("Failed to create link1", + p.getProperty("test.record.link1.created")); + + assertNotNull("Failed to create link2", + p.getProperty("test.record.link2.created")); + + assertNotNull("Failed to create link3", + p.getProperty("test.record.link3.created")); + + assertNotNull("Failed to create dirlink", + p.getProperty("test.record.dirlink.created")); + + assertNotNull("Couldn't record links in dir1", + p.getProperty("test.record.dir1.recorded")); + + assertNotNull("Couldn't record links in dir2", + p.getProperty("test.record.dir2.recorded")); + + String dir3rec = p.getProperty("test.record.dir3.recorded"); + + if (dir3rec != null) { + fail(dir3rec); + } + + } + testfail = false; + } + + public void testRecreate() { + testfail = true; + if (supportsSymlinks) { + executeTarget("test-recreate"); + p = getProject(); + String link1Rem = p.getProperty("test.recreate.link1.not.removed"); + String link2Rem = p.getProperty("test.recreate.link2.not.removed"); + String link3Rem = p.getProperty("test.recreate.link3.not.removed"); + String dirlinkRem = p.getProperty("test.recreate.dirlink.not.removed"); + if (link1Rem != null) { + fail(link1Rem); + } + if (link2Rem != null) { + fail(link2Rem); + } + if (link3Rem != null) { + fail(link3Rem); + } + if (dirlinkRem != null) { + fail(dirlinkRem); + } + assertNotNull("Failed to recreate link1", + p.getProperty("test.recreate.link1.recreated")); + assertNotNull("Failed to recreate link2", + p.getProperty("test.recreate.link2.recreated")); + assertNotNull("Failed to recreate link3", + p.getProperty("test.recreate.link3.recreated")); + assertNotNull("Failed to recreate dirlink", + p.getProperty("test.recreate.dirlink.recreated")); + } + testfail = false; + } + + public void tearDown() { + if (supportsSymlinks && !testfail) { + executeTarget("teardown"); + } + } + +}