+ <
| target |
No |
NAnt target(s) to run. Default is "", or the default target
@@ -5357,6 +5369,203 @@
+
+
+ top
+
+
+
+
+ Specifies a Phing build to be run at a given time or build number.
+ For instance, we can run a clean build every 5 builds, or run more
+ comprehensive (and time intensive) system tests every 10 builds. We
+ can also schedule an official build to run every night at midnight,
+ if we so desire.
+
+ When a build runs, Phing is invoked in a separate Java process. Phing and the PHP requisites need
+ to be installed for this builder to function. See Phing's documentation for details on installation
+ and configuration.
+
+ The standard CruiseControl properties
+ passed to builders are available from within the Phing build.
+
+ See below for examples of the
+ <phing> element.
+
+
+
+
+
+
+ | Attribute |
+ Required |
+ Description |
+
+
+
+
+ | buildfile |
+ No (defaults to build.xml) |
+ Path to Phing build file. |
+
+
+ | target |
+ No |
+ Phing target(s) to run. Default is "", or the default target
+ for the build file. |
+
+
+ | multiple |
+ No |
+ Build index used to run different builders. For example,
+ if this is set to 3, the builder will be run every 3 builds.
+ Default value is 1. Can't be set if time is set. |
+
+
+ | time |
+ No |
+ Time in the form HHmm. Can't be set if multiple is set. |
+
+
+ | day |
+ No |
+ Valid values are (case-insensitive) the English names for
+ the days of the week (Sunday, Monday, Tuesday, etc). Does not
+ support multiple days except for the default of every day. |
+
+
+ | tempfile |
+ No |
+ Name of temp file. Defaults to log.xml |
+
+
+ | phingscript |
+ No. Cannot be specified if phinghome attribute is also specified |
+ Absolute filename of script (shell script or bat file) used
+ to start Phing. If this is not specified, the PhingBuilder assumes that phing (or phing.bat)
+ is installed on your path. |
+
+
+ | phinghome |
+ No. Cannot be specified if phingscript attribute is also specified. |
+ Directory in which Phing is installed. CruiseControl will attempt to use the standard
+ Phing execution scripts (i.e. phing.bat or phing). See below for examples. |
+
+
+ | phingWorkingDir |
+ No |
+ Will invoke phing in the specified directory. |
+
+
+ | saveLogDir |
+ No |
+ If supplied a copy of the phing log will be saved in the specified directory. Example:
+saveLogDir="/usr/local/dev/projects/cc/logs" |
+
+
+ | timeout |
+ No |
+ Phing build will be halted if it continues longer than the
+ specified timeout. Value in seconds. |
+
+
+ | uselogger |
+ No |
+ 'true' if CruiseControl should call Phing using -logger; 'false' to call Phing using '-listener',
+ thus using the loggerclass as a Listener. uselogger="true" will make Phing log its messages using the
+ class specified by loggerclassname as a Phing Logger, which can make for smaller log files since it
+ doesn't log DEBUG messages (see useDebug and useQuiet attributes below, and the Phing manual). Set to false
+ to have Phing echo messages to console using its DefaultLogger, which is useful when debugging your Phing
+ build. Defaults to 'false' to make initial setup easier but setting it to 'true' is recommended for
+ production situations. |
+
+
+ | loggerclassname |
+ No (defaults to phing.listener.XmlLogger) |
+ If you want to use another logger (or listener, when uselogger="false") than Phing's XmlLogger,
+ you can specify the classname of the logger here. The logger needs to output compatible XML, and
+ the class needs to be available on the classpath at buildtime. |
+
+
+ | usedebug |
+ No |
+ If true will invoke phing with -debug, which can be
+ useful for debugging your phing build. Defaults to 'false',
+ cannot be set to 'true' if usequiet is also set to 'true'.
+ When used in combination with uselogger="true", this will
+ result in bigger XML log files; otherwise, it will cause
+ more output to be written to the console by Phing's
+ DefaultLogger. |
+
+
+ | usequiet |
+ No |
+ If true will invoke phing with -quiet, which can be
+ useful for creating smaller log files since messages with
+ a priority of INFO will not be logged. Defaults to 'false',
+ cannot be set to 'true' if usedebug is also set to 'true'.
+ Smaller logfiles are only achieved when used in combination
+ with uselogger="true", otherwise there will just be less
+ output echoed to the console by Phing's DefaultLogger. |
+
+
+
+
+
+
+
+
+
+ | Element |
+ Cardinality |
+ Description |
+
+
+
+
+ | <property> |
+ 0 .. * |
+ Used to define properties for the phing build. The element
+ has two required attributes: "name" and "value". These will
+ be passed on the phing command-line as "-Dname=value"
+ Example: <property name="foo" value="bar"/> |
+
+
+
+
+
+
+
+ - Invoke the phing.bat script distributed with phing using the
phingscript attribute,
+ specifying the working directory as D:\workspace\MyProject and the
+ phing build file as MyProject-nightlybuild.xml using the default target.
+
+ <schedule>
+ <phing phingscript="C:\PHP\applications\phing-2.3.0\bin\phing.bat"
+ phingworkingdir="D:\workspace\MyProject"
+ buildfile="MyProject-nightlybuild.xml"
+ uselogger="true"
+ usedebug="false"/>
+<schedule>
+
+ Or equivalently, using the phinghome attribute
+
+ <schedule>
+ <phing phinghome="C:\PHP\applications\phing-2.3.0\bin\"
+ phingworkingdir="D:\workspace\MyProject"
+ buildfile="MyProject-nightlybuild.xml"
+ uselogger="true"
+ usedebug="false"/>
+<schedule>
+
+
+
top
Index: cruisecontrol/main/src/net/sourceforge/cruisecontrol/builders/PhingBuilder.java
===================================================================
--- cruisecontrol/main/src/net/sourceforge/cruisecontrol/builders/PhingBuilder.java (revision 0)
+++ cruisecontrol/main/src/net/sourceforge/cruisecontrol/builders/PhingBuilder.java (revision 0)
@@ -0,0 +1,343 @@
+package net.sourceforge.cruisecontrol.builders;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import net.sourceforge.cruisecontrol.Builder;
+import net.sourceforge.cruisecontrol.CruiseControlException;
+import net.sourceforge.cruisecontrol.util.EmptyElementFilter;
+import net.sourceforge.cruisecontrol.util.Util;
+import net.sourceforge.cruisecontrol.util.ValidationHelper;
+
+import org.apache.log4j.Logger;
+import org.jdom.Element;
+import org.jdom.input.SAXBuilder;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+public class PhingBuilder extends Builder {
+
+ protected static final String DEFAULT_LOGGER = "phing.listener.XmlLogger";
+ private static final Logger LOG = Logger.getLogger(AntBuilder.class);
+
+ private String phingWorkingDir = null;
+ private String buildFile = "build.xml";
+ private String target = "";
+ private String tempFileName = "log.xml";
+ private String phingScript = "phing";
+ private String phingHome;
+ private boolean useLogger;
+ private List properties = new ArrayList();
+ private boolean useDebug = false;
+ private boolean useQuiet = false;
+ private String loggerClassName = DEFAULT_LOGGER;
+ private File saveLogDir = null;
+ private long timeout = ScriptRunner.NO_TIMEOUT;
+ private boolean wasValidated = false;
+
+ public void validate() throws CruiseControlException {
+ super.validate();
+
+ ValidationHelper.assertIsSet(buildFile, "buildfile", this.getClass());
+ ValidationHelper.assertIsSet(target, "target", this.getClass());
+ ValidationHelper.assertFalse(useDebug && useQuiet,
+ "'useDebug' and 'useQuiet' can't be used together");
+
+ if (!useLogger && (useDebug || useQuiet)) {
+ LOG.warn("usedebug and usequiet are ignored if uselogger is not set to 'true'!");
+ }
+
+ if (saveLogDir != null) {
+ ValidationHelper.assertTrue(saveLogDir.isDirectory(), "'saveLogDir' must exist and be a directory");
+ }
+
+ if (phingHome != null) {
+ final File phingHomeFile = new File(phingHome);
+ ValidationHelper.assertTrue(phingHomeFile.exists() && phingHomeFile.isDirectory(),
+ "'phingHome' must exist and be a directory. Expected to find "
+ + phingHomeFile.getAbsolutePath());
+
+ final File phingScriptInPhingHome = new File(findPhingScript(Util.isWindows()));
+ ValidationHelper.assertTrue(phingScriptInPhingHome.exists() && phingScriptInPhingHome.isFile(),
+ "'phingHome' must contain an ant execution script. Expected to find "
+ + phingScriptInPhingHome.getAbsolutePath());
+
+ phingScript = phingScriptInPhingHome.getAbsolutePath();
+ }
+
+ wasValidated = true;
+ }
+
+ /**
+ * build and return the results via xml. debug status can be determined
+ * from log4j category once we get all the logging in place.
+ */
+ public Element build(Map buildProperties) throws CruiseControlException {
+ if (!wasValidated) {
+ throw new IllegalStateException("This builder was never validated."
+ + " The build method should not be getting called.");
+ }
+
+ validateBuildFileExists();
+
+ PhingScript script = new PhingScript();
+ script.setBuildProperties(buildProperties);
+ script.setProperties(properties);
+ script.setUseLogger(useLogger);
+ script.setWindows(Util.isWindows());
+ script.setPhingScript(phingScript);
+ script.setBuildFile(buildFile);
+ script.setTarget(target);
+ script.setLoggerClassName(loggerClassName);
+ script.setTempFileName(tempFileName);
+ script.setUseDebug(useDebug);
+ script.setUseQuiet(useQuiet);
+
+ File workingDir = phingWorkingDir != null ? new File(phingWorkingDir) : null;
+
+ boolean scriptCompleted = new ScriptRunner().runScript(workingDir, script, timeout);
+
+ File logFile = new File(phingWorkingDir, tempFileName);
+ Element buildLogElement;
+ if (!scriptCompleted) {
+ LOG.warn("Build timeout timer of " + timeout + " seconds has expired");
+ buildLogElement = new Element("build");
+ buildLogElement.setAttribute("error", "build timeout");
+ // although log file is most certainly empty, let's try to preserve it
+ // somebody should really fix ant's XmlLogger
+ if (logFile.exists()) {
+ try {
+ buildLogElement.setText(Util.readFileToString(logFile));
+ } catch (IOException likely) {
+ }
+ }
+ } else {
+ //read in log file as element, return it
+ buildLogElement = getPhingLogAsElement(logFile);
+ savePhingLog(logFile);
+ logFile.delete();
+ }
+ return buildLogElement;
+ }
+
+ public Element buildWithTarget(Map properties, String buildTarget) throws CruiseControlException {
+ String origTarget = target;
+ try {
+ target = buildTarget;
+ return build(properties);
+ } finally {
+ target = origTarget;
+ }
+ }
+
+ void validateBuildFileExists() throws CruiseControlException {
+ File build = new File(buildFile);
+ if (!build.isAbsolute() && phingWorkingDir != null) {
+ build = new File(phingWorkingDir, buildFile);
+ }
+ ValidationHelper.assertExists(build, "buildfile", this.getClass());
+ }
+
+
+ /**
+ * Set the location to which the ant log will be saved before Cruise
+ * Control merges the file into its log.
+ *
+ * @param dir
+ * the absolute path to the directory where the ant log will be
+ * saved or relative path to where you started CruiseControl
+ */
+ public void setSaveLogDir(String dir) {
+ saveLogDir = null;
+
+ if (dir != null && !dir.trim().equals("")) {
+ saveLogDir = new File(dir.trim());
+ }
+ }
+
+ void savePhingLog(File logFile) {
+ if (saveLogDir == null) {
+ return;
+ }
+
+ try {
+ File newPhingLogFile = new File(saveLogDir, tempFileName);
+ newPhingLogFile.createNewFile();
+
+ FileInputStream in = new FileInputStream(logFile);
+ FileOutputStream out = new FileOutputStream(newPhingLogFile);
+
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+ in.close();
+ out.close();
+ } catch (IOException ioe) {
+ LOG.error(ioe);
+ LOG.error("Unable to create file: " + new File(saveLogDir, tempFileName));
+ }
+ }
+
+ /**
+ * Set the working directory where Phing will be invoked. This parameter gets
+ * set in the XML file via the phingWorkingDir attribute. The directory can
+ * be relative (to the cruisecontrol current working directory) or absolute.
+ *
+ * @param dir
+ * the directory to make the current working directory.
+ */
+ public void setPhingWorkingDir(String dir) {
+ phingWorkingDir = dir;
+ }
+
+ /**
+ * Sets the Phing script file to be invoked.
+ *
+ * This is a platform dependent script file.
+ *
+ * @param phingScript the name of the script file
+ */
+ public void setPhingScript(String phingScript) {
+ this.phingScript = phingScript;
+ }
+
+ /**
+ * If set CC will use the platform specific script provided by Phing
+ *
+ * @param antHome the path to ANT_HOME
+ */
+ public void setPhingHome(String antHome) {
+ this.phingHome = antHome;
+ }
+
+ /**
+ * If the phinghome attribute is set, then this method returns the correct shell script
+ * to use for a specific environment.
+ */
+ protected String findPhingScript(boolean isWindows) throws CruiseControlException {
+ if (phingHome == null) {
+ throw new CruiseControlException("phinghome attribute not set.");
+ }
+
+ if (isWindows) {
+ return phingHome + "\\bin\\phing.bat";
+ } else {
+ return phingHome + "/bin/phing";
+ }
+ }
+
+ /**
+ * Set the name of the temporary file used to capture output.
+ *
+ * @param tempFileName
+ */
+ public void setTempFile(String tempFileName) {
+ this.tempFileName = tempFileName;
+ }
+
+ /**
+ * Set the Phing target(s) to invoke.
+ *
+ * @param target the target(s) name.
+ */
+ public void setTarget(String target) {
+ this.target = target;
+ }
+
+ /**
+ * Sets the name of the build file that Phing will use. The Phing default is
+ * build.xml, use this to override it.
+ *
+ * @param buildFile the name of the build file.
+ */
+ public void setBuildFile(String buildFile) {
+ this.buildFile = buildFile;
+ }
+
+ /**
+ * Sets whether Phing will use the custom loggers.
+ *
+ * @param useLogger
+ */
+ public void setUseLogger(boolean useLogger) {
+ this.useLogger = useLogger;
+ }
+
+ public Property createProperty() {
+ Property property = new Property();
+ properties.add(property);
+ return property;
+ }
+
+ protected static Element getPhingLogAsElement(File file) throws CruiseControlException {
+ if (!file.exists()) {
+ throw new CruiseControlException("phing logfile " + file.getAbsolutePath() + " does not exist.");
+ } else if (file.length() == 0) {
+ throw new CruiseControlException("phing logfile " + file.getAbsolutePath()
+ + " is empty. Your build probably failed. Check your CruiseControl logs.");
+ }
+
+ try {
+ SAXBuilder builder = new SAXBuilder("org.apache.xerces.parsers.SAXParser");
+
+ // old Ant-versions contain a bug in the XmlLogger that outputs
+ // an invalid PI containing the target "xml:stylesheet"
+ // instead of "xml-stylesheet": fix this
+ // FIXME - remove this, as it shouldn't affect Phing (though I don't know what this is
+ // for yet.)
+ XMLFilter piFilter = new XMLFilterImpl() {
+ public void processingInstruction(String target, String data) throws SAXException {
+ if (target.equals("xml:stylesheet")) { target = "xml-stylesheet"; }
+ super.processingInstruction(target, data);
+ }
+ };
+
+ // get rid of empty - and -elements created by Ant's XmlLogger
+ XMLFilter emptyTaskFilter = new EmptyElementFilter("task");
+ emptyTaskFilter.setParent(piFilter);
+ XMLFilter emptyMessageFilter = new EmptyElementFilter("message");
+ emptyMessageFilter.setParent(emptyTaskFilter);
+ builder.setXMLFilter(emptyMessageFilter);
+ return builder.build(file).getRootElement();
+ } catch (Exception ee) {
+ if (ee instanceof CruiseControlException) {
+ throw (CruiseControlException) ee;
+ }
+ File saveFile = new File(file.getParentFile(), System.currentTimeMillis() + file.getName());
+ file.renameTo(saveFile);
+ throw new CruiseControlException("Error reading : " + file.getAbsolutePath()
+ + ". Saved as : " + saveFile.getAbsolutePath(), ee);
+ }
+ }
+
+ public void setUseDebug(boolean debug) {
+ useDebug = debug;
+ }
+
+ public void setUseQuiet(boolean quiet) {
+ useQuiet = quiet;
+ }
+
+ public String getLoggerClassName() {
+ return loggerClassName;
+ }
+
+ public void setLoggerClassName(String string) {
+ loggerClassName = string;
+ }
+
+ /**
+ * @param timeout The timeout to set.
+ */
+ public void setTimeout(long timeout) {
+ this.timeout = timeout;
+ }
+}
Index: cruisecontrol/main/src/net/sourceforge/cruisecontrol/builders/PhingScript.java
===================================================================
--- cruisecontrol/main/src/net/sourceforge/cruisecontrol/builders/PhingScript.java (revision 0)
+++ cruisecontrol/main/src/net/sourceforge/cruisecontrol/builders/PhingScript.java (revision 0)
@@ -0,0 +1,167 @@
+package net.sourceforge.cruisecontrol.builders;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import net.sourceforge.cruisecontrol.CruiseControlException;
+import net.sourceforge.cruisecontrol.util.Commandline;
+
+public class PhingScript implements Script {
+ private Map buildProperties;
+
+ private boolean isWindows;
+ private String phingScript;
+ private String loggerClassName;
+ private String tempFileName = "log.xml";
+ private boolean useLogger;
+ private boolean useQuiet;
+ private boolean useDebug;
+ private String buildFile = "build.xml";
+ private List properties;
+ private String target = "";
+ private int exitCode;
+
+
+ /**
+ * construct the command that we're going to execute.
+ *
+ * @return Commandline holding command to be executed
+ * @throws CruiseControlException on unquotable attributes
+ */
+ public Commandline buildCommandline() throws CruiseControlException {
+
+ Commandline cmdLine = new Commandline();
+
+ cmdLine.setExecutable(phingScript);
+
+ if (useLogger) {
+ cmdLine.createArguments("-logger", getLoggerClassName());
+ cmdLine.createArguments("-logfile", tempFileName);
+ } else {
+ cmdLine.createArguments("-listener", getLoggerClassName());
+ cmdLine.createArgument("-DXmlLogger.file=" + tempFileName);
+ }
+
+ // -debug and -quiet only affect loggers, not listeners: when we use the loggerClassName as
+ // a listener, they will affect the default logger that writes to the console
+ if (useDebug) {
+ cmdLine.createArgument("-debug");
+ } else if (useQuiet) {
+ cmdLine.createArgument("-quiet");
+ }
+
+ for (Iterator propertiesIter = buildProperties.entrySet().iterator(); propertiesIter.hasNext(); ) {
+ Map.Entry property = (Map.Entry) propertiesIter.next();
+ String value = (String) property.getValue();
+ if (!"".equals(value)) {
+ cmdLine.createArgument("-D" + property.getKey() + "=" + value);
+ }
+ }
+
+ for (Iterator antPropertiesIterator = properties.iterator(); antPropertiesIterator.hasNext(); ) {
+ Property property = (Property) antPropertiesIterator.next();
+ cmdLine.createArgument("-D" + property.getName() + "=" + property.getValue());
+ }
+
+ cmdLine.createArguments("-buildfile", buildFile);
+
+ StringTokenizer targets = new StringTokenizer(target);
+ while (targets.hasMoreTokens()) {
+ cmdLine.createArgument(targets.nextToken());
+ }
+
+ System.out.println(cmdLine.toString());
+
+ return cmdLine;
+ }
+
+ /**
+ * @param buildProperties The buildProperties to set.
+ */
+ public void setBuildProperties(Map buildProperties) {
+ this.buildProperties = buildProperties;
+ }
+
+ /**
+ * @return Returns the loggerClassName.
+ */
+ public String getLoggerClassName() {
+ return loggerClassName;
+ }
+ /**
+ * @param loggerClassName The loggerClassName to set.
+ */
+ public void setLoggerClassName(String loggerClassName) {
+ this.loggerClassName = loggerClassName;
+ }
+ /**
+ * @param antScript The antScript to set.
+ */
+ public void setPhingScript(String antScript) {
+ this.phingScript = antScript;
+ }
+
+ /**
+ * @param isWindows The isWindows to set.
+ */
+ public void setWindows(boolean isWindows) {
+ this.isWindows = isWindows;
+ }
+ /**
+ * @param buildFile The buildFile to set.
+ */
+ public void setBuildFile(String buildFile) {
+ this.buildFile = buildFile;
+ }
+ /**
+ * @param tempFileName The tempFileName to set.
+ */
+ public void setTempFileName(String tempFileName) {
+ this.tempFileName = tempFileName;
+ }
+ /**
+ * @param useDebug The useDebug to set.
+ */
+ public void setUseDebug(boolean useDebug) {
+ this.useDebug = useDebug;
+ }
+ /**
+ * @param useLogger The useLogger to set.
+ */
+ public void setUseLogger(boolean useLogger) {
+ this.useLogger = useLogger;
+ }
+ /**
+ * @param useQuiet The useQuiet to set.
+ */
+ public void setUseQuiet(boolean useQuiet) {
+ this.useQuiet = useQuiet;
+ }
+
+ /**
+ * @param properties The properties to set.
+ */
+ public void setProperties(List properties) {
+ this.properties = properties;
+ }
+ /**
+ * @param target The target to set.
+ */
+ public void setTarget(String target) {
+ this.target = target;
+ }
+ /**
+ * @return Returns the exitCode.
+ */
+ public int getExitCode() {
+ return exitCode;
+ }
+ /**
+ * @param exitCode The exitCode to set.
+ */
+ public void setExitCode(int exitCode) {
+ this.exitCode = exitCode;
+ }
+}
Index: cruisecontrol/main/src/net/sourceforge/cruisecontrol/default-plugins.properties
===================================================================
--- cruisecontrol/main/src/net/sourceforge/cruisecontrol/default-plugins.properties (revision 2899)
+++ cruisecontrol/main/src/net/sourceforge/cruisecontrol/default-plugins.properties (working copy)
@@ -57,6 +57,7 @@
maven2=net.sourceforge.cruisecontrol.builders.Maven2Builder
nant=net.sourceforge.cruisecontrol.builders.NantBuilder
pause=net.sourceforge.cruisecontrol.PauseBuilder
+phing=net.sourceforge.cruisecontrol.PhingBuilder
rake=net.sourceforge.cruisecontrol.builders.RakeBuilder
# label incrementer -- only one!
|