make WiX task work

git-svn-id: https://svn.apache.org/repos/asf/ant/antlibs/dotnet/trunk@411883 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index cca76fb..c852f07 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1 +1,2 @@
 Brian Watson
+Stefan Bodewig
diff --git a/README b/README
index 70bc73d..606ee20 100644
--- a/README
+++ b/README
@@ -1,10 +1,5 @@
-dotnet sandbox README
-=====================
-
-Author:
--------
-
-Stefan Bodewig, but feel free to go ahead and modify to your liking.
+dotnet README
+=============
 
 Goal:
 -----
@@ -34,5 +29,3 @@
 
 * A <nunit> task.
 
-Those tasks should end up in an antlib of their own in order to be
-distributable independent of Ant.
diff --git a/docs/index.html b/docs/index.html
index 66272d8..4bddc6c 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -127,7 +127,8 @@
       <li><a href="msbuild.html">msbuild</a> - execute the MSBuild build
       tool of Microsoft's .NET framework 2.0.</li>
 
-      <li><a href="wix.html">wix</a> - execute the WiX toolset, untested.</li>
+      <li><a href="wix.html">wix</a> - execute candle and/or light of
+      the WiX toolset.</li>
 
       <li><a href="nunit.html">nunit</a> - execute the
       nunit-console.exe <a href="http://www.nunit.org/">NUnit</a>
@@ -135,6 +136,6 @@
     </ul>
 
     <hr/>
-      <p align="center">Copyright &copy; 2003-2005 The Apache Software Foundation. All rights Reserved.</p>
+      <p align="center">Copyright &copy; 2003-2006 The Apache Software Foundation. All rights Reserved.</p>
   </body>
 </html>
diff --git a/docs/wix.html b/docs/wix.html
index a79ff99..b007385 100644
--- a/docs/wix.html
+++ b/docs/wix.html
@@ -9,8 +9,9 @@
 
     <h3>Description</h3>
 
-    <p>Runs the candle, light or both from the <a
-    href="http://sourceforge.net/projects/wix">Wix</a> toolset.</p>
+    <p>Runs candle or light from the <a
+    href="http://sourceforge.net/projects/wix">Wix</a> toolset - or
+    both of them in sequence.</p>
 
     <h3>Parameters</h3>
     <table border="1" cellpadding="2" cellspacing="0">
@@ -44,6 +45,19 @@
           Specify the framework to use.</td>
         <td align="center">No.</td>
       </tr>
+      <tr>
+        <td valign="top">wixHome</td>
+        <td valign="top">Installation directory of WiX.</td>
+        <td align="center">No - Ant will assume WiX is on your PATH
+        otherwise.</td>
+      </tr>
+      <tr>
+        <td valign="top">wixobjDestDir</td>
+        <td valign="top">Directory that shall hold the .wixobj files
+          generated by candle.</td>
+        <td align="center">No - Ant will candle have its way and use
+          the current working directory if omitted.</td>
+      </tr>
     </table>
 
     <h3>Parameters specified as nested elements</h3>
@@ -61,11 +75,19 @@
     href="http://ant.apache.org/manual/CoreTypes/fileset.html">fileset</a>.</p>
 
     <p>Typically this would list include files when running candle or
-    the files that vecome part of the MSI file when running light.
-    The files in this set are only used for timestamp comparisons.  If
-    neither these files nor the given &quot;normal&quot; sources are
-    newer than the expected target, the task won't do anything.</p>
+    the files that become part of the MSI file when running light.</p>
 
+    <p>The files in this set are only used for timestamp comparisons.
+    If neither these files nor the given &quot;normal&quot; sources
+    are newer than the expected target, the task won't do
+    anything.</p>
+
+    <h4>candleParameters</h4>
+
+    <p>Specifies preprocessor parameters for candle</p>
+
+    <p><code>candleParameters</code> has two required attributes.
+    name and value that specify name and value of a parameter.</p>
 
     <h3>Examples</h3>
 
@@ -172,6 +194,6 @@
     do anything.</p>
 
     <hr/>
-      <p align="center">Copyright &copy; 2004 The Apache Software Foundation. All rights Reserved.</p>
+      <p align="center">Copyright &copy; 2004,2006 The Apache Software Foundation. All rights Reserved.</p>
   </body>
 </html>
