diff options
Diffstat (limited to 'buildscripts/phing/classes/phing/tasks/system/ExecTask.php')
-rwxr-xr-x | buildscripts/phing/classes/phing/tasks/system/ExecTask.php | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/buildscripts/phing/classes/phing/tasks/system/ExecTask.php b/buildscripts/phing/classes/phing/tasks/system/ExecTask.php new file mode 100755 index 00000000..f5a57948 --- /dev/null +++ b/buildscripts/phing/classes/phing/tasks/system/ExecTask.php @@ -0,0 +1,516 @@ +<?php + +/** + * $Id: 3101d1403eeb4fe4fe8bae729b7d918b303de1cf $ + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS 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 COPYRIGHT + * OWNER OR 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 + * and is licensed under the LGPL. For more information please see + * <http://phing.info>. + */ + +require_once 'phing/Task.php'; + +/** + * Executes a command on the shell. + * + * @author Andreas Aderhold <andi@binarycloud.com> + * @author Hans Lellelid <hans@xmpl.org> + * @author Christian Weiske <cweiske@cweiske.de> + * @version $Id: 3101d1403eeb4fe4fe8bae729b7d918b303de1cf $ + * @package phing.tasks.system + */ +class ExecTask extends Task +{ + + /** + * Command to execute. + * @var string + */ + protected $command; + + /** + * Commandline managing object + * + * @var Commandline + */ + protected $commandline; + + /** + * Working directory. + * @var PhingFile + */ + protected $dir; + + /** + * Operating system. + * @var string + */ + protected $os; + + /** + * Whether to escape shell command using escapeshellcmd(). + * @var boolean + */ + protected $escape = false; + + /** + * Where to direct output. + * @var File + */ + protected $output; + + /** + * Whether to use PHP's passthru() function instead of exec() + * @var boolean + */ + protected $passthru = false; + + /** + * Whether to log returned output as MSG_INFO instead of MSG_VERBOSE + * @var boolean + */ + protected $logOutput = false; + + /** + * Logging level for status messages + * @var integer + */ + protected $logLevel = Project::MSG_VERBOSE; + + /** + * Where to direct error output. + * @var File + */ + protected $error; + + /** + * If spawn is set then [unix] programs will redirect stdout and add '&'. + * @var boolean + */ + protected $spawn = false; + + /** + * Property name to set with return value from exec call. + * + * @var string + */ + protected $returnProperty; + + /** + * Property name to set with output value from exec call. + * + * @var string + */ + protected $outputProperty; + + /** + * Whether to check the return code. + * @var boolean + */ + protected $checkreturn = false; + + + + public function __construct() + { + $this->commandline = new Commandline(); + } + + /** + * Main method: wraps execute() command. + * + * @return void + */ + public function main() + { + if (!$this->isApplicable()) { + return; + } + + $this->prepare(); + $this->buildCommand(); + list($return, $output) = $this->executeCommand(); + $this->cleanup($return, $output); + } + + /** + * Checks whether the command shall be executed + * + * @return boolean False if the exec command shall not be run + */ + protected function isApplicable() + { + if ($this->os === null) { + return true; + } + + $myos = Phing::getProperty('os.name'); + $this->log('Myos = ' . $myos, Project::MSG_VERBOSE); + + if (strpos($this->os, $myos) !== false) { + // this command will be executed only on the specified OS + // OS matches + return true; + } + + $this->log( + sprintf( + 'Operating system %s not found in %s', + $myos, $this->os + ), + Project::MSG_VERBOSE + ); + return false; + } + + /** + * Prepares the command building and execution, i.e. + * changes to the specified directory. + * + * @return void + */ + protected function prepare() + { + if ($this->dir === null) { + return; + } + + // expand any symbolic links first + if (!$this->dir->getCanonicalFile()->isDirectory()) { + throw new BuildException( + "'" . (string) $this->dir . "' is not a valid directory" + ); + } + $this->currdir = getcwd(); + @chdir($this->dir->getPath()); + } + + /** + * Builds the full command to execute and stores it in $command. + * + * @return void + * @uses $command + */ + protected function buildCommand() + { + if ($this->command === null && $this->commandline->getExecutable() === null) { + throw new BuildException( + 'ExecTask: Please provide "command" OR "executable"' + ); + } else if ($this->command === null) { + $this->command = Commandline::toString($this->commandline->getCommandline(), $this->escape); + } else if ($this->commandline->getExecutable() === null) { + //we need to escape the command only if it's specified directly + // commandline takes care of "executable" already + if ($this->escape == true) { + $this->command = escapeshellcmd($this->command); + } + } else { + throw new BuildException( + 'ExecTask: Either use "command" OR "executable"' + ); + } + + if ($this->error !== null) { + $this->command .= ' 2> ' . $this->error->getPath(); + $this->log( + "Writing error output to: " . $this->error->getPath(), + $this->logLevel + ); + } + + if ($this->output !== null) { + $this->command .= ' 1> ' . $this->output->getPath(); + $this->log( + "Writing standard output to: " . $this->output->getPath(), + $this->logLevel + ); + } elseif ($this->spawn) { + $this->command .= ' 1>/dev/null'; + $this->log("Sending output to /dev/null", $this->logLevel); + } + + // If neither output nor error are being written to file + // then we'll redirect error to stdout so that we can dump + // it to screen below. + + if ($this->output === null && $this->error === null) { + $this->command .= ' 2>&1'; + } + + // we ignore the spawn boolean for windows + if ($this->spawn) { + $this->command .= ' &'; + } + } + + /** + * Executes the command and returns return code and output. + * + * @return array array(return code, array with output) + */ + protected function executeCommand() + { + $this->log("Executing command: " . $this->command, $this->logLevel); + + $output = array(); + $return = null; + + if ($this->passthru) { + passthru($this->command, $return); + } else { + exec($this->command, $output, $return); + } + + return array($return, $output); + } + + /** + * Runs all tasks after command execution: + * - change working directory back + * - log output + * - verify return value + * + * @param integer $return Return code + * @param array $output Array with command output + * + * @return void + */ + protected function cleanup($return, $output) + { + if ($this->dir !== null) { + @chdir($this->currdir); + } + + $outloglevel = $this->logOutput ? Project::MSG_INFO : Project::MSG_VERBOSE; + foreach ($output as $line) { + $this->log($line, $outloglevel); + } + + if ($this->returnProperty) { + $this->project->setProperty($this->returnProperty, $return); + } + + if ($this->outputProperty) { + $this->project->setProperty( + $this->outputProperty, implode("\n", $output) + ); + } + + if ($return != 0 && $this->checkreturn) { + throw new BuildException("Task exited with code $return"); + } + } + + + /** + * The command to use. + * + * @param mixed $command String or string-compatible (e.g. w/ __toString()). + * + * @return void + */ + public function setCommand($command) + { + $this->command = "" . $command; + } + + /** + * The executable to use. + * + * @param mixed $executable String or string-compatible (e.g. w/ __toString()). + * + * @return void + */ + public function setExecutable($executable) + { + $this->commandline->setExecutable((string)$executable); + } + + /** + * Whether to use escapeshellcmd() to escape command. + * + * @param boolean $escape If the command shall be escaped or not + * + * @return void + */ + public function setEscape($escape) + { + $this->escape = (bool) $escape; + } + + /** + * Specify the working directory for executing this command. + * + * @param PhingFile $dir Working directory + * + * @return void + */ + public function setDir(PhingFile $dir) + { + $this->dir = $dir; + } + + /** + * Specify OS (or muliple OS) that must match in order to execute this command. + * + * @param string $os Operating system string (e.g. "Linux") + * + * @return void + */ + public function setOs($os) + { + $this->os = (string) $os; + } + + /** + * File to which output should be written. + * + * @param PhingFile $f Output log file + * + * @return void + */ + public function setOutput(PhingFile $f) + { + $this->output = $f; + } + + /** + * File to which error output should be written. + * + * @param PhingFile $f Error log file + * + * @return void + */ + public function setError(PhingFile $f) + { + $this->error = $f; + } + + /** + * Whether to use PHP's passthru() function instead of exec() + * + * @param boolean $passthru If passthru shall be used + * + * @return void + */ + public function setPassthru($passthru) + { + $this->passthru = (bool) $passthru; + } + + /** + * Whether to log returned output as MSG_INFO instead of MSG_VERBOSE + * + * @param boolean $logOutput If output shall be logged visibly + * + * @return void + */ + public function setLogoutput($logOutput) + { + $this->logOutput = (bool) $logOutput; + } + + /** + * Whether to suppress all output and run in the background. + * + * @param boolean $spawn If the command is to be run in the background + * + * @return void + */ + public function setSpawn($spawn) + { + $this->spawn = (bool) $spawn; + } + + /** + * Whether to check the return code. + * + * @param boolean $checkreturn If the return code shall be checked + * + * @return void + */ + public function setCheckreturn($checkreturn) + { + $this->checkreturn = (bool) $checkreturn; + } + + /** + * The name of property to set to return value from exec() call. + * + * @param string $prop Property name + * + * @return void + */ + public function setReturnProperty($prop) + { + $this->returnProperty = $prop; + } + + /** + * The name of property to set to output value from exec() call. + * + * @param string $prop Property name + * + * @return void + */ + public function setOutputProperty($prop) + { + $this->outputProperty = $prop; + } + + /** + * Set level of log messages generated (default = verbose) + * + * @param string $level Log level + * + * @return void + */ + public function setLevel($level) + { + switch ($level) { + case 'error': + $this->logLevel = Project::MSG_ERR; + break; + case 'warning': + $this->logLevel = Project::MSG_WARN; + break; + case 'info': + $this->logLevel = Project::MSG_INFO; + break; + case 'verbose': + $this->logLevel = Project::MSG_VERBOSE; + break; + case 'debug': + $this->logLevel = Project::MSG_DEBUG; + break; + default: + throw new BuildException( + sprintf('Unknown log level "%s"', $level) + ); + } + } + + /** + * Creates a nested <arg> tag. + * + * @return CommandlineArgument Argument object + */ + public function createArg() + { + return $this->commandline->createArgument(); + } +} + |