package hudson.plugins.phing;
import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.Descriptor;
import hudson.tasks.Builder;
import hudson.util.ArgumentListBuilder;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.StringReader;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import org.kohsuke.stapler.DataBoundConstructor;
/**
* Phing Builder Plugin.
*
* @author Seiji Sogabe
*/
public final class PhingBuilder extends Builder {
@Extension
public static final PhingDescriptor DESCRIPTOR = new PhingDescriptor();
/**
* Set to true for debugging.
*/
private static final boolean DEBUG = false;
/**
* Optional build script.
*/
private final String buildFile;
/**
* Identifies {@link PhingInstallation} to be used.
*/
private final String name;
/**
* List of Phing targets to be invoked.
* If not specified, use "build.xml".
*/
private final String targets;
/**
* Optional properties to be passed to Phing.
* Follow {@link Properties} syntax.
*/
private final String properties;
public String getBuildFile() {
return buildFile;
}
public String getName() {
return name;
}
public String getTargets() {
return targets;
}
public String getProperties() {
return properties;
}
@DataBoundConstructor
public PhingBuilder(final String name, final String buildFile, final String targets, final String properties) {
this.name = Util.fixEmptyAndTrim(name);
this.buildFile = Util.fixEmptyAndTrim(buildFile);
this.targets = Util.fixEmptyAndTrim(targets);
this.properties = Util.fixEmptyAndTrim(properties);
}
public PhingInstallation getPhing() {
for (final PhingInstallation inst : DESCRIPTOR.getInstallations()) {
if (name != null && name.equals(inst.getName())) {
return inst;
}
}
return null;
}
@Override
public Descriptor<Builder> getDescriptor() {
return DESCRIPTOR;
}
@Override
public boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher, final BuildListener listener)
throws InterruptedException, IOException {
ArgumentListBuilder args = new ArgumentListBuilder();
final EnvVars env = build.getEnvironment(listener);
final PhingInstallation pi = getPhing();
// PHP Command
if (pi != null) {
final String phpCommand = pi.getPhpCommand();
if (phpCommand != null) {
env.put("PHP_COMMAND", phpCommand);
}
}
// Phing Command
if (pi == null) {
args.add(PhingInstallation.getExecName(launcher));
} else {
args.add(pi.getExecutable(launcher));
}
// Build script
FilePath buildFilePath;
if (buildFile == null) {
buildFilePath = build.getModuleRoot().child("build.xml");
} else {
final boolean absolute = new File(buildFile).isAbsolute();
buildFilePath = (absolute) ? new FilePath(new File(buildFile)) : build.getModuleRoot().child(buildFile);
args.add("-buildfile", buildFilePath.getName());
}
// Targets
if (targets != null) {
final String normalizedTargets = targets.replaceAll("[\t\r\n]+", " ");
args.addTokenized(normalizedTargets);
}
// Properties
if (properties != null) {
final Properties props = loadProperties();
for (final Entry<Object, Object> entry : props.entrySet()) {
args.add("-D" + entry.getKey() + "=" + entry.getValue());
}
}
args.addKeyValuePairs("-D", build.getBuildVariables());
// avoid printing esc sequence
args.add("-logger", "phing.listener.NoBannerLogger");
// Environment variables
if (pi != null && pi.getPhingHome() != null) {
env.put("PHING_HOME", pi.getPhingHome());
env.put("PHING_CLASSPATH", pi.getPhingHome() + File.separator + "classes");
}
if (!launcher.isUnix()) {
args.add("&&", "exit", "%%ERRORLEVEL%%");
args = new ArgumentListBuilder().add("cmd.exe", "/C").addQuoted(args.toStringWithQuote());
}
if (DEBUG) {
final PrintStream logger = listener.getLogger();
for (final Map.Entry<String, String> entry : env.entrySet()) {
logger.println("(DEBUG) env: key= " + entry.getKey() + " value= " + entry.getValue());
}
}
final long startTime = System.currentTimeMillis();
try {
final int result = launcher.launch().cmds(args).envs(env).stdout(listener).pwd(buildFilePath.getParent()).join();
return result == 0;
} catch (final IOException e) {
Util.displayIOException(e, listener);
final long processingTime = System.currentTimeMillis() - startTime;
final String errorMessage = buildErrorMessage(pi, processingTime);
e.printStackTrace(listener.fatalError(errorMessage));
return false;
}
}
private String buildErrorMessage(final PhingInstallation pi, final long processingTime) {
final StringBuffer msg = new StringBuffer();
msg.append(Messages.Phing_ExecFailed());
if (pi == null && processingTime < 1000) {
if (DESCRIPTOR.getInstallations() == null) {
msg.append(Messages.Phing_GlocalConfigNeeded());
} else {
msg.append(Messages.Phing_ProjectConfigNeeded());
}
}
return msg.toString();
}
private Properties loadProperties() throws IOException {
final Properties props = new Properties();
try {
// JavaSE 6.0
props.load(new StringReader(properties));
} catch (final NoSuchMethodError e) {
// J2SE 5.0
props.load(new ByteArrayInputStream(properties.getBytes()));
}
return props;
}
}