blob: 9dcdcafc307532f56848118cb7d67b774496d17f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.ant.dotnet.wix;
import org.apache.ant.dotnet.DotNetExecTask;
import org.apache.ant.dotnet.build.AbstractBuildTask;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.FileSet;
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.
*
* @see http://sf.net/projects/wix
*/
public class WixTask extends Task {
/**
* The vm attribute - if given.
*/
private String vm;
/**
* The source files.
*/
private ArrayList sources = new ArrayList();
/**
* Additional source files (include files in the case of candle,
* or media/files/whatever in the case of light).
*/
private ArrayList moreSources = new ArrayList();
/**
* A single source file.
*/
private File source;
/**
* The target file.
*/
private File target;
/**
* What to do.
*/
private Mode mode;
/**
* Where is WiX installed?
*/
private File wixHome = null;
/**
* Where to place the generated .wixobj files.
*/
private File wixobjDestDir = null;
/**
* addtional command line arguments for candle.
*/
private Commandline candleCmdl = new Commandline();
/**
* list of parameters for the preprocessor.
*/
private ArrayList candleParameters = new ArrayList();
/**
* addtional command line arguments for light.
*/
private Commandline lightCmdl = new Commandline();
/**
* list of parameters for the "compiler".
*/
private ArrayList lightParameters = new ArrayList();
/**
* Whether to use lit.exe rather than light.exe.
*/
private boolean useLit = false;
public WixTask() {
super();
}
/**
* Set the name of the executable for the virtual machine.
*
* @param value the name of the executable for the virtual machine
*/
public void setVm(String value) {
this.vm = value;
}
/**
* The main source file.
*
* <p><code>candle</code> may include more files than this one,
* the main source is the one passed on the command line.</p>
*
* @param File object of the main source file.
*/
public void setSource(File f) {
source = f;
}
/**
* 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) {
sources.add(fs);
}
/**
* A set of additional source files (include files in the case of
* candle, or media/files/whatever in the case of light).
*
* <p>Unlike the files specified as sources, these will not be
* passed on the command line, they only help Ant to determine
* whether the target is out-of-date.</p>
*/
public void addMoreSources(FileSet fs) {
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) {
candleParameters.add(t);
}
/**
* A parameter to pass to light.exe.
*/
public final void addLightParameter(AbstractBuildTask.Property t) {
lightParameters.add(t);
}
/**
* Adds a command-line argument for light.exe.
*
* @return new command line argument created.
*/
public Commandline.Argument createLightArg() {
return lightCmdl.createArgument();
}
/**
* Adds a command-line argument for candle.exe.
*
* @return new command line argument created.
*/
public Commandline.Argument createCandleArg() {
return candleCmdl.createArgument();
}
/**
* Instructs the task to use lit.exe rather than light.exe as "compiler".
*
* @since .NET Antlib 1.1
*/
public void setUseLit(boolean b) {
useLit = b;
}
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();
}
if (target == null && !m.equals(Mode.CANDLE)) {
throw new BuildException("You must specify the target if you want"
+ " to run light.");
}
Collection lightSources = null;
if (!m.equals(Mode.LIGHT)) {
lightSources = doCandle();
} else {
lightSources = new HashSet();
if (source != null) {
lightSources.add(source);
}
if (sources.size() > 0) {
lightSources.addAll(grabFiles(sources));
}
}
if (!m.equals(Mode.CANDLE)) {
Collection moreLightSources = Collections.EMPTY_SET;
if (moreSources.size() > 0) {
moreLightSources = grabFiles(moreSources);
}
doLight(lightSources, moreLightSources);
}
}
/**
* Invoke candle on all sources that are newer than their targets.
*
* @return a set of File objects pointing to the generated files.
*/
private Collection doCandle() {
Set s = new HashSet();
if (source != null) {
s.add(source);
}
if (sources != null) {
s.addAll(grabFiles(sources));
}
Set ms = new HashSet();
if (moreSources != null) {
ms.addAll(grabFiles(moreSources));
}
Set toProcess = new HashSet();
Set generatedTargets = new HashSet();
Iterator iter = s.iterator();
while (iter.hasNext()) {
File thisSource = (File) iter.next();
File t = getTarget(thisSource);
generatedTargets.add(t);
if (isOutOfDate(t, thisSource, ms)) {
toProcess.add(thisSource);
}
}
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(Collection lightSources,
Collection moreLightSources) {
Set tmp = new HashSet(lightSources);
tmp.addAll(moreLightSources);
if (isOutOfDate(target, tmp)) {
runLight(lightSources);
}
}
/**
* Run candle passing all files of the collection on the command line.
*/
private void runCandle(Collection s) {
run(wixExecutable("candle.exe"), s, null, wixobjDestDir,
candleParameters, candleCmdl);
}
/**
* Run light passing all files of the collection on the command line.
*/
private void runLight(Collection s) {
run(wixExecutable(useLit ? "lit.exe" : "light.exe"), s, target, null,
lightParameters, lightCmdl);
}
/**
* returns an absolute path for the given executable if wixHome
* has been specified, the given name otherwise.
*/
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, Commandline cmdl) {
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();
exec.createArg().setValue(f.getAbsolutePath());
}
if (target != null) {
exec.createArg().setValue("/out");
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());
}
String[] extraArgs = cmdl.getArguments();
for (int i = 0; i < extraArgs.length; i++) {
exec.createArg().setValue(extraArgs[i]);
}
exec.execute();
}
/**
* Is t older than s or any of the files in list?
*/
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, Collection l) {
Iterator iter = l.iterator();
while (iter.hasNext()) {
File f = (File) iter.next();
if (t.lastModified() < f.lastModified()) {
return true;
}
}
return false;
}
/**
* Turn the fileset collection into a list of Files.
*/
private Collection grabFiles(Collection s) {
Set r = new HashSet();
Iterator iter = s.iterator();
while (iter.hasNext()) {
FileSet fs = (FileSet) iter.next();
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
String[] f = ds.getIncludedFiles();
File base = fs.getDir(getProject());
for (int i = 0; i < f.length; i++) {
r.add(new File(base, f[i]));
}
}
return r;
}
/**
* Generates the name of a candle target from the source file.
*
* <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.getName();
int dot = name.lastIndexOf(".");
if (dot > -1) {
name = name.substring(0, dot) + ".wixobj";
} else {
name = name + ".wixobj";
}
return wixobjDestDir == null
? new File(name) : new File(wixobjDestDir, name);
}
public static class Mode extends EnumeratedAttribute {
private final static String CANDLE = "candle";
private final static String LIGHT = "light";
private final static String BOTH = "both";
public Mode() {
super();
}
public String[] getValues() {
return new String[] {CANDLE, LIGHT, BOTH,};
}
}
}