\ No newline at end of file
diff --git a/src/main/org/apache/ant/dotnet/WixTask.java b/src/main/org/apache/ant/dotnet/WixTask.java
index b7895aa..c6cc2be 100644
--- a/src/main/org/apache/ant/dotnet/WixTask.java
+++ b/src/main/org/apache/ant/dotnet/WixTask.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2005 The Apache Software Foundation
+ * Copyright 2004-2006 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.
@@ -25,8 +25,12 @@
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Task to run the WiX utility to create MSI files from an XML description.
@@ -66,6 +70,21 @@
      */
     private Mode mode;
 
+    /**
+     * Where is WiX installed?
+     */
+    private File wixHome = null;
+
+    /**
+     * Where to place the generated .wixobj files.
+     */
+    private File wixobjDestDir = null;
+
+    /**
+     * list of parameters for the preprocessor.
+     */
+    private ArrayList parameters = new ArrayList();
+
     public WixTask() {
         super();
     }
@@ -92,6 +111,15 @@
     }
 
     /**
+     * The main target file.
+     *
+     * @param File object of the main target file.
+     */
+    public void setTarget(File f) {
+        target = f;
+    }
+
+    /**
      * A set of source files.
      */
     public void addSources(FileSet fs) {
@@ -110,11 +138,51 @@
         moreSources.add(fs);
     }
 
+    /**
+     * Sets the installation directory of WiX.
+     *
+     * <p>If omitted, Ant will assume that WiX's executables can be
+     * found on the PATH.</p>
+     */
+    public void setWixHome(File f) {
+        wixHome = f;
+    }
+
+    /**
+     * Whether to run candle, light or both.
+     */
+    public void setMode(Mode m) {
+        mode = m;
+    }
+
+    /**
+     * Sets the destination directory for wixobj files generated by candle.
+     *
+     * <p>Let's candle decide and assumes they'll be created in the
+     * current working directory.</p>
+     */
+    public void setWixobjDestDir(File f) {
+        wixobjDestDir = f;
+    }
+
+    /**
+     * A parameter to pass to candle.exe.
+     */
+    public final void addCandleParameter(AbstractBuildTask.Property t) {
+        parameters.add(t);
+    }
+
     public void execute() {
         if (source == null && sources.size() == 0) {
             throw new BuildException("You must specify at least one source"
                                      + " file.");
         }
+
+        if (source != null && !source.exists()) {
+            throw new BuildException("Source file " + source
+                                     + " doesn't exist.");
+        }
+
         String m = Mode.BOTH;
         if (mode != null) {
             m = mode.getValue();
@@ -125,10 +193,11 @@
                                      + " to run light.");
         }
 
-        List lightSources = new ArrayList();
+        Collection lightSources = null;
         if (!m.equals(Mode.LIGHT)) {
-            doCandle(lightSources);
+            lightSources = doCandle();
         } else {
+            lightSources = new HashSet();
             if (source != null) {
                 lightSources.add(source);
             }
@@ -136,11 +205,12 @@
                 lightSources.addAll(grabFiles(sources));
             }
         }
-        List moreLightSources = new ArrayList();
-        if (moreSources.size() > 0) {
-            moreLightSources = grabFiles(moreSources);
-        }
+
         if (!m.equals(Mode.CANDLE)) {
+            Collection moreLightSources = Collections.EMPTY_SET;
+            if (moreSources.size() > 0) {
+                moreLightSources = grabFiles(moreSources);
+            }
             doLight(lightSources, moreLightSources);
         }
     }
@@ -148,44 +218,45 @@
     /**
      * Invoke candle on all sources that are newer than their targets.
      *
-     * @param lightSources list that will be filled with File objects
-     * pointing to the generated object files.
+     * @return a set of File objects pointing to the generated files.
      */
-    private void doCandle(List lightSources) {
-        List s = new ArrayList();
+    private Collection doCandle() {
+        Set s = new HashSet();
         if (source != null) {
             s.add(source);
         }
         if (sources != null) {
             s.addAll(grabFiles(sources));
         }
-        List ms = new ArrayList();
+        Set ms = new HashSet();
         if (moreSources != null) {
             ms.addAll(grabFiles(moreSources));
         }
+
+        Set toProcess = new HashSet();
+        Set generatedTargets = new HashSet();
         Iterator iter = s.iterator();
-        List toProcess = new ArrayList();
         while (iter.hasNext()) {
             File thisSource = (File) iter.next();
-            File t = target;
-            if (t == null) {
-                t = getTarget(thisSource);
-            }
+            File t = getTarget(thisSource);
+            generatedTargets.add(t);
             if (isOutOfDate(t, thisSource, ms)) {
                 toProcess.add(thisSource);
-                lightSources.add(t);
             }
         }
         if (toProcess.size() != 0) {
             runCandle(toProcess);
+            return generatedTargets;
         }
+        return Collections.EMPTY_SET;
     }
 
     /**
      * Invoke light on all sources that are newer than their targets.
      */
-    private void doLight(List lightSources, List moreLightSources) {
-        List tmp = new ArrayList(lightSources);
+    private void doLight(Collection lightSources,
+                         Collection moreLightSources) {
+        Set tmp = new HashSet(lightSources);
         tmp.addAll(moreLightSources);
         if (isOutOfDate(target, tmp)) {
             runLight(lightSources);
@@ -193,26 +264,45 @@
     }
 
     /**
-     * Run candle passing all files in list on the command line.
+     * Run candle passing all files of the collection on the command line.
      */
-    private void runCandle(List s) {
-        run("candle.exe", s, null);
+    private void runCandle(Collection s) {
+        run(wixExecutable("candle.exe"), s, null, wixobjDestDir, parameters);
     }
 
     /**
-     * Run light passing all files in list on the command line.
+     * Run light passing all files of the collection on the command line.
      */
-    private void runLight(List s) {
-        run("light.exe", s, target);
+    private void runLight(Collection s) {
+        run(wixExecutable("light.exe"), s, target, null, Collections.EMPTY_LIST);
     }
 
     /**
-     * Runs the specified command passing list on the command line an
-     * potentially adding an /out parameter.
+     * returns an absolute path for the given executable if wixHome
+     * has been specified, the given name otherwise.
      */
-    private void run(String executable, List s, File target) {
+    private String wixExecutable(String name) {
+        return wixHome == null ? name
+            : new File(wixHome, name).getAbsolutePath();
+    }
+
+    /**
+     * Runs the specified command passing all files of the collection
+     * on the command line - potentially adding an /out parameter.
+     */
+    private void run(String executable, Collection s, File target,
+                     File runInDir, Collection params) {
         DotNetExecTask exec = DotNetExecTask.getTask(this, vm, 
                                                      executable, null);
+        if (runInDir != null) {
+            exec.setDir(runInDir);
+        }
+
+        exec.setFailonerror(true);
+        exec.setTaskType("wix");
+
+        exec.createArg().setValue("/nologo");
+
         Iterator iter = s.iterator();
         while (iter.hasNext()) {
             File f = (File) iter.next();
@@ -223,20 +313,27 @@
             exec.createArg().setValue(target.getAbsolutePath());
         }
         
+        iter = params.iterator();
+        while (iter.hasNext()) {
+            AbstractBuildTask.Property p =
+                (AbstractBuildTask.Property) iter.next();
+            exec.createArg().setValue("-d" + p.getName() + "=" + p.getValue());
+        }
+
         exec.execute();
     }
 
     /**
      * Is t older than s or any of the files in list?
      */
-    private boolean isOutOfDate(File t, File s, List l) {
+    private boolean isOutOfDate(File t, File s, Collection l) {
         return t.lastModified() < s.lastModified() || isOutOfDate(t, l);
     }
 
     /**
      * Is t older than any of the files in list?
      */
-    private boolean isOutOfDate(File t, List l) {
+    private boolean isOutOfDate(File t, Collection l) {
         Iterator iter = l.iterator();
         while (iter.hasNext()) {
             File f = (File) iter.next();
@@ -250,8 +347,8 @@
     /**
      * Turn the fileset collection into a list of Files.
      */
-    private List grabFiles(List s) {
-        List r = new ArrayList();
+    private Collection grabFiles(Collection s) {
+        Set r = new HashSet();
         Iterator iter = s.iterator();
         while (iter.hasNext()) {
             FileSet fs = (FileSet) iter.next();
@@ -268,16 +365,20 @@
     /**
      * Generates the name of a candle target from the source file.
      *
-     * <p>Simply chops of the extension and adds .wixobj.</p>
+     * <p>Simply chops of the extension, adds .wixobj and calculates
+     * the absolute path based on wixobjDestDir.</p>
      */
     private File getTarget(File s) {
-        String name = s.getAbsolutePath();
+        String name = s.getName();
         int dot = name.lastIndexOf(".");
         if (dot > -1) {
-            return new File(name.substring(0, dot) + ".wixobj");
+            name = name.substring(0, dot) + ".wixobj";
         } else {
-            return new File(name + ".wixobj");
+            name = name + ".wixobj";
         }
+
+        return wixobjDestDir == null
+            ? new File(name) : new File(wixobjDestDir, name);
     }
 
     public static class Mode extends EnumeratedAttribute {