From e5f130c9977c4438df5997cc9575fc84a7da4103 Mon Sep 17 00:00:00 2001
From: siad007
Date: Sat, 24 Jun 2017 16:40:55 +0200
Subject: [PATCH 01/12] Fixed whitespace issue on argument escaping.
---
classes/phing/tasks/system/ExecTask.php | 5 ++-
classes/phing/types/Commandline.php | 37 +++++++++++++------
.../phing/tasks/system/ExecTaskTest.php | 7 ++++
test/etc/tasks/system/ExecTest.xml | 6 +++
4 files changed, 41 insertions(+), 14 deletions(-)
diff --git a/classes/phing/tasks/system/ExecTask.php b/classes/phing/tasks/system/ExecTask.php
index 89815a0381..816766da91 100644
--- a/classes/phing/tasks/system/ExecTask.php
+++ b/classes/phing/tasks/system/ExecTask.php
@@ -137,6 +137,7 @@ class ExecTask extends Task
public function __construct()
{
$this->commandline = new Commandline();
+ parent::__construct();
}
/**
@@ -226,14 +227,14 @@ protected function buildCommand()
);
} else {
if ($this->command === null) {
- $this->realCommand = Commandline::toString($this->commandline->getCommandline(), $this->escape);
+ $this->realCommand = (string) $this->commandline;
} else {
if ($this->commandline->getExecutable() === null) {
$this->realCommand = $this->command;
//we need to escape the command only if it's specified directly
// commandline takes care of "executable" already
- if ($this->escape == true) {
+ if ($this->escape === true) {
$this->realCommand = escapeshellcmd($this->realCommand);
}
} else {
diff --git a/classes/phing/types/Commandline.php b/classes/phing/types/Commandline.php
index 8c16b1bc8a..613cfd3de4 100644
--- a/classes/phing/types/Commandline.php
+++ b/classes/phing/types/Commandline.php
@@ -167,7 +167,13 @@ public function getArguments()
*/
public function __toString()
{
- return self::toString($this->getCommandline());
+ try {
+ $cmd = self::toString($this->getCommandline());
+ } catch (BuildException $be) {
+ $cmd = '';
+ }
+
+ return $cmd;
}
/**
@@ -188,18 +194,25 @@ public static function quoteArgument($argument, $escape = false)
{
if ($escape) {
return escapeshellarg($argument);
- } elseif (strpos($argument, "\"") !== false && $argument != '""') {
+ }
+
+ if (strpos($argument, "\"") !== false && $argument !== '""') {
if (strpos($argument, "'") !== false) {
throw new BuildException("Can't handle single and double quotes in same argument");
- } else {
+ }
+
+ if ($escape) {
return escapeshellarg($argument);
}
- } elseif (strpos($argument, "'") !== false || strpos($argument, " ") !== false) {
+ return $argument;
+ }
+
+ if (strpos($argument, "'") !== false || strpos($argument, " ") !== false) {
return escapeshellarg($argument);
//return '\"' . $argument . '\"';
- } else {
- return $argument;
}
+
+ return $argument;
}
/**
@@ -257,7 +270,7 @@ public static function translateCommandline($to_process)
while (($nextTok = array_shift($tokens)) !== null) {
switch ($state) {
case $inQuote:
- if ("'" == $nextTok) {
+ if ("'" === $nextTok) {
$lastTokenHasBeenQuoted = true;
$state = $normal;
} else {
@@ -265,7 +278,7 @@ public static function translateCommandline($to_process)
}
break;
case $inDoubleQuote:
- if ("\"" == $nextTok) {
+ if ("\"" === $nextTok) {
$lastTokenHasBeenQuoted = true;
$state = $normal;
} else {
@@ -273,12 +286,12 @@ public static function translateCommandline($to_process)
}
break;
default:
- if ("'" == $nextTok) {
+ if ("'" === $nextTok) {
$state = $inQuote;
- } elseif ("\"" == $nextTok) {
+ } elseif ("\"" === $nextTok) {
$state = $inDoubleQuote;
- } elseif (" " == $nextTok) {
- if ($lastTokenHasBeenQuoted || strlen($current) != 0) {
+ } elseif (" " === $nextTok) {
+ if ($lastTokenHasBeenQuoted || $current !== '') {
$args[] = $current;
$current = "";
}
diff --git a/test/classes/phing/tasks/system/ExecTaskTest.php b/test/classes/phing/tasks/system/ExecTaskTest.php
index cea215e9f0..d595141e64 100644
--- a/test/classes/phing/tasks/system/ExecTaskTest.php
+++ b/test/classes/phing/tasks/system/ExecTaskTest.php
@@ -363,4 +363,11 @@ public function testEscapedArg()
$this->executeTarget(__FUNCTION__);
$this->assertPropertyEquals('outval', 'abc$b3!SB');
}
+
+ public function testEscapedArgWithoutWhitespace()
+ {
+ $this->executeTarget(__FUNCTION__);
+ $this->assertInLogs('echo "foo|bar" 2>&1');
+ $this->assertNotInLogs('echo " foo|bar " 2>&1');
+ }
}
diff --git a/test/etc/tasks/system/ExecTest.xml b/test/etc/tasks/system/ExecTest.xml
index c312b4881d..32fdbddb7d 100644
--- a/test/etc/tasks/system/ExecTest.xml
+++ b/test/etc/tasks/system/ExecTest.xml
@@ -157,4 +157,10 @@
+
+
+
+
+
+
\ No newline at end of file
From 331567834f35620799b1ba6a66c567d1630f8679 Mon Sep 17 00:00:00 2001
From: siad007
Date: Sat, 24 Jun 2017 16:47:56 +0200
Subject: [PATCH 02/12] Fixed test execution under linux.
---
test/classes/phing/tasks/system/ExecTaskTest.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/classes/phing/tasks/system/ExecTaskTest.php b/test/classes/phing/tasks/system/ExecTaskTest.php
index d595141e64..782ab6a8ea 100644
--- a/test/classes/phing/tasks/system/ExecTaskTest.php
+++ b/test/classes/phing/tasks/system/ExecTaskTest.php
@@ -367,7 +367,7 @@ public function testEscapedArg()
public function testEscapedArgWithoutWhitespace()
{
$this->executeTarget(__FUNCTION__);
- $this->assertInLogs('echo "foo|bar" 2>&1');
- $this->assertNotInLogs('echo " foo|bar " 2>&1');
+ $this->assertInLogs($this->windows ? 'echo "foo|bar" 2>&1' : 'echo \'foo|bar\' 2>&1');
+ $this->assertNotInLogs($this->windows ? 'echo " foo|bar " 2>&1' : 'echo \' foo|bar \' 2>&1');
}
}
From c06bcc85c2de41fa73c7fa85a2a42a761c11cab0 Mon Sep 17 00:00:00 2001
From: siad007
Date: Sat, 24 Jun 2017 17:04:40 +0200
Subject: [PATCH 03/12] Fixed test execution under linux 2.
---
classes/phing/types/Commandline.php | 10 +---------
test/classes/phing/tasks/system/ExecTaskTest.php | 1 +
2 files changed, 2 insertions(+), 9 deletions(-)
diff --git a/classes/phing/types/Commandline.php b/classes/phing/types/Commandline.php
index 613cfd3de4..e021bf2a1b 100644
--- a/classes/phing/types/Commandline.php
+++ b/classes/phing/types/Commandline.php
@@ -192,24 +192,16 @@ public function __toString()
*/
public static function quoteArgument($argument, $escape = false)
{
- if ($escape) {
- return escapeshellarg($argument);
- }
-
if (strpos($argument, "\"") !== false && $argument !== '""') {
if (strpos($argument, "'") !== false) {
throw new BuildException("Can't handle single and double quotes in same argument");
}
- if ($escape) {
- return escapeshellarg($argument);
- }
return $argument;
}
- if (strpos($argument, "'") !== false || strpos($argument, " ") !== false) {
+ if (strpos($argument, "'") !== false || strpos($argument, " ") !== false || $escape) {
return escapeshellarg($argument);
- //return '\"' . $argument . '\"';
}
return $argument;
diff --git a/test/classes/phing/tasks/system/ExecTaskTest.php b/test/classes/phing/tasks/system/ExecTaskTest.php
index 782ab6a8ea..ad7f62eed0 100644
--- a/test/classes/phing/tasks/system/ExecTaskTest.php
+++ b/test/classes/phing/tasks/system/ExecTaskTest.php
@@ -366,6 +366,7 @@ public function testEscapedArg()
public function testEscapedArgWithoutWhitespace()
{
+ $arg = 'foo|bar';
$this->executeTarget(__FUNCTION__);
$this->assertInLogs($this->windows ? 'echo "foo|bar" 2>&1' : 'echo \'foo|bar\' 2>&1');
$this->assertNotInLogs($this->windows ? 'echo " foo|bar " 2>&1' : 'echo \' foo|bar \' 2>&1');
From d5242893768e0ec33d1e9ab61acf18fc76c15560 Mon Sep 17 00:00:00 2001
From: siad007
Date: Fri, 16 Feb 2018 19:17:32 +0100
Subject: [PATCH 04/12] ExecTaskTests working again
---
classes/phing/tasks/system/ApplyTask.php | 2 +-
classes/phing/tasks/system/ExecTask.php | 237 ++++++++++++------
.../tasks/system/condition/OsCondition.php | 14 +-
classes/phing/types/Commandline.php | 128 +++++-----
classes/phing/types/CommandlineArgument.php | 13 +-
.../phing/tasks/system/ExecTaskTest.php | 34 +--
test/etc/tasks/system/ExecTest.xml | 22 +-
7 files changed, 274 insertions(+), 176 deletions(-)
diff --git a/classes/phing/tasks/system/ApplyTask.php b/classes/phing/tasks/system/ApplyTask.php
index cea9f518d3..be128833ba 100644
--- a/classes/phing/tasks/system/ApplyTask.php
+++ b/classes/phing/tasks/system/ApplyTask.php
@@ -586,7 +586,7 @@ private function buildCommand()
$this->log('Command building started ', $this->loglevel);
// Building the executable
- $this->realCommand = Commandline::toString($this->commandline->getCommandline(), $this->escape);
+ $this->realCommand = (string) $this->commandline;
// Adding the source filename at the end of command, validating the existing
// sourcefile position explicit mentioning
diff --git a/classes/phing/tasks/system/ExecTask.php b/classes/phing/tasks/system/ExecTask.php
index 6ab853c6fc..c2d9e7257c 100644
--- a/classes/phing/tasks/system/ExecTask.php
+++ b/classes/phing/tasks/system/ExecTask.php
@@ -38,12 +38,6 @@ class ExecTask extends Task
*/
protected $realCommand;
- /**
- * Given command
- * @var string
- */
- protected $command;
-
/**
* Commandline managing object
*
@@ -128,21 +122,24 @@ class ExecTask extends Task
protected $checkreturn = false;
private $osFamily;
+ private $executable;
+ private $resolveExecutable = false;
+ private $searchPath = false;
/**
- *
+ * @throws \BuildException
*/
public function __construct()
{
parent::__construct();
$this->commandline = new Commandline();
- parent::__construct();
}
/**
* Main method: wraps execute() command.
*
* @return void
+ * @throws \BuildException
*/
public function main()
{
@@ -150,9 +147,15 @@ public function main()
return;
}
+ try {
+ $this->commandline->setExecutable($this->resolveExecutable($this->executable, $this->searchPath));
+ } catch (IOException | NullPointerException $e) {
+ throw new BuildException($e);
+ }
+
$this->prepare();
$this->buildCommand();
- list($return, $output) = $this->executeCommand();
+ [$return, $output] = $this->executeCommand();
$this->cleanup($return, $output);
}
@@ -166,14 +169,25 @@ public function main()
protected function prepare()
{
if ($this->dir === null) {
- return;
+ $this->dir = $this->getProject()->getBasedir();
+ }
+
+ if ($this->commandline->getExecutable() === null) {
+ throw new BuildException(
+ 'ExecTask: Please provide "executable"'
+ );
}
// expand any symbolic links first
try {
+ if (!$this->dir->getCanonicalFile()->exists()) {
+ throw new BuildException(
+ "The directory '" . (string) $this->dir . "' does not exist"
+ );
+ }
if (!$this->dir->getCanonicalFile()->isDirectory()) {
throw new BuildException(
- "'" . (string) $this->dir . "' is not a valid directory"
+ "'" . (string) $this->dir . "' is not a directory"
);
}
} catch (IOException $e) {
@@ -183,6 +197,8 @@ protected function prepare()
}
$this->currdir = getcwd();
@chdir($this->dir->getPath());
+
+ $this->commandline->setEscape($this->escape);
}
/**
@@ -194,34 +210,10 @@ protected function prepare()
*/
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->realCommand = (string) $this->commandline;
- } else {
- if ($this->commandline->getExecutable() === null) {
- $this->realCommand = $this->command;
-
- //we need to escape the command only if it's specified directly
- // commandline takes care of "executable" already
- if ($this->escape === true) {
- $this->realCommand = escapeshellcmd($this->realCommand);
- }
- } else {
- throw new BuildException(
- 'ExecTask: Either use "command" OR "executable"'
- );
- }
- }
- }
-
if ($this->error !== null) {
$this->realCommand .= ' 2> ' . escapeshellarg($this->error->getPath());
$this->log(
- "Writing error output to: " . $this->error->getPath(),
+ 'Writing error output to: ' . $this->error->getPath(),
$this->logLevel
);
}
@@ -229,12 +221,12 @@ protected function buildCommand()
if ($this->output !== null) {
$this->realCommand .= ' 1> ' . escapeshellarg($this->output->getPath());
$this->log(
- "Writing standard output to: " . $this->output->getPath(),
+ 'Writing standard output to: ' . $this->output->getPath(),
$this->logLevel
);
} elseif ($this->spawn) {
$this->realCommand .= ' 1>/dev/null';
- $this->log("Sending output to /dev/null", $this->logLevel);
+ $this->log('Sending output to /dev/null', $this->logLevel);
}
// If neither output nor error are being written to file
@@ -255,18 +247,21 @@ protected function buildCommand()
* Executes the command and returns return code and output.
*
* @return array array(return code, array with output)
+ * @throws \BuildException
*/
protected function executeCommand()
{
- $this->log("Executing command: " . $this->realCommand, $this->logLevel);
+ $cmdl = (string) $this->commandline . $this->realCommand;
+
+ $this->log('Executing command: ' . $cmdl, $this->logLevel);
$output = [];
$return = null;
if ($this->passthru) {
- passthru($this->realCommand, $return);
+ passthru($cmdl, $return);
} else {
- exec($this->realCommand, $output, $return);
+ exec($cmdl, $output, $return);
}
return [$return, $output];
@@ -284,7 +279,7 @@ protected function executeCommand()
* @throws BuildException
* @return void
*/
- protected function cleanup($return, $output)
+ protected function cleanup($return, $output): void
{
if ($this->dir !== null) {
@chdir($this->currdir);
@@ -295,9 +290,7 @@ protected function cleanup($return, $output)
$this->log($line, $outloglevel);
}
- if ($this->returnProperty) {
- $this->project->setProperty($this->returnProperty, $return);
- }
+ $this->maybeSetReturnPropertyValue($return);
if ($this->outputProperty) {
$this->project->setProperty(
@@ -308,8 +301,11 @@ protected function cleanup($return, $output)
$this->setExitValue($return);
- if ($return != 0 && $this->checkreturn) {
- throw new BuildException("Task exited with code $return");
+ if ($return !== 0) {
+ if ($this->checkreturn) {
+ throw new BuildException($this->getTaskType() . ' returned: ' . $return, $this->getLocation());
+ }
+ $this->log('Result: ' . $return, Project::MSG_ERR);
}
}
@@ -318,7 +314,7 @@ protected function cleanup($return, $output)
*
* @param int $value exit value of the process.
*/
- protected function setExitValue($value)
+ protected function setExitValue($value): void
{
$this->exitValue = $value;
}
@@ -329,7 +325,7 @@ protected function setExitValue($value)
* @return int the exit value or self::INVALID if no exit value has
* been received.
*/
- public function getExitValue()
+ public function getExitValue(): int
{
return $this->exitValue;
}
@@ -337,25 +333,33 @@ public function getExitValue()
/**
* The command to use.
*
- * @param mixed $command String or string-compatible (e.g. w/ __toString()).
+ * @param string $command String or string-compatible (e.g. w/ __toString()).
*
* @return void
+ * @throws \BuildException
*/
- public function setCommand($command)
+ public function setCommand($command): void
{
- $this->command = "" . $command;
+ $this->log("The command attribute is deprecated.\nPlease use the executable attribute and nested arg elements.",
+ Project::MSG_WARN);
+ $this->commandline = new Commandline($command);
+ $this->executable = $this->commandline->getExecutable();
}
/**
* The executable to use.
*
- * @param mixed $executable String or string-compatible (e.g. w/ __toString()).
+ * @param string|bool $value String or string-compatible (e.g. w/ __toString()).
*
* @return void
*/
- public function setExecutable($executable)
+ public function setExecutable($value): void
{
- $this->commandline->setExecutable((string) $executable);
+ if (is_bool($value)) {
+ $value = $value === true ? 'true' : 'false';
+ }
+ $this->executable = $value;
+ $this->commandline->setExecutable($value);
}
/**
@@ -365,7 +369,7 @@ public function setExecutable($executable)
*
* @return void
*/
- public function setEscape($escape)
+ public function setEscape($escape): void
{
$this->escape = (bool) $escape;
}
@@ -377,7 +381,7 @@ public function setEscape($escape)
*
* @return void
*/
- public function setDir(PhingFile $dir)
+ public function setDir(PhingFile $dir): void
{
$this->dir = $dir;
}
@@ -389,7 +393,7 @@ public function setDir(PhingFile $dir)
*
* @return void
*/
- public function setOs($os)
+ public function setOs($os): void
{
$this->os = (string) $os;
}
@@ -397,7 +401,7 @@ public function setOs($os)
/**
* List of operating systems on which the command may be executed.
*/
- public function getOs()
+ public function getOs(): string
{
return $this->os;
}
@@ -406,7 +410,7 @@ public function getOs()
* Restrict this execution to a single OS Family
* @param string $osFamily the family to restrict to.
*/
- public function setOsFamily($osFamily)
+ public function setOsFamily($osFamily): void
{
$this->osFamily = strtolower($osFamily);
}
@@ -426,7 +430,7 @@ public function getOsFamily()
*
* @return void
*/
- public function setOutput(PhingFile $f)
+ public function setOutput(PhingFile $f): void
{
$this->output = $f;
}
@@ -438,7 +442,7 @@ public function setOutput(PhingFile $f)
*
* @return void
*/
- public function setError(PhingFile $f)
+ public function setError(PhingFile $f): void
{
$this->error = $f;
}
@@ -450,7 +454,7 @@ public function setError(PhingFile $f)
*
* @return void
*/
- public function setPassthru($passthru)
+ public function setPassthru($passthru): void
{
$this->passthru = $passthru;
}
@@ -462,7 +466,7 @@ public function setPassthru($passthru)
*
* @return void
*/
- public function setLogoutput($logOutput)
+ public function setLogoutput($logOutput): void
{
$this->logOutput = $logOutput;
}
@@ -474,7 +478,7 @@ public function setLogoutput($logOutput)
*
* @return void
*/
- public function setSpawn($spawn)
+ public function setSpawn($spawn): void
{
$this->spawn = $spawn;
}
@@ -486,7 +490,7 @@ public function setSpawn($spawn)
*
* @return void
*/
- public function setCheckreturn($checkreturn)
+ public function setCheckreturn($checkreturn): void
{
$this->checkreturn = $checkreturn;
}
@@ -498,11 +502,18 @@ public function setCheckreturn($checkreturn)
*
* @return void
*/
- public function setReturnProperty($prop)
+ public function setReturnProperty($prop): void
{
$this->returnProperty = $prop;
}
+ protected function maybeSetReturnPropertyValue(int $return)
+ {
+ if ($this->returnProperty) {
+ $this->getProject()->setNewProperty($this->returnProperty, $return);
+ }
+ }
+
/**
* The name of property to set to output value from exec() call.
*
@@ -510,7 +521,7 @@ public function setReturnProperty($prop)
*
* @return void
*/
- public function setOutputProperty($prop)
+ public function setOutputProperty($prop): void
{
$this->outputProperty = $prop;
}
@@ -523,7 +534,7 @@ public function setOutputProperty($prop)
* @throws BuildException
* @return void
*/
- public function setLevel($level)
+ public function setLevel($level): void
{
switch ($level) {
case 'error':
@@ -575,9 +586,9 @@ public function createArg()
*
false otherwise.
*
*/
- protected function isValidOs()
+ protected function isValidOs(): bool
{
- //hand osfamily off to Os class, if set
+ //hand osfamily off to OsCondition class, if set
if ($this->osFamily !== null && !OsCondition::isFamily($this->osFamily)) {
return false;
}
@@ -595,4 +606,88 @@ protected function isValidOs()
}
return true;
}
+
+ /**
+ * Set whether to attempt to resolve the executable to a file.
+ *
+ * @param bool $resolveExecutable if true, attempt to resolve the
+ * path of the executable.
+ */
+ public function setResolveExecutable(boolean $resolveExecutable): void
+ {
+ $this->resolveExecutable = $resolveExecutable;
+ }
+
+ /**
+ * Set whether to search nested, then
+ * system PATH environment variables for the executable.
+ *
+ * @param bool $searchPath if true, search PATHs.
+ */
+ public function setSearchPath(boolean $searchPath): void
+ {
+ $this->searchPath = $searchPath;
+ }
+
+ /**
+ * Indicates whether to attempt to resolve the executable to a
+ * file.
+ * @return bool the resolveExecutable flag
+ *
+ */
+ public function getResolveExecutable(): bool
+ {
+ return $this->resolveExecutable;
+ }
+
+ /**
+ * The method attempts to figure out where the executable is so that we can feed
+ * the full path. We first try basedir, then the exec dir, and then
+ * fallback to the straight executable name (i.e. on the path).
+ *
+ * @param string $exec the name of the executable.
+ * @param bool $mustSearchPath if true, the executable will be looked up in
+ * the PATH environment and the absolute path is returned.
+ *
+ * @return string the executable as a full path if it can be determined.
+ * @throws \BuildException
+ * @throws IOException
+ * @throws NullPointerException
+ */
+ protected function resolveExecutable($exec, $mustSearchPath): ?string
+ {
+ if (!$this->resolveExecutable) {
+ return $exec;
+ }
+ // try to find the executable
+ $executableFile = $this->getProject()->resolveFile($exec);
+ if ($executableFile->exists()) {
+ return $executableFile->getAbsolutePath();
+ }
+ // now try to resolve against the dir if given
+ if ($this->dir !== null) {
+ $executableFile = (new FileUtils())->resolveFile($this->dir, $exec);
+ if ($executableFile->exists()) {
+ return $executableFile->getAbsolutePath();
+ }
+ }
+ // couldn't find it - must be on path
+ if ($mustSearchPath) {
+ $p = null;
+ if (getenv('path')) {
+ $p = new Path($this->getProject(), getenv('path'));
+ }
+ if ($p !== null) {
+ $dirs = $p->listPaths();
+ foreach ($dirs as $dir) {
+ $executableFile = (new FileUtils())->resolveFile(new PhingFile($dir), $exec);
+ if ($executableFile->exists()) {
+ return $executableFile->getAbsolutePath();
+ }
+ }
+ }
+ }
+
+ return $exec;
+ }
}
diff --git a/classes/phing/tasks/system/condition/OsCondition.php b/classes/phing/tasks/system/condition/OsCondition.php
index 9ca469adcc..a25d7403ba 100644
--- a/classes/phing/tasks/system/condition/OsCondition.php
+++ b/classes/phing/tasks/system/condition/OsCondition.php
@@ -64,8 +64,18 @@ public static function isOS($family)
$osName = strtolower(Phing::getProperty("os.name"));
if ($family !== null) {
- if ($family === "windows") {
- return StringHelper::startsWith("win", $osName);
+ $isWindows = StringHelper::startsWith('win', $osName);
+
+ if ($family === 'windows') {
+ return $isWindows;
+ }
+
+ if ($family === 'win32') {
+ return $isWindows && $osName === 'win32';
+ }
+
+ if ($family === 'winnt') {
+ return $isWindows && $osName === 'winnt';
}
if ($family === "mac") {
diff --git a/classes/phing/types/Commandline.php b/classes/phing/types/Commandline.php
index 041f993a75..21d8668558 100644
--- a/classes/phing/types/Commandline.php
+++ b/classes/phing/types/Commandline.php
@@ -41,9 +41,8 @@
* @author Stefan Bodewig
* @package phing.types
*/
-class Commandline
+class Commandline implements Countable
{
-
/**
* @var CommandlineArgument[]
*/
@@ -56,6 +55,7 @@ class Commandline
public $executable; // public so "inner" class can access
const DISCLAIMER = "The ' characters around the executable and arguments are not part of the command.";
+ private $escape = false;
/**
* @param null $to_process
@@ -98,16 +98,17 @@ public function createArgument($insertAtStart = false)
/**
* Sets the executable to run.
- * @param $executable
+ * @param string $executable
+ * @param bool $translateFileSeparator
*/
- public function setExecutable($executable)
+ public function setExecutable($executable, $translateFileSeparator = true): void
{
- if (!$executable) {
+ if ($executable === null || $executable === '') {
return;
}
- $this->executable = $executable;
- $this->executable = strtr($this->executable, '/', DIRECTORY_SEPARATOR);
- $this->executable = strtr($this->executable, '\\', DIRECTORY_SEPARATOR);
+ $this->executable = $translateFileSeparator
+ ? str_replace(['/', '\\'], PhingFile::$separator, $executable)
+ : $executable;
}
/**
@@ -132,28 +133,28 @@ public function addArguments(array $arguments)
* Returns the executable and all defined arguments.
* @return array
*/
- public function getCommandline()
+ public function getCommandline(): array
{
$args = $this->getArguments();
- if ($this->executable === null) {
- return $args;
+ if ($this->executable !== null && $this->executable !== '') {
+ array_unshift($args, $this->executable);
}
- return array_merge([$this->executable], $args);
+ return $args;
}
/**
* Returns all arguments defined by addLine,
* addValue or the argument object.
*/
- public function getArguments()
+ public function getArguments(): array
{
$result = [];
foreach ($this->arguments as $arg) {
$parts = $arg->getParts();
if ($parts !== null) {
foreach ($parts as $part) {
- $result[] = $arg->escape ? self::quoteArgument($part, true) : $part;
+ $result[] = $part;
}
}
}
@@ -161,13 +162,18 @@ public function getArguments()
return $result;
}
+ public function setEscape($flag)
+ {
+ $this->escape = $flag;
+ }
+
/**
* @return string
*/
public function __toString()
{
try {
- $cmd = self::toString($this->getCommandline());
+ $cmd = $this->toString($this->getCommandline());
} catch (BuildException $be) {
$cmd = '';
}
@@ -182,25 +188,32 @@ public function __toString()
* as is. If it contains double quotes, use single quotes - else
* surround the argument by double quotes.
*
- * @exception BuildException if the argument contains both, single
- * and double quotes.
* @param $argument
- * @param bool $escape
- * @throws BuildException
+ *
* @return string
+ *
+ * @throws BuildException if the argument contains both, single
+ * and double quotes.
*/
public static function quoteArgument($argument, $escape = false)
{
- if (strpos($argument, "\"") !== false && $argument !== '""') {
+ if ($escape) {
+ return escapeshellarg($argument);
+ }
+
+ if (strpos($argument, '"') !== false) {
if (strpos($argument, "'") !== false) {
throw new BuildException("Can't handle single and double quotes in same argument");
}
- return $argument;
+ return '\'' . $argument . '\'';
}
- if (strpos($argument, "'") !== false || strpos($argument, " ") !== false || $escape) {
- return escapeshellarg($argument);
+ if (strpos($argument, "'") !== false
+ || strpos($argument, ' ') !== false
+ // WIN9x uses a bat file for executing commands
+ || (OsCondition::isFamily('win32') && strpos($argument, ';') !== false)) {
+ return '"' . $argument . '"';
}
return $argument;
@@ -209,39 +222,38 @@ public static function quoteArgument($argument, $escape = false)
/**
* Quotes the parts of the given array in way that makes them
* usable as command line arguments.
+ *
* @param $lines
- * @param bool $escape
- * @throws BuildException
+ *
* @return string
+ *
+ * @throws BuildException
*/
- public static function toString($lines, $escape = false)
+ private function toString($lines = null): string
{
// empty path return empty string
- if (!$lines) {
- return "";
+ if ($lines === null || count($lines) === 0) {
+ return '';
}
- // path containing one or more elements
- $result = "";
- for ($i = 0, $len = count($lines); $i < $len; $i++) {
- if ($i > 0) {
- $result .= ' ';
- }
- $result .= self::quoteArgument($lines[$i], $escape);
- }
+ $cmd = array_shift($lines);
- return $result;
+ return $cmd . ' ' . implode(' ', array_map(function ($arg) {return self::quoteArgument($arg, $this->escape);}, $lines));
}
/**
+ * Crack a command line.
*
- * @param string $to_process
- * @throws BuildException
- * @return array
+ * @param string $toProcess the command line to process.
+ *
+ * @return string[] the command line broken into strings.
+ * An empty or null toProcess parameter results in a zero sized array.
+ *
+ * @throws \BuildException
*/
- public static function translateCommandline($to_process)
+ public static function translateCommandline(string $toProcess = null): array
{
- if (!$to_process) {
+ if ($toProcess === null || $toProcess === '') {
return [];
}
@@ -256,8 +268,7 @@ public static function translateCommandline($to_process)
$current = "";
$lastTokenHasBeenQuoted = false;
- $tok = strtok($to_process, "");
- $tokens = preg_split('/(["\' ])/', $to_process, -1, PREG_SPLIT_DELIM_CAPTURE);
+ $tokens = preg_split('/(["\' ])/', $toProcess, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
while (($nextTok = array_shift($tokens)) !== null) {
switch ($state) {
case $inQuote:
@@ -294,12 +305,12 @@ public static function translateCommandline($to_process)
}
}
- if ($lastTokenHasBeenQuoted || strlen($current) != 0) {
+ if ($lastTokenHasBeenQuoted || $current !== '') {
$args[] = $current;
}
- if ($state == $inQuote || $state == $inDoubleQuote) {
- throw new BuildException("unbalanced quotes in " . $to_process);
+ if ($state === $inQuote || $state === $inDoubleQuote) {
+ throw new BuildException('unbalanced quotes in ' . $toProcess);
}
return $args;
@@ -308,21 +319,18 @@ public static function translateCommandline($to_process)
/**
* @return int Number of components in current commandline.
*/
- public function size()
+ public function count(): int
{
return count($this->getCommandline());
}
/**
- * @return Commandline
+ * @throws \BuildException
*/
- public function __copy()
+ public function __clone()
{
- $c = new Commandline();
- $c->setExecutable($this->executable);
+ $c = new self();
$c->addArguments($this->getArguments());
-
- return $c;
}
/**
@@ -345,24 +353,26 @@ public function createMarker()
*
*
This method assumes that the first entry in the array is the
* executable to run.
- * @param array $args CommandlineArgument[] to use
+ * @param array|Commandline $args CommandlineArgument[] to use
* @return string
*/
public function describeCommand($args = null)
{
if ($args === null) {
$args = $this->getCommandline();
+ } elseif ($args instanceof self) {
+ $args = $args->getCommandline();
}
if (!$args) {
- return "";
+ return '';
}
$buf = "Executing '";
$buf .= $args[0];
$buf .= "'";
if (count($args) > 0) {
- $buf .= " with ";
+ $buf .= ' with ';
$buf .= $this->describeArguments($args, 1);
} else {
$buf .= self::DISCLAIMER;
@@ -379,14 +389,14 @@ public function describeCommand($args = null)
* @param int $offset ignore entries before this index
* @return string
*/
- protected function describeArguments(array $args = null, $offset = 0)
+ public function describeArguments(array $args = null, $offset = 0)
{
if ($args === null) {
$args = $this->getArguments();
}
if ($args === null || count($args) <= $offset) {
- return "";
+ return '';
}
$buf = "argument";
diff --git a/classes/phing/types/CommandlineArgument.php b/classes/phing/types/CommandlineArgument.php
index 3286cae57c..01e2e70093 100644
--- a/classes/phing/types/CommandlineArgument.php
+++ b/classes/phing/types/CommandlineArgument.php
@@ -41,13 +41,14 @@ public function setValue($value)
* Line to split into several commandline arguments.
*
* @param string $line line to split into several commandline arguments
+ * @throws \BuildException
*/
public function setLine($line)
{
if ($line === null) {
return;
}
- $this->parts = $this->outer->translateCommandline($line);
+ $this->parts = $this->outer::translateCommandline($line);
}
/**
@@ -55,11 +56,11 @@ public function setLine($line)
* PATH - ensures the right separator for the local platform
* is used.
*
- * @param string $value a single commandline argument
+ * @param Path $value a single commandline argument
*/
- public function setPath($value)
+ public function setPath(Path $value): void
{
- $this->parts = [(string)$value];
+ $this->parts = [(string) $value];
}
/**
@@ -69,7 +70,7 @@ public function setPath($value)
* @param PhingFile $value
* @internal param a $value single commandline argument.
*/
- public function setFile(PhingFile $value)
+ public function setFile(PhingFile $value): void
{
$this->parts = [$value->getAbsolutePath()];
}
@@ -78,7 +79,7 @@ public function setFile(PhingFile $value)
* Returns the parts this Argument consists of.
* @return array string[]
*/
- public function getParts()
+ public function getParts(): array
{
return $this->parts;
}
diff --git a/test/classes/phing/tasks/system/ExecTaskTest.php b/test/classes/phing/tasks/system/ExecTaskTest.php
index 1bd7f947a1..a85c023cf4 100644
--- a/test/classes/phing/tasks/system/ExecTaskTest.php
+++ b/test/classes/phing/tasks/system/ExecTaskTest.php
@@ -94,9 +94,9 @@ protected function assertAttributeIsSetTo($property, $value, $propertyName = nul
$this->assertEquals($value, $rprop->getValue($task));
}
- public function testPropertySetCommand()
+ public function testPropertySetCommandline()
{
- $this->assertAttributeIsSetTo('command', "echo 'foo'");
+ $this->assertAttributeIsSetTo('commandline', new Commandline("echo 'foo'"));
}
public function testPropertySetDir()
@@ -239,7 +239,7 @@ public function testFailOnNonExistingDir()
$this->fail('Expected BuildException was not thrown');
} catch (BuildException $e) {
$this->assertContains(
- str_replace('/', DIRECTORY_SEPARATOR, "'/this/dir/does/not/exist' is not a valid directory"),
+ str_replace('/', DIRECTORY_SEPARATOR, "'/this/dir/does/not/exist' does not exist"),
$e->getMessage()
);
}
@@ -256,8 +256,8 @@ public function testChangeToDir()
public function testCheckreturnTrue()
{
- if ($this->windows) {
- $this->markTestSkipped("Windows does not have '/bin/true'");
+ if (FileSystem::getFileSystem()->which('true') === false) {
+ $this->markTestSkipped("'true' not found.");
}
$this->executeTarget(__FUNCTION__);
$this->assertTrue(true);
@@ -265,12 +265,12 @@ public function testCheckreturnTrue()
/**
* @expectedException BuildException
- * @expectedExceptionMessage Task exited with code 1
+ * @expectedExceptionMessage exec returned: 1
*/
public function testCheckreturnFalse()
{
- if ($this->windows) {
- $this->markTestSkipped("Windows does not have '/bin/false'");
+ if (FileSystem::getFileSystem()->which('false') === false) {
+ $this->markTestSkipped("'false' not found.");
}
$this->executeTarget(__FUNCTION__);
}
@@ -290,7 +290,7 @@ public function testReturnProperty()
public function testEscape()
{
$this->executeTarget(__FUNCTION__);
- $this->assertInLogs($this->windows ? 'foo | cat' : 'foo | cat');
+ $this->assertInLogs($this->windows ? '"foo" "|" "cat"' : 'foo | cat');
}
public function testPassthru()
@@ -315,9 +315,6 @@ public function testOutput()
public function testError()
{
- if ($this->windows) {
- $this->markTestSkipped("The script is unlikely to run on Windows");
- }
$file = tempnam(sys_get_temp_dir(), 'phing-exectest-');
$this->project->setProperty('execTmpFile', $file);
$this->executeTarget(__FUNCTION__);
@@ -346,16 +343,7 @@ public function testNestedArg()
/**
* @expectedException BuildException
- * @expectedExceptionMessage ExecTask: Either use "command" OR "executable"
- */
- public function testExecutableAndCommand()
- {
- $this->executeTarget(__FUNCTION__);
- }
-
- /**
- * @expectedException BuildException
- * @expectedExceptionMessage ExecTask: Please provide "command" OR "executable"
+ * @expectedExceptionMessage ExecTask: Please provide "executable"
*/
public function testMissingExecutableAndCommand()
{
@@ -368,7 +356,7 @@ public function testMissingExecutableAndCommand()
public function testEscapedArg()
{
$this->executeTarget(__FUNCTION__);
- $this->assertPropertyEquals('outval', 'abc$b3!SB');
+ $this->assertPropertyEquals('outval', $this->windows ? '"abc$b3 SB"' : 'abc$b3!SB');
}
public function testEscapedArgWithoutWhitespace()
diff --git a/test/etc/tasks/system/ExecTest.xml b/test/etc/tasks/system/ExecTest.xml
index 279550c029..f5dac5c0d2 100644
--- a/test/etc/tasks/system/ExecTest.xml
+++ b/test/etc/tasks/system/ExecTest.xml
@@ -12,7 +12,7 @@
-
+
@@ -119,7 +119,7 @@
+ osfamily="${current}" />
@@ -134,11 +134,11 @@
-
+
-
+
@@ -147,7 +147,7 @@
-
+ The return property's value is: "${execReturnProp}"
@@ -166,7 +166,7 @@
-
+
@@ -181,12 +181,6 @@
-
-
-
-
-
-
@@ -199,8 +193,8 @@
-
-
+
+
From 0d4b6b82b5c2029d525361fae46a15fa5617b0ed Mon Sep 17 00:00:00 2001
From: siad007
Date: Sat, 17 Feb 2018 16:28:14 +0100
Subject: [PATCH 05/12] Reduced complexity by extending from ExecTask
---
classes/phing/tasks/system/ApplyTask.php | 738 ++++++++----------
classes/phing/tasks/system/AttribTask.php | 2 +-
classes/phing/tasks/system/ExecTask.php | 44 +-
classes/phing/types/CommandlineMarker.php | 40 +-
classes/phing/types/Environment.php | 75 ++
.../phing/types/environment/EnvVariable.php | 94 +++
.../phing/tasks/system/ApplyTaskTest.php | 38 +-
test/etc/tasks/system/ApplyTest.xml | 72 +-
8 files changed, 658 insertions(+), 445 deletions(-)
create mode 100644 classes/phing/types/Environment.php
create mode 100644 classes/phing/types/environment/EnvVariable.php
diff --git a/classes/phing/tasks/system/ApplyTask.php b/classes/phing/tasks/system/ApplyTask.php
index be128833ba..4c70cc43e6 100644
--- a/classes/phing/tasks/system/ApplyTask.php
+++ b/classes/phing/tasks/system/ApplyTask.php
@@ -27,54 +27,13 @@
*
* @todo Add support for mapper, targetfile expressions
*/
-class ApplyTask extends Task
+class ApplyTask extends ExecTask
{
use ResourceAware;
- /**
- * Configuration(s)
- *
- */
- //[TBA]const TARGETFILE_ID = '__TARGETFILE__';
const SOURCEFILE_ID = '__SOURCEFILE__';
-
- /**
- * Commandline managing object
- * @var Commandline
- */
- protected $commandline;
-
- /**
- * Working directory
- * @var phingfile
- */
- protected $dir;
protected $currentdirectory;
- /**
- * Command to be executed
- * @var string
- */
- protected $realCommand;
-
- /**
- * Escape (shell) command using 'escapeshellcmd' before execution
- * @var boolean
- */
- protected $escape = false;
-
- /**
- * Where to direct output
- * @var phingfile
- */
- protected $output;
-
- /**
- * Where to direct error
- * @var phingfile
- */
- protected $error;
-
/**
* Whether output should be appended to or overwrite an existing file
* @var boolean
@@ -94,35 +53,12 @@ class ApplyTask extends Task
*/
protected $addsourcefile = true;
- /**
- * Whether to spawn the command execution as a background process
- * @var boolean
- */
- protected $spawn = false;
-
- /**
- * Property name to set with return value
- * @var string
- */
- protected $returnProperty;
-
- /**
- * Property name to set with output value
- * @var string
- */
- protected $outputProperty;
-
/**
* Whether the filenames should be passed on the command line as relative pathnames (relative to the base directory of the corresponding fileset/list)
* @var boolean
*/
protected $relative = false;
- /**
- * Operating system information
- * @var string
- */
- protected $os;
protected $currentos;
protected $osvariant;
@@ -132,27 +68,12 @@ class ApplyTask extends Task
*/
protected $loglevel = null;
- /**
- * Fail on command that exits with a returncode other than zero
- * @var boolean
- *
- */
- protected $failonerror = false;
-
- /**
- * Whether to use PHP's passthru() function instead of exec()
- * @var boolean
- */
- protected $passthru = false;
-
-
/**
* Whether to use forward-slash as file-separator on the file names
* @var boolean
*/
protected $forwardslash = false;
-
/**
* Limit the amount of parallelism by passing at most this many sourcefiles at once
* (Set it to <= 0 for unlimited)
@@ -160,52 +81,46 @@ class ApplyTask extends Task
*/
protected $maxparallel = 0;
- /**
- * Sets the command executable information
- *
- * @param string $executable Executable path
- *
- * @return void
- */
- public function setExecutable($executable)
- {
- $this->commandline->setExecutable((string) $executable);
- }
+ protected static $types = [
+ 'FILE' => 'file',
+ 'DIR' => 'dir',
+ 'BOTH' => 'both',
+ ];
- /**
- * Specify the working directory for the command execution.
- *
- * @param PhingFile $dir Set the working directory as specified
- *
- * @return void
- */
- public function setDir(PhingFile $dir)
- {
- $this->dir = $dir;
- }
+ protected $type = 'file';
+ /** @var CommandlineMarker $targetFilePos */
+ protected $targetFilePos;
+ /** @var CommandlineMarker $srcFilePos */
+ protected $srcFilePos;
+ protected $srcIsFirst = true;
+
+ protected $skipEmpty = false;
+ private $force = false;
+ private $mapper;
+ private $destDir;
+
+ /** @var Mapper $mapperElement */
+ private $mapperElement;
/**
- * Escape command using 'escapeshellcmd' before execution
- *
- * @param boolean $escape Escape command before execution
- *
- * @return void
+ * Set whether empty filesets will be skipped. If true and
+ * no source files have been found or are newer than their
+ * corresponding target files, the command will not be run.
+ * @param boolean $skip whether to skip empty filesets.
*/
- public function setEscape($escape)
+ public function setSkipEmptyFilesets(boolean $skip)
{
- $this->escape = (bool) $escape;
+ $this->skipEmpty = $skip;
}
/**
- * File to which output should be written
- *
- * @param PhingFile $outputfile Output log file
+ * Specify the directory where target files are to be placed.
*
- * @return void
+ * @param PhingFile $dest the File object representing the destination directory.
*/
- public function setOutput(PhingFile $outputfile)
+ public function setDest(PhingFile $dest)
{
- $this->output = $outputfile;
+ $this->destDir = $dest;
}
/**
@@ -245,58 +160,6 @@ public function setAddsourcefile($addsourcefile)
$this->addsourcefile = (bool) $addsourcefile;
}
- /**
- * File to which error output should be written
- *
- * @param PhingFile $errorfile Error log file
- *
- * @return void
- */
- public function setError(PhingFile $errorfile)
- {
- $this->error = $errorfile;
- }
-
-
- /**
- * Whether to spawn the command and run as background process
- *
- * @param boolean $spawn If the command is to be run as a background process
- *
- * @return void
- */
- public function setSpawn($spawn)
- {
- $this->spawn = (bool) $spawn;
- }
-
-
- /**
- * The name of property to set to return value
- *
- * @param string $propertyname Property name
- *
- * @return void
- */
- public function setReturnProperty($propertyname)
- {
- $this->returnProperty = (string) $propertyname;
- }
-
-
- /**
- * The name of property to set to output value
- *
- * @param string $propertyname Property name
- *
- * @return void
- */
- public function setOutputProperty($propertyname)
- {
- $this->outputProperty = (string) $propertyname;
- }
-
-
/**
* Whether the filenames should be passed on the command line as relative
* pathnames (relative to the base directory of the corresponding fileset/list)
@@ -311,32 +174,6 @@ public function setRelative($relative)
$this->relative = (bool) $relative;
}
-
- /**
- * Specify OS (or multiple 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;
- }
-
-
- /**
- * 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;
- }
-
/**
* Fail on command exits with a returncode other than zero
*
@@ -346,15 +183,7 @@ public function setPassthru($passthru)
*/
public function setFailonerror($failonerror)
{
- $this->failonerror = (bool) $failonerror;
- }
-
- /**
- * @param $failonerror
- */
- public function setCheckreturn($failonerror)
- {
- $this->setFailonerror($failonerror);
+ $this->checkreturn = (bool) $failonerror;
}
/**
@@ -382,126 +211,197 @@ public function setMaxparallel($max)
$this->maxparallel = (int) $max;
}
- /** [TBA]
+ public function setForce(bool $force)
+ {
+ $this->force = $force;
+ }
+
+ /**
+ * Set whether the command works only on files, directories or both.
+ * @param string $type a FileDirBoth EnumeratedAttribute.
+ */
+ public function setType(string $type)
+ {
+ $this->type = $type;
+ }
+
+ /**
* Supports embedded element.
*
- * @return void
+ * @return CommandlineMarker
+ * @throws \BuildException
*/
- /**public function createTargetfile() {
- * return $this->commandline->addArguments( array(self::TARGETFILE_ID) );
- * }*/
+ public function createTargetfile()
+ {
+
+ if ($this->targetFilePos !== null) {
+ throw new BuildException($this->getTaskType() . " doesn\'t support multiple "
+ . "targetfile elements.", $this->getLocation());
+ }
+
+ $this->targetFilePos = $this->commandline->createMarker();
+ $this->srcIsFirst = ($this->srcFilePos !== null);
+
+ return $this->targetFilePos;
+ }
/**
* Supports embedded element.
*
- * @return void
+ * @return CommandlineMarker
+ * @throws \BuildException
*/
public function createSrcfile()
{
- return $this->commandline->addArguments([self::SOURCEFILE_ID]);
+ if ($this->srcFilePos !== null) {
+ throw new BuildException($this->getTaskType() . " doesn\'t support multiple "
+ . "srcfile elements.", $this->getLocation());
+ }
+
+ $this->srcFilePos = $this->commandline->createMarker();
+ return $this->srcFilePos;
}
/**
- * Supports embedded element.
- *
- * @return CommandlineArgument
+ * @return Mapper
+ * @throws \BuildException
*/
- public function createArg()
+ public function createMapper()
{
- return $this->commandline->createArgument();
+ if ($this->mapperElement !== null) {
+ throw new BuildException(
+ 'Cannot define more than one mapper',
+ $this->getLocation()
+ );
+ }
+ $this->mapperElement = new Mapper($this->getProject());
+ return $this->mapperElement;
}
/**********************************************************************************/
/**************************** T A S K M E T H O D S ******************************/
/**********************************************************************************/
- /**
- * Class Initialization
- * @return void
- */
- public function init()
- {
- $this->commandline = new Commandline();
- $this->loglevel = Project::MSG_VERBOSE;
- }
-
/**
* Do work
- * @throws BuildException
+ * @throws \BuildException
*/
public function main()
{
-
- // Log
- $this->log('Started ', $this->loglevel);
-
- // Initialize //
- $this->initialize();
-
- // Validate O.S. applicability
- if ($this->validateOS()) {
-
- // Build the command //
- $this->buildCommand();
-
- // Process //
- // - FileLists
- foreach ($this->filelists as $fl) {
- $this->process($fl->getFiles($this->project), $fl->getDir($this->project));
+ try {
+ // Log
+ $this->log('Started ', $this->loglevel);
+ // Initialize //
+ $this->prepare();
+ $haveExecuted = false;
+ // Validate O.S. applicability
+ if ($this->isValidOs()) {
+ // Build the command //
+ $this->buildCommand();
+ // Process //
+ $totalFiles = 0;
+ $totalDirs = 0;
+ $fileNames = [];
+ // - FileSets
+ foreach ($this->filesets as $fs) {
+ $currentType = $this->type;
+ if ($fs instanceof DirSet) {
+ if ($this->type !== self::$types['DIR']) {
+ $this->log("Found a nested dirset but type is $this->type . "
+ . "Temporarily switching to type=\"dir\" on the"
+ . " assumption that you really did mean"
+ . " not .", Project::MSG_DEBUG
+ );
+ $currentType = 'dir';
+ }
+ }
+ $base = $fs->getDir($this->project);
+ $ds = $fs->getDirectoryScanner($this->project);
+ if ($currentType !== self::$types['DIR']) {
+ $s = $this->getFiles($base, $ds);
+ foreach ($s as $fileName) {
+ $totalFiles++;
+ $fileNames[] = $fileName;
+ $baseDirs[] = $base;
+ }
+ }
+ if ($currentType !== self::$types['FILE']) {
+ $s = $this->getDirs($base, $ds);
+ foreach ($s as $fileName) {
+ $totalDirs++;
+ $fileNames[] = $fileName;
+ $baseDirs[] = $base;
+ }
+ }
+ if (count($fileNames) === 0 && $this->skipEmpty) {
+ $this->logSkippingFileset($currentType, $ds, $base);
+ continue;
+ }
+ $this->process(
+ $fs->getDirectoryScanner($this->project)->getIncludedFiles(),
+ $fs->getDir($this->project)
+ );
+ $haveExecuted = true;
+ }
+ unset($this->filesets);
+ // - FileLists
+ /** @var FileList $fl */
+ foreach ($this->filelists as $fl) {
+ $totalFiles++;
+ $this->process($fl->getFiles($this->project), $fl->getDir($this->project));
+ $haveExecuted = true;
+ }
+ unset($this->filelists);
}
- unset($this->filelists);
-
- // - FileSets
- foreach ($this->filesets as $fs) {
- $this->process(
- $fs->getDirectoryScanner($this->project)->getIncludedFiles(),
- $fs->getDir($this->project)
- );
+ if ($haveExecuted) {
+ $this->log(
+ "Applied " . $this->commandline->getExecutable() . " to "
+ . $totalFiles . " file"
+ . ($totalFiles !== 1 ? "s" : "") . " and "
+ . $totalDirs . " director"
+ . ($totalDirs !== 1 ? "ies" : "y") . ".",
+ $this->loglevel);
}
- unset($this->filesets);
+ /// Cleanup //
+ $this->cleanup();
+ // Log
+ $this->log('End ', $this->loglevel);
+ } catch (IOException | NullPointerException | UnexpectedValueException $e) {
+ throw new BuildException('Execute failed: ' . $e, $e, $this->getLocation());
}
-
- /// Cleanup //
- $this->cleanup();
-
- // Log
- $this->log('End ', $this->loglevel);
}
/**********************************************************************************/
/********************** T A S K C O R E M E T H O D S ***************************/
/**********************************************************************************/
- /**
- * Checks whether the current O.S. should be supported
- *
- * @return boolean False if the exec command shall not be run
- */
- protected function validateOS()
+ protected function getFiles(PhingFile $baseDir, DirectoryScanner $ds)
{
+ return $this->restrict($ds->getIncludedFiles(), $baseDir);
+ }
- // Log
- $this->log('Validating Operating System information ', $this->loglevel);
-
- // Checking whether'os' information is specified
- if (empty($this->os)) {
-
- // Log
- $this->log("Operating system information not specified. Skipped checking. ", $this->loglevel);
-
- return true;
- }
-
- // Validating the operating system information
- $matched = stripos($this->os, $this->currentos) !== false;
+ protected function getDirs(PhingFile $baseDir, DirectoryScanner $ds)
+ {
+ return $this->restrict($ds->getIncludedDirectories(), $baseDir);
+ }
- // Log
- $this->log(
- "Operating system '" . $this->currentos . "' " . ($matched ? '' : 'not ') . "found in " . $this->os,
- $this->loglevel
- );
+ protected function restrict($s, PhingFile $baseDir)
+ {
+ $sfs = new SourceFileScanner($this);
+ return ($this->mapper === null || $this->force)
+ ? $s
+ : $sfs->restrict($s, $baseDir, $this->destDir, $this->mapper);
+ }
- return $matched;
+ private function logSkippingFileset($currentType, DirectoryScanner $ds, PhingFile $base)
+ {
+ $includedCount = (
+ ($currentType !== self::$types['DIR']) ? $ds->getIncludedFilesCount() : 0
+ ) + (
+ ($currentType !== self::$types['FILES']) ? $ds->getIncludedDirectoriesCount() : 0);
+ $this->log("Skipping fileset for directory " . $base . ". It is "
+ . (($includedCount > 0) ? "up to date." : "empty."),
+ $this->loglevel);
}
/**
@@ -509,18 +409,21 @@ protected function validateOS()
* - Required information validation
* - Working directory
*
- * @param none
- *
* @return void
+ * @throws \BuildException
+ * @throws IOException
*/
- private function initialize()
+ protected function prepare()
{
-
// Log
$this->log('Initializing started ', $this->loglevel);
///// Validating the required parameters /////
+ if (!in_array($this->type, self::$types)) {
+ throw new BuildException('Type must be one of \'file\', \'dir\' or \'both\'.');
+ }
+
// Executable
if ($this->commandline->getExecutable() === null) {
$this->throwBuildException('Please provide "executable" information');
@@ -542,8 +445,7 @@ private function initialize()
// Log
$this->log(
- 'Working directory change ' . ($dirchangestatus ? 'successful' : 'failed') . ' to ' . $this->dir->getPath(
- ),
+ 'Working directory change ' . ($dirchangestatus ? 'successful' : 'failed') . ' to ' . $this->dir->getPath(),
$this->loglevel
);
}
@@ -557,9 +459,9 @@ private function initialize()
$this->log('Operating System identified : ' . $this->currentos, $this->loglevel);
// Getting the O.S. type identifier
- // Validating the 'filesystem' for determining the OS type [UNIX, WINDOWS]
+ // Validating the 'filesystem' for determining the OS type [UNIX, WINNT and WIN32]
// (Another usage could be with 'os.name' for determination)
- if ('WINDOWS' == Phing::getProperty('host.fstype')) {
+ if ('WIN' === strtoupper(substr(Phing::getProperty('host.fstype'), 0, 3))) {
$this->osvariant = 'WIN'; // Probable Windows flavour
} else {
$this->osvariant = 'LIN'; // Probable GNU/Linux flavour
@@ -568,18 +470,38 @@ private function initialize()
// Log
$this->log('Operating System variant identified : ' . $this->osvariant, $this->loglevel);
+ if (count($this->filesets) === 0 && count($this->filelists) === 0 && count($this->getDirSets()) === 0) {
+ throw new BuildException("no resources specified",
+ $this->getLocation());
+ }
+ if ($this->targetFilePos !== null && $this->mapperElement === null) {
+ throw new BuildException("targetfile specified without mapper",
+ $this->getLocation());
+ }
+ if ($this->destDir !== null && $this->mapperElement === null) {
+ throw new BuildException("dest specified without mapper",
+ $this->getLocation());
+ }
+
+ if ($this->mapperElement !== null) {
+ $this->mapper = $this->mapperElement->getImplementation();
+ $this->log('Mapper identified : ' . get_class($this->mapper), $this->loglevel);
+ }
+
+ $this->commandline->setEscape($this->escape);
+
// Log
$this->log('Initializing completed ', $this->loglevel);
-
- return;
}
/**
* Builds the full command to execute and stores it in $realCommand.
*
* @return void
+ *
+ * @throws \BuildException
*/
- private function buildCommand()
+ protected function buildCommand()
{
// Log
@@ -590,39 +512,29 @@ private function buildCommand()
// Adding the source filename at the end of command, validating the existing
// sourcefile position explicit mentioning
- if (($this->addsourcefile === true) && (strpos($this->realCommand, self::SOURCEFILE_ID) === false)) {
+ if ($this->addsourcefile === true) {
$this->realCommand .= ' ' . self::SOURCEFILE_ID;
}
// Setting command output redirection with content appending
if ($this->output !== null) {
- $this->realCommand .= ' 1>';
- $this->realCommand .= ($this->appendoutput ? '>' : ''); // Append output
- $this->realCommand .= ' ' . escapeshellarg($this->output->getPath());
+ $this->realCommand .= sprintf(' 1>%s %s', $this->appendoutput ? '>' : '', escapeshellarg($this->output->getPath()));
} elseif ($this->spawn) { // Validating the 'spawn' configuration, and redirecting the output to 'null'
-
- // Validating the O.S. variant
- if ('WIN' == $this->osvariant) {
- $this->realCommand .= ' > NUL'; // MS Windows output nullification
- } else {
- $this->realCommand .= ' 1>/dev/null'; // GNU/Linux output nullification
- }
+ $this->realCommand .= sprintf(' %s', 'WIN' === $this->osvariant ? '> NUL' : '1>/dev/null');
$this->log("For process spawning, setting Output nullification ", $this->loglevel);
}
// Setting command error redirection with content appending
if ($this->error !== null) {
- $this->realCommand .= ' 2>';
- $this->realCommand .= ($this->appendoutput ? '>' : ''); // Append error
- $this->realCommand .= ' ' . escapeshellarg($this->error->getPath());
+ $this->realCommand .= sprintf(' 2>%s %s', $this->appendoutput ? '>' : '', escapeshellarg($this->error->getPath()));
}
// Setting the execution as a background process
if ($this->spawn) {
// Validating the O.S. variant
- if ('WIN' == $this->osvariant) {
+ if ('WIN' === $this->osvariant) {
$this->realCommand = 'start /b ' . $this->realCommand; // MS Windows background process forking
} else {
$this->realCommand .= ' &'; // GNU/Linux background process forking
@@ -634,114 +546,138 @@ private function buildCommand()
// Log
$this->log('Command building completed ', $this->loglevel);
-
- return;
}
/**
* Processes the files list with provided information for execution
*
- * @param array $files File list for processing
+ * @param array $srcFiles File list for processing
* @param string $basedir Base directory of the file list
*
* @return void
+ * @throws \BuildException
+ * @throws IOException
+ * @throws NullPointerException
*/
- private function process($files, $basedir)
+ private function process($srcFiles, $basedir)
{
-
// Log
- $this->log("Processing Filelist with base directory ($basedir) ", $this->loglevel);
+ $this->log("Processing files with base directory ($basedir) ", $this->loglevel);
+ $targets = [];
+ if ($this->targetFilePos !== null) {
+ $addedFiles = [];
+ foreach ($srcFiles as $count => $file) {
+ if ($this->mapper !== null) {
+ $subTargets = $this->mapper->main($file);
+ if ($subTargets !== null) {
+ foreach ($subTargets as $subTarget) {
+ if ($this->relative) {
+ $name = $subTarget;
+ } else {
+ $name = (new PhingFile($this->destDir, $subTarget))->getAbsolutePath();
+ }
+ if ($this->forwardslash && PhingFile::$separator !== '/') {
+ $name = str_replace(PhingFile::$separator, '/', $name);
+ }
+ if (!isset($addedFiles[$name])) {
+ $targets[] = $name;
+ $addedFiles[] = $name;
+ }
+ }
+ }
+ }
+ }
+ }
+ $targetFiles = $targets;
- // Process each file in the list for applying the 'realcommand'
- foreach ($files as $count => $file) {
+ if (!$this->addsourcefile) {
+ $srcFiles = [];
+ }
+ $orig = $this->commandline->getCommandline();
+ $result = []; // range(0,count($orig) + count($srcFiles) + count($targetFiles));
- // Preparing the absolute filename with relative path information
- $absolutefilename = $this->getFilePath($file, $basedir, $this->relative);
+ $srcIndex = count($orig);
+ if ($this->srcFilePos !== null) {
+ $srcIndex = $this->srcFilePos->getPosition();
+ }
+ if ($this->targetFilePos !== null) {
+ $targetIndex = $this->targetFilePos->getPosition();
- // Checking whether 'parallel' information is enabled. If enabled, append all
- // the file names as arguments, and run only once.
- if ($this->parallel) {
+ if ($srcIndex < $targetIndex || ($srcIndex === $targetIndex && $this->srcIsFirst)) {
+ // 0 --> srcIndex
+ $result[] = $orig;
- // Checking whether 'maxparallel' setting describes parallelism limitation
- // by passing at most 'maxparallel' many sourcefiles at once
- $slicedfiles = array_splice(
- $files,
- 0,
- (($this->maxparallel > 0) ? $this->maxparallel : count($files))
- );
+ // srcIndex --> targetIndex
+ $result += array_slice($orig, $srcIndex + count($srcFiles), $targetIndex - $srcIndex, true);
- $absolutefilename = implode(' ', $this->getFilePath($slicedfiles, $basedir, $this->relative));
- }
+ $this->insertTargetFiles($targetFiles, $result,
+ $targetIndex + count($srcFiles),
+ $this->targetFilePos->getPrefix(),
+ $this->targetFilePos->getSuffix());
- // Checking whether the forward-slash as file-separator has been set.
- // (Applicability: The source {and target} file names must use the forward slash as file separator)
- if ($this->forwardslash) {
- $absolutefilename = str_replace(DIRECTORY_SEPARATOR, '/', $absolutefilename);
+ // targetIndex --> end
+ $result = array_merge(array_slice($orig, $targetIndex + count($srcFiles) + count($targetFiles),
+ count($orig) - $targetIndex, true), $result);
+ } else {
+ // 0 --> targetIndex
+ $result[] = $orig;
+ $result[] = $targetFiles;
+ $result = array_merge(... $result);
+
+ // targetIndex --> srcIndex
+ $result = array_merge(array_slice($orig, $targetIndex + count($targetFiles), $srcIndex - $targetIndex,
+ true), $result);
+
+ // srcIndex --> end
+ $result = array_merge(array_slice($orig, $srcIndex + count($srcFiles) + count($targetFiles),
+ count($orig) - $srcIndex, true), $result);
+ $srcIndex += count($targetFiles);
}
- // Preparing the command to be executed
- $filecommand = str_replace([self::SOURCEFILE_ID], [$absolutefilename], $this->realCommand);
-
- // Command execution
- list($returncode, $output) = $this->executeCommand($filecommand);
+ } else { // no targetFilePos
- // Process the stuff on the first command execution only
- if (0 == $count) {
-
- // Sets the return property
- if ($this->returnProperty) {
- $this->project->setProperty($this->returnProperty, $returncode);
- }
+ // 0 --> srcIndex
+ $result = array_merge(array_slice($orig, 0, $srcIndex, true), $result);
+ // srcIndex --> end
+ $result = array_merge(array_slice($orig, $srcIndex + count($srcFiles), count($orig) - $srcIndex, true),
+ $result);
+ }
+ // fill in source file names
+ foreach ($srcFiles as $i => $file) {
+ if ($this->relative) {
+ $src = $file;
+ } else {
+ $src = (new PhingFile($basedir, $file))->getAbsolutePath();
}
-
- // Sets the output property
- if ($this->outputProperty) {
- $previousValue = $this->project->getProperty($this->outputProperty);
- if (! empty($previousValue)) {
- $previousValue .= "\n";
- }
- $this->project->setProperty($this->outputProperty, $previousValue . implode("\n", $output));
+ if ($this->forwardslash && PhingFile::$separator !== '/') {
+ $src = str_replace(PhingFile::$separator, '/', $src);
}
-
- // Validating the 'return-code'
- if (($this->failonerror) && ($returncode != 0)) {
- $this->throwBuildException("Task exited with code ($returncode)");
+ if ($this->srcFilePos !== null && (count($this->srcFilePos->getPrefix()) > 0
+ || count($this->srcFilePos->getSuffix()) > 0)) {
+ $src = $this->srcFilePos->getPrefix() . $src . $this->srcFilePos->getSuffix();
}
+ $result[$srcIndex + $i] = $src;
+ }
- // Validate the 'parallel' information for command execution. If the command has been
- // executed with the filenames as argument, considering 'maxparallel', just break.
- if (($this->parallel) && (!array_key_exists($count, $files))) {
- break;
- }
- } // Each file processing loop ends
+ $this->commandline = new Commandline(implode(' ', $result));
- return;
- }
+ [$returncode, $output] = $this->executeCommand();
- /**
- * Executes the specified command and returns the return code & output.
- *
- * @param string $command
- *
- * @return array array(return code, array with output)
- */
- private function executeCommand($command)
- {
-
- // Var(s)
- $output = [];
- $return = null;
-
- // Validating the command executor container
- ($this->passthru ? passthru($command, $return) : exec($command, $output, $return));
+ $this->maybeSetReturnPropertyValue($returncode);
- // Log
- $this->log(
- 'Command execution : (' . ($this->passthru ? 'passthru' : 'exec') . ') : ' . $command . " : completed with return code ($return) ",
- $this->loglevel
- );
+ // Sets the output property
+ if ($this->outputProperty) {
+ $previousValue = $this->project->getProperty($this->outputProperty);
+ if (!empty($previousValue)) {
+ $previousValue .= "\n";
+ }
+ $this->project->setProperty($this->outputProperty, $previousValue . implode("\n", $output));
+ }
- return [$return, $output];
+ // Validating the 'return-code'
+ if ($this->checkreturn && ($returncode !== 0)) {
+ $this->throwBuildException("Task exited with code ($returncode)");
+ }
}
/**
@@ -750,36 +686,39 @@ private function executeCommand($command)
*
* @return void
*/
- private function cleanup()
+ protected function cleanup($return = null, $output = null): void
{
-
// Restore working directory
if ($this->dir !== null) {
@chdir($this->currentdirectory);
}
-
- return;
}
/**
* Prepares the filename per base directory and relative path information
*
- * @param $filename
+ * @param array|string $filename
* @param $basedir
* @param $relative
*
* @return mixed processed filenames
+ *
+ * @throws IOException
*/
public function getFilePath($filename, $basedir, $relative)
{
// Validating the 'file' information
- $files = (is_array($filename)) ? $filename : [$filename];
+ $files = (array) $filename;
// Processing the file information
foreach ($files as $index => $file) {
- $absolutefilename = (($relative === false) ? ($basedir . DIRECTORY_SEPARATOR) : '');
+ $absolutefilename = (($relative === false) ? ($basedir . PhingFile::$separator) : '');
$absolutefilename .= $file;
- $files[$index] = $absolutefilename;
+ if ($relative === false) {
+ $files[$index] = (new FileUtils())->normalize($absolutefilename);
+ } else {
+ $files[$index] = $absolutefilename;
+ }
}
return (is_array($filename) ? $files : $files[0]);
@@ -788,12 +727,13 @@ public function getFilePath($filename, $basedir, $relative)
/**
* Throws the exception with specified information
*
- * @param $information Exception information
+ * @param string $information Exception information
*
* @throws BuildException
+ *
* @return void
*/
- private function throwBuildException($information)
+ private function throwBuildException($information): void
{
throw new BuildException('ApplyTask: ' . (string) $information);
}
diff --git a/classes/phing/tasks/system/AttribTask.php b/classes/phing/tasks/system/AttribTask.php
index e554728036..b1c946c92c 100644
--- a/classes/phing/tasks/system/AttribTask.php
+++ b/classes/phing/tasks/system/AttribTask.php
@@ -133,7 +133,7 @@ protected function checkConfiguration()
* @param mixed $e
* @throws BuildException
*/
- public function setExecutable($e)
+ public function setExecutable($e): void
{
throw new BuildException(
$this->getTaskType() . ' doesn\'t support the executable attribute',
diff --git a/classes/phing/tasks/system/ExecTask.php b/classes/phing/tasks/system/ExecTask.php
index c2d9e7257c..2d6becce60 100644
--- a/classes/phing/tasks/system/ExecTask.php
+++ b/classes/phing/tasks/system/ExecTask.php
@@ -125,6 +125,7 @@ class ExecTask extends Task
private $executable;
private $resolveExecutable = false;
private $searchPath = false;
+ private $env;
/**
* @throws \BuildException
@@ -133,6 +134,7 @@ public function __construct()
{
parent::__construct();
$this->commandline = new Commandline();
+ $this->env = new Environment();
}
/**
@@ -251,7 +253,7 @@ protected function buildCommand()
*/
protected function executeCommand()
{
- $cmdl = (string) $this->commandline . $this->realCommand;
+ $cmdl = (string) $this->commandline; // . $this->realCommand;
$this->log('Executing command: ' . $cmdl, $this->logLevel);
@@ -559,6 +561,16 @@ public function setLevel($level): void
}
}
+ /**
+ * Add an environment variable to the launched process.
+ *
+ * @param EnvVariable $var new environment variable.
+ */
+ public function addEnv(EnvVariable $var)
+ {
+ $this->env->addVariable($var);
+ }
+
/**
* Creates a nested tag.
*
@@ -674,7 +686,16 @@ protected function resolveExecutable($exec, $mustSearchPath): ?string
// couldn't find it - must be on path
if ($mustSearchPath) {
$p = null;
- if (getenv('path')) {
+ $environment = $this->env->getVariables();
+ if ($environment !== null) {
+ foreach ($environment as $env) {
+ if ($this->isPath($env)) {
+ $p = new Path($this->getProject(), $this->getPath($env));
+ break;
+ }
+ }
+ }
+ if ($p === null) {
$p = new Path($this->getProject(), getenv('path'));
}
if ($p !== null) {
@@ -690,4 +711,23 @@ protected function resolveExecutable($exec, $mustSearchPath): ?string
return $exec;
}
+
+ private function isPath($line)
+ {
+ return StringHelper::startsWith('PATH=', $line) || StringHelper::startsWith('Path=', $line);
+ }
+
+ private function getPath($value)
+ {
+ if (is_string($value)) {
+ return StringHelper::substring($value, strlen("PATH="));
+ }
+
+ if (is_array($value)) {
+ $p = $value['PATH'];
+ return $p ?? $value['Path'];
+ }
+
+ throw new InvalidArgumentException('$value should be of type array or string.');
+ }
}
diff --git a/classes/phing/types/CommandlineMarker.php b/classes/phing/types/CommandlineMarker.php
index af0a92c9e5..65f0b6fab5 100644
--- a/classes/phing/types/CommandlineMarker.php
+++ b/classes/phing/types/CommandlineMarker.php
@@ -14,6 +14,8 @@ class CommandlineMarker
private $position;
private $realPos = -1;
private $outer;
+ private $prefix;
+ private $suffix;
/**
* @param Commandline $outer
@@ -33,7 +35,7 @@ public function __construct(Commandline $outer, $position)
*/
public function getPosition()
{
- if ($this->realPos == -1) {
+ if ($this->realPos === -1) {
$this->realPos = ($this->outer->executable === null ? 0 : 1);
for ($i = 0; $i < $this->position; $i++) {
$arg = $this->outer->arguments[$i];
@@ -43,4 +45,40 @@ public function getPosition()
return $this->realPos;
}
+
+ /**
+ * Set the prefix to be placed in front of the inserted argument.
+ *
+ * @param string $prefix fixed prefix string.
+ */
+ public function setPrefix($prefix)
+ {
+ $this->prefix = $prefix !== null ? $prefix : '';
+ }
+
+ /**
+ * Get the prefix to be placed in front of the inserted argument.
+ */
+ public function getPrefix()
+ {
+ return $this->prefix;
+ }
+
+ /**
+ * Set the suffix to be placed at the end of the inserted argument.
+ *
+ * @param string $suffix fixed suffix string.
+ */
+ public function setSuffix($suffix)
+ {
+ $this->suffix = $suffix !== null ? $suffix : '';
+ }
+
+ /**
+ * Get the suffix to be placed at the end of the inserted argument.
+ */
+ public function getSuffix()
+ {
+ return $this->suffix;
+ }
}
diff --git a/classes/phing/types/Environment.php b/classes/phing/types/Environment.php
new file mode 100644
index 0000000000..44eb266586
--- /dev/null
+++ b/classes/phing/types/Environment.php
@@ -0,0 +1,75 @@
+.
+ */
+
+/**
+ * Wrapper for environment variables.
+ *
+ */
+class Environment
+{
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /**
+ * a vector of type Environment.Variable
+ * @see Variable
+ */
+ protected $variables;
+
+ /**
+ * constructor
+ */
+ public function __construct()
+ {
+ $this->variables = new ArrayObject();
+ }
+
+ /**
+ * add a variable.
+ * Validity checking is not performed at this point. Duplicates
+ * are not caught either.
+ * @param EnvVariable $var new variable.
+ */
+ public function addVariable(EnvVariable $var)
+ {
+ $this->variables->append($var);
+ }
+
+ /**
+ * get the variable list as an array
+ * @return array of key=value assignment strings
+ * @throws BuildException if any variable is misconfigured
+ */
+ public function getVariables()
+ {
+ if ($this->variables->count() === 0) {
+ return null;
+ }
+ return $this->variables->getArrayCopy();
+ }
+
+ /**
+ * Get the raw vector of variables. This is not a clone.
+ * @return ArrayObject a potentially empty (but never null) vector of elements of type
+ * Variable
+ */
+ public function getVariablesObject()
+ {
+ return $this->variables;
+ }
+}
diff --git a/classes/phing/types/environment/EnvVariable.php b/classes/phing/types/environment/EnvVariable.php
new file mode 100644
index 0000000000..93240f07cd
--- /dev/null
+++ b/classes/phing/types/environment/EnvVariable.php
@@ -0,0 +1,94 @@
+key = $key;
+ }
+
+ /**
+ * set the value
+ * @param string $value
+ */
+ public function setValue($value)
+ {
+ $this->value = $value;
+ }
+
+ /**
+ * key accessor
+ * @return string key
+ */
+ public function getKey()
+ {
+ return $this->key;
+ }
+
+ /**
+ * value accessor
+ * @return string value
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * stringify path and assign to the value.
+ * The value will contain all path elements separated by the appropriate
+ * separator
+ * @param Path $path
+ */
+ public function setPath(Path $path)
+ {
+ $this->value = (string) $path;
+ }
+
+ /**
+ * get the absolute path of a file and assign it to the value
+ * @param PhingFile $file file to use as the value
+ */
+ public function setFile(PhingFile $file)
+ {
+ $this->value = $file->getAbsolutePath();
+ }
+
+ /**
+ * get the assignment string
+ * This is not ready for insertion into a property file without following
+ * the escaping rules of the properties class.
+ * @return string of the form key=value.
+ * @throws BuildException if key or value are unassigned
+ */
+ public function getContent()
+ {
+ $this->validate();
+ return trim($this->key) . '=' . trim($this->value);
+ }
+
+ /**
+ * checks whether all required attributes have been specified.
+ * @throws BuildException if key or value are unassigned
+ */
+ public function validate()
+ {
+ if ($this->key === null || $this->value === null) {
+ throw new BuildException('key and value must be specified for environment variables.');
+ }
+ }
+}
diff --git a/test/classes/phing/tasks/system/ApplyTaskTest.php b/test/classes/phing/tasks/system/ApplyTaskTest.php
index 2fffb5821e..f675610750 100644
--- a/test/classes/phing/tasks/system/ApplyTaskTest.php
+++ b/test/classes/phing/tasks/system/ApplyTaskTest.php
@@ -61,7 +61,7 @@ public function testPropertySetOs()
*/
public function testPropertySetDir()
{
- $this->assertAttributeIsSetTo('dir', new PhingFile('/tmp/'));
+ $this->assertAttributeIsSetTo('dir', new PhingFile($this->project->getProperty('php.tmpdir')));
}
/**
@@ -109,7 +109,7 @@ public function testPropertySetOutputProperty()
*/
public function testPropertySetCheckReturn()
{
- $this->assertAttributeIsSetTo('checkreturn', true, 'failonerror');
+ $this->assertAttributeIsSetTo('checkreturn', true);
}
/**
@@ -117,7 +117,7 @@ public function testPropertySetCheckReturn()
*/
public function testPropertySetOutput()
{
- $this->assertAttributeIsSetTo('output', new PhingFile('/tmp/outputfilename'));
+ $this->assertAttributeIsSetTo('output', new PhingFile($this->project->getProperty('php.tmpdir') . '/outputfilename'));
}
/**
@@ -125,7 +125,7 @@ public function testPropertySetOutput()
*/
public function testPropertySetError()
{
- $this->assertAttributeIsSetTo('error', new PhingFile('/tmp/errorfilename'));
+ $this->assertAttributeIsSetTo('error', new PhingFile($this->project->getProperty('php.tmpdir') . '/errorfilename'));
}
/**
@@ -184,7 +184,7 @@ public function testDoNotExecuteOnWrongOs()
// Process
$this->executeTarget(__FUNCTION__);
- $this->assertInLogs('Not found in unknownos');
+ $this->assertInLogs('was not found in the specified list of valid OSes: unknownos');
$this->assertNotContains('this should not be executed', $this->getOutput());
}
@@ -203,8 +203,7 @@ public function testExecuteOnCorrectOs()
*/
public function testFailOnNonExistingDir()
{
- $nonExistentDir = DIRECTORY_SEPARATOR
- . 'tmp' . DIRECTORY_SEPARATOR
+ $nonExistentDir = $this->project->getProperty('php.tmpdir') . DIRECTORY_SEPARATOR
. 'non' . DIRECTORY_SEPARATOR
. 'existent' . DIRECTORY_SEPARATOR
. 'dir';
@@ -296,7 +295,7 @@ public function testEscape()
public function testPassThru()
{
$this->executeTarget(__FUNCTION__);
- $this->assertInLogs('Command execution : (passthru)');
+ $this->assertInLogs('Executing command:');
}
/**
@@ -392,7 +391,7 @@ public function testMissingExecutable()
public function testEscapedArg()
{
$this->executeTarget(__FUNCTION__);
- $this->assertPropertyEquals('outval', 'abc$b3!SB');
+ $this->assertPropertyEquals('outval', $this->windows ? '"abc$b3 SB"' : 'abc$b3!SB');
}
/**
@@ -404,7 +403,7 @@ public function testRelativeSourceFilenames()
if ($this->windows) {
$this->markTestSkipped("Windows does not have 'ls'");
}
-
+
$this->executeTarget(__FUNCTION__);
$this->assertNotInLogs('/etc/');
}
@@ -454,9 +453,26 @@ public function testParallel()
foreach($this->logBuffer as $log) {
$messages[] = $log['message'];
}
- $this->assertEquals(1, substr_count(implode("\n", $messages), 'Command execution :'));
+ $this->assertEquals(1, substr_count(implode("\n", $messages), 'Executing command:'));
+ }
+
+ public function testMapperSupport()
+ {
+ // Getting a temp. file
+ $tempfile = tempnam(sys_get_temp_dir(), 'phing-exectest-');
+
+ // Setting the property
+ $this->project->setProperty('execTmpFile', $tempfile);
+
+ $this->executeTarget(__FUNCTION__);
+ $messages = [];
+ foreach($this->logBuffer as $log) {
+ $messages[] = $log['message'];
+ }
+ $this->assertTrue(in_array('Applied echo to 4 files and 0 directories.', $messages));
}
+
/**********************************************************************************/
/************************** H E L P E R M E T H O D S ****************************/
/**********************************************************************************/
diff --git a/test/etc/tasks/system/ApplyTest.xml b/test/etc/tasks/system/ApplyTest.xml
index 1eeace9b12..7a7b2d8747 100644
--- a/test/etc/tasks/system/ApplyTest.xml
+++ b/test/etc/tasks/system/ApplyTest.xml
@@ -17,39 +17,39 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -76,7 +76,7 @@
-
+
@@ -85,7 +85,7 @@
-
+
@@ -94,9 +94,9 @@
-
+
-
+
@@ -104,7 +104,7 @@
-
+
@@ -118,7 +118,7 @@
-
+
@@ -126,9 +126,9 @@
-
+
-
+
@@ -145,7 +145,7 @@
The return property's value is: "${execReturnProp}"
-
+
@@ -163,9 +163,9 @@
-
+
-
+
@@ -180,9 +180,9 @@
-
+
-
+
@@ -191,7 +191,7 @@
-
+
@@ -199,13 +199,13 @@
-
+
-
+
@@ -230,7 +230,7 @@
-
+
@@ -238,11 +238,21 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 18726cc87d9e0f0953def2fdab9f6ba260cf03b9 Mon Sep 17 00:00:00 2001
From: siad007
Date: Sat, 17 Feb 2018 16:39:14 +0100
Subject: [PATCH 06/12] Removed trailing whitespace
---
classes/phing/tasks/system/ApplyTask.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/classes/phing/tasks/system/ApplyTask.php b/classes/phing/tasks/system/ApplyTask.php
index 4c70cc43e6..ed44e8a367 100644
--- a/classes/phing/tasks/system/ApplyTask.php
+++ b/classes/phing/tasks/system/ApplyTask.php
@@ -541,6 +541,8 @@ protected function buildCommand()
}
}
+ $this->realCommand = rtrim($this->realCommand);
+
// Log
$this->log('Command built : ' . $this->realCommand, $this->loglevel);
From eb8a922b90ac34f9736c509fc4e11c6eda2328b9 Mon Sep 17 00:00:00 2001
From: siad007
Date: Sat, 17 Feb 2018 16:51:53 +0100
Subject: [PATCH 07/12] Added additional stuff
---
classes/phing/tasks/system/ExecTask.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/classes/phing/tasks/system/ExecTask.php b/classes/phing/tasks/system/ExecTask.php
index 2d6becce60..c66a989e93 100644
--- a/classes/phing/tasks/system/ExecTask.php
+++ b/classes/phing/tasks/system/ExecTask.php
@@ -253,7 +253,7 @@ protected function buildCommand()
*/
protected function executeCommand()
{
- $cmdl = (string) $this->commandline; // . $this->realCommand;
+ $cmdl = (string) $this->commandline . $this->realCommand;
$this->log('Executing command: ' . $cmdl, $this->logLevel);
From 164ce97438842749da4437d4a2e242a0e41e3bd4 Mon Sep 17 00:00:00 2001
From: siad007
Date: Sat, 17 Feb 2018 17:32:14 +0100
Subject: [PATCH 08/12] Fixed executCommand
---
classes/phing/tasks/system/ApplyTask.php | 24 +++++++++++
classes/phing/util/DirectoryScanner.php | 53 ++++++++++++++++++++++--
2 files changed, 74 insertions(+), 3 deletions(-)
diff --git a/classes/phing/tasks/system/ApplyTask.php b/classes/phing/tasks/system/ApplyTask.php
index ed44e8a367..cce622e843 100644
--- a/classes/phing/tasks/system/ApplyTask.php
+++ b/classes/phing/tasks/system/ApplyTask.php
@@ -682,6 +682,30 @@ private function process($srcFiles, $basedir)
}
}
+ /**
+ * Executes the command and returns return code and output.
+ *
+ * @return array array(return code, array with output)
+ * @throws \BuildException
+ */
+ protected function executeCommand()
+ {
+ $cmdl = $this->realCommand;
+
+ $this->log('Executing command: ' . $cmdl, $this->logLevel);
+
+ $output = [];
+ $return = null;
+
+ if ($this->passthru) {
+ passthru($cmdl, $return);
+ } else {
+ exec($cmdl, $output, $return);
+ }
+
+ return [$return, $output];
+ }
+
/**
* Runs cleanup tasks post execution
* - Restore working directory
diff --git a/classes/phing/util/DirectoryScanner.php b/classes/phing/util/DirectoryScanner.php
index 5a525aff3d..c9df6a7425 100644
--- a/classes/phing/util/DirectoryScanner.php
+++ b/classes/phing/util/DirectoryScanner.php
@@ -714,15 +714,37 @@ protected function isExcluded($_name)
return false;
}
+ /**
+ * Return the count of included files.
+ *
+ * @return int
+ *
+ * @throws UnexpectedValueException
+ */
+ public function getIncludedFilesCount(): int
+ {
+ if ($this->filesIncluded === null) {
+ throw new UnexpectedValueException('Must call scan() first');
+ }
+ return count($this->filesIncluded);
+ }
+
/**
* Get the names of the files that matched at least one of the include
* patterns, and matched none of the exclude patterns.
* The names are relative to the basedir.
*
* @return array names of the files
+ * @throws \UnexpectedValueException
*/
- public function getIncludedFiles()
+ public function getIncludedFiles(): array
{
+ if ($this->filesIncluded === null) {
+ throw new UnexpectedValueException('Must call scan() first');
+ }
+
+ sort($this->filesIncluded);
+
return $this->filesIncluded;
}
@@ -778,13 +800,35 @@ public function getDeselectedFiles()
* The names are relative to the basedir.
*
* @return array the names of the directories
+ * @throws \UnexpectedValueException
*/
public function getIncludedDirectories()
{
+ if ($this->dirsIncluded === null) {
+ throw new UnexpectedValueException('Must call scan() first');
+ }
+
+ sort($this->dirsIncluded);
+
return $this->dirsIncluded;
}
+ /**
+ * Return the count of included directories.
+ *
+ * @return int
+ *
+ * @throws UnexpectedValueException
+ */
+ public function getIncludedDirectoriesCount(): int
+ {
+ if ($this->dirsIncluded === null) {
+ throw new UnexpectedValueException('Must call scan() first');
+ }
+ return count($this->dirsIncluded);
+ }
+
/**
* Get the names of the directories that matched at none of the include
* patterns.
@@ -869,10 +913,13 @@ public function isEverythingIncluded()
/**
* Tests whether a name should be selected.
*
- * @param string $name The filename to check for selecting.
- * @param string $file The full file path.
+ * @param string $name The filename to check for selecting.
+ * @param string $file The full file path.
* @return boolean False when the selectors says that the file
* should not be selected, True otherwise.
+ * @throws \BuildException
+ * @throws \IOException
+ * @throws NullPointerException
*/
protected function isSelected($name, $file)
{
From 11a88f0941bcd4dedac8218e3c3e1812a444cca4 Mon Sep 17 00:00:00 2001
From: siad007
Date: Sat, 17 Feb 2018 17:43:53 +0100
Subject: [PATCH 09/12] Fixed test
---
test/classes/phing/tasks/system/ApplyTaskTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/classes/phing/tasks/system/ApplyTaskTest.php b/test/classes/phing/tasks/system/ApplyTaskTest.php
index f675610750..34dda0f785 100644
--- a/test/classes/phing/tasks/system/ApplyTaskTest.php
+++ b/test/classes/phing/tasks/system/ApplyTaskTest.php
@@ -421,7 +421,7 @@ public function testSourceFilename()
$this->executeTarget(__FUNCTION__);
// As the addsourcefilename is 'off', only the executable should be processed in the execution
- $this->assertInLogs(': ls :');
+ $this->assertInLogs('Executing command: ls');
}
/**
From d899e57e884e43c1c807d5981e5d5acad6baa087 Mon Sep 17 00:00:00 2001
From: siad007
Date: Sat, 17 Feb 2018 17:59:33 +0100
Subject: [PATCH 10/12] Some minor fixes
---
classes/phing/types/Commandline.php | 4 +---
test/classes/phing/tasks/system/ApplyTaskTest.php | 4 ++--
test/classes/phing/tasks/system/ExecTaskTest.php | 4 ++--
3 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/classes/phing/types/Commandline.php b/classes/phing/types/Commandline.php
index 21d8668558..d07276910e 100644
--- a/classes/phing/types/Commandline.php
+++ b/classes/phing/types/Commandline.php
@@ -236,9 +236,7 @@ private function toString($lines = null): string
return '';
}
- $cmd = array_shift($lines);
-
- return $cmd . ' ' . implode(' ', array_map(function ($arg) {return self::quoteArgument($arg, $this->escape);}, $lines));
+ return implode(' ', array_map(function ($arg) {return self::quoteArgument($arg, $this->escape);}, $lines));
}
/**
diff --git a/test/classes/phing/tasks/system/ApplyTaskTest.php b/test/classes/phing/tasks/system/ApplyTaskTest.php
index 34dda0f785..c8ed99ba99 100644
--- a/test/classes/phing/tasks/system/ApplyTaskTest.php
+++ b/test/classes/phing/tasks/system/ApplyTaskTest.php
@@ -391,7 +391,7 @@ public function testMissingExecutable()
public function testEscapedArg()
{
$this->executeTarget(__FUNCTION__);
- $this->assertPropertyEquals('outval', $this->windows ? '"abc$b3 SB"' : 'abc$b3!SB');
+ $this->assertPropertyEquals('outval', $this->windows ? 'abc$b3 SB' : 'abc$b3!SB');
}
/**
@@ -440,7 +440,7 @@ public function testOutputAppend()
// Validating the output
$output = @file_get_contents($tempfile);
@unlink($tempfile);
- $this->assertEquals("Append OK\nAppend OK", rtrim($output));
+ $this->assertEquals($this->windows ? "\"Append OK\" \r\n\"Append OK\"" : "Append OK\nAppend OK", rtrim($output));
}
/**
diff --git a/test/classes/phing/tasks/system/ExecTaskTest.php b/test/classes/phing/tasks/system/ExecTaskTest.php
index a85c023cf4..c55e98a9a7 100644
--- a/test/classes/phing/tasks/system/ExecTaskTest.php
+++ b/test/classes/phing/tasks/system/ExecTaskTest.php
@@ -356,14 +356,14 @@ public function testMissingExecutableAndCommand()
public function testEscapedArg()
{
$this->executeTarget(__FUNCTION__);
- $this->assertPropertyEquals('outval', $this->windows ? '"abc$b3 SB"' : 'abc$b3!SB');
+ $this->assertPropertyEquals('outval', $this->windows ? 'abc$b3 SB' : 'abc$b3!SB');
}
public function testEscapedArgWithoutWhitespace()
{
$arg = 'foo|bar';
$this->executeTarget(__FUNCTION__);
- $this->assertInLogs($this->windows ? 'echo "foo|bar" 2>&1' : 'echo \'foo|bar\' 2>&1');
+ $this->assertInLogs($this->windows ? '"echo" "foo|bar" 2>&1' : 'echo \'foo|bar\' 2>&1');
$this->assertNotInLogs($this->windows ? 'echo " foo|bar " 2>&1' : 'echo \' foo|bar \' 2>&1');
}
}
From 6a9d6d57307c955befa3cc092736a976df53c2d2 Mon Sep 17 00:00:00 2001
From: siad007
Date: Sat, 17 Feb 2018 18:04:31 +0100
Subject: [PATCH 11/12] Fixed last on linux
---
test/classes/phing/tasks/system/ExecTaskTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/classes/phing/tasks/system/ExecTaskTest.php b/test/classes/phing/tasks/system/ExecTaskTest.php
index c55e98a9a7..316900928a 100644
--- a/test/classes/phing/tasks/system/ExecTaskTest.php
+++ b/test/classes/phing/tasks/system/ExecTaskTest.php
@@ -363,7 +363,7 @@ public function testEscapedArgWithoutWhitespace()
{
$arg = 'foo|bar';
$this->executeTarget(__FUNCTION__);
- $this->assertInLogs($this->windows ? '"echo" "foo|bar" 2>&1' : 'echo \'foo|bar\' 2>&1');
+ $this->assertInLogs($this->windows ? '"echo" "foo|bar" 2>&1' : '\'echo\' \'foo|bar\' 2>&1');
$this->assertNotInLogs($this->windows ? 'echo " foo|bar " 2>&1' : 'echo \' foo|bar \' 2>&1');
}
}
From 29aa2d39eb6dd210f4204174cc6e630960c555c1 Mon Sep 17 00:00:00 2001
From: siad007
Date: Sun, 18 Feb 2018 16:18:53 +0100
Subject: [PATCH 12/12] ApplyTask additions
---
classes/phing/tasks/system/ApplyTask.php | 50 +++------
classes/phing/tasks/system/ExecTask.php | 8 +-
classes/phing/types/Environment.php | 2 +-
docs/guide/en/source/appendixes/coretasks.xml | 106 +++++++++++++++++-
.../phing/tasks/system/ApplyTaskTest.php | 2 +-
5 files changed, 125 insertions(+), 43 deletions(-)
diff --git a/classes/phing/tasks/system/ApplyTask.php b/classes/phing/tasks/system/ApplyTask.php
index cce622e843..76af02e51a 100644
--- a/classes/phing/tasks/system/ApplyTask.php
+++ b/classes/phing/tasks/system/ApplyTask.php
@@ -101,6 +101,7 @@ class ApplyTask extends ExecTask
/** @var Mapper $mapperElement */
private $mapperElement;
+ private $additionalCmds;
/**
* Set whether empty filesets will be skipped. If true and
@@ -510,6 +511,8 @@ protected function buildCommand()
// Building the executable
$this->realCommand = (string) $this->commandline;
+ $this->additionalCmds = '';
+
// Adding the source filename at the end of command, validating the existing
// sourcefile position explicit mentioning
if ($this->addsourcefile === true) {
@@ -518,16 +521,16 @@ protected function buildCommand()
// Setting command output redirection with content appending
if ($this->output !== null) {
- $this->realCommand .= sprintf(' 1>%s %s', $this->appendoutput ? '>' : '', escapeshellarg($this->output->getPath()));
+ $this->additionalCmds .= sprintf(' 1>%s %s', $this->appendoutput ? '>' : '', escapeshellarg($this->output->getPath()));
} elseif ($this->spawn) { // Validating the 'spawn' configuration, and redirecting the output to 'null'
- $this->realCommand .= sprintf(' %s', 'WIN' === $this->osvariant ? '> NUL' : '1>/dev/null');
+ $this->additionalCmds .= sprintf(' %s', 'WIN' === $this->osvariant ? '> NUL' : '1>/dev/null');
$this->log("For process spawning, setting Output nullification ", $this->loglevel);
}
// Setting command error redirection with content appending
if ($this->error !== null) {
- $this->realCommand .= sprintf(' 2>%s %s', $this->appendoutput ? '>' : '', escapeshellarg($this->error->getPath()));
+ $this->additionalCmds .= sprintf(' 2>%s %s', $this->appendoutput ? '>' : '', escapeshellarg($this->error->getPath()));
}
// Setting the execution as a background process
@@ -535,16 +538,16 @@ protected function buildCommand()
// Validating the O.S. variant
if ('WIN' === $this->osvariant) {
- $this->realCommand = 'start /b ' . $this->realCommand; // MS Windows background process forking
+ $this->additionalCmds = 'start /b ' . $this->additionalCmds; // MS Windows background process forking
} else {
- $this->realCommand .= ' &'; // GNU/Linux background process forking
+ $this->additionalCmds .= ' &'; // GNU/Linux background process forking
}
}
- $this->realCommand = rtrim($this->realCommand);
+ $this->additionalCmds = rtrim($this->additionalCmds);
// Log
- $this->log('Command built : ' . $this->realCommand, $this->loglevel);
+ $this->log('Command built : ' . $this->realCommand . $this->additionalCmds, $this->loglevel);
// Log
$this->log('Command building completed ', $this->loglevel);
@@ -612,10 +615,9 @@ private function process($srcFiles, $basedir)
// srcIndex --> targetIndex
$result += array_slice($orig, $srcIndex + count($srcFiles), $targetIndex - $srcIndex, true);
- $this->insertTargetFiles($targetFiles, $result,
- $targetIndex + count($srcFiles),
- $this->targetFilePos->getPrefix(),
- $this->targetFilePos->getSuffix());
+ $result[] = $orig;
+ $result[] = $targetFiles;
+ $result = array_merge(... $result);
// targetIndex --> end
$result = array_merge(array_slice($orig, $targetIndex + count($srcFiles) + count($targetFiles),
@@ -662,6 +664,8 @@ private function process($srcFiles, $basedir)
}
$this->commandline = new Commandline(implode(' ', $result));
+ $this->commandline->setEscape($this->escape);
+ $this->realCommand = (string) $this->commandline . $this->additionalCmds;
[$returncode, $output] = $this->executeCommand();
@@ -682,30 +686,6 @@ private function process($srcFiles, $basedir)
}
}
- /**
- * Executes the command and returns return code and output.
- *
- * @return array array(return code, array with output)
- * @throws \BuildException
- */
- protected function executeCommand()
- {
- $cmdl = $this->realCommand;
-
- $this->log('Executing command: ' . $cmdl, $this->logLevel);
-
- $output = [];
- $return = null;
-
- if ($this->passthru) {
- passthru($cmdl, $return);
- } else {
- exec($cmdl, $output, $return);
- }
-
- return [$return, $output];
- }
-
/**
* Runs cleanup tasks post execution
* - Restore working directory
diff --git a/classes/phing/tasks/system/ExecTask.php b/classes/phing/tasks/system/ExecTask.php
index c66a989e93..f443f5dd2c 100644
--- a/classes/phing/tasks/system/ExecTask.php
+++ b/classes/phing/tasks/system/ExecTask.php
@@ -243,6 +243,8 @@ protected function buildCommand()
if ($this->spawn) {
$this->realCommand .= ' &';
}
+
+ $this->realCommand = (string) $this->commandline . $this->realCommand;
}
/**
@@ -253,7 +255,7 @@ protected function buildCommand()
*/
protected function executeCommand()
{
- $cmdl = (string) $this->commandline . $this->realCommand;
+ $cmdl = $this->realCommand;
$this->log('Executing command: ' . $cmdl, $this->logLevel);
@@ -625,7 +627,7 @@ protected function isValidOs(): bool
* @param bool $resolveExecutable if true, attempt to resolve the
* path of the executable.
*/
- public function setResolveExecutable(boolean $resolveExecutable): void
+ public function setResolveExecutable($resolveExecutable): void
{
$this->resolveExecutable = $resolveExecutable;
}
@@ -636,7 +638,7 @@ public function setResolveExecutable(boolean $resolveExecutable): void
*
* @param bool $searchPath if true, search PATHs.
*/
- public function setSearchPath(boolean $searchPath): void
+ public function setSearchPath($searchPath): void
{
$this->searchPath = $searchPath;
}
diff --git a/classes/phing/types/Environment.php b/classes/phing/types/Environment.php
index 44eb266586..fff6b02d7c 100644
--- a/classes/phing/types/Environment.php
+++ b/classes/phing/types/Environment.php
@@ -60,7 +60,7 @@ public function getVariables()
if ($this->variables->count() === 0) {
return null;
}
- return $this->variables->getArrayCopy();
+ return array_map(function ($env) {return $env->getContent();}, $this->variables->getArrayCopy());
}
/**
diff --git a/docs/guide/en/source/appendixes/coretasks.xml b/docs/guide/en/source/appendixes/coretasks.xml
index fbc6eee052..2633d449b1 100644
--- a/docs/guide/en/source/appendixes/coretasks.xml
+++ b/docs/guide/en/source/appendixes/coretasks.xml
@@ -496,8 +496,34 @@
No
-
-
+
+ skipemptyfilesets
+ Boolean
+ Don't run the command, if no source files have been found or are newer than their
+ corresponding target files. Despite its name, this attribute applies to filelists as well.
+ false
+ No
+
+
+
+ type
+ String
+ One of file, dir or both. If set to file, only the names of plain files will be sent to
+ the command. If set to dir, only the names of directories are considered.
+ Note: The type attribute does not apply to nested dirsets - dirsets always implicitly assume
+ type to be dir.
+ file
+ No
+
+
+
+ force
+ Boolean
+ Whether to bypass timestamp comparisons for target files.
+ false
+ No
+
+
@@ -585,7 +611,9 @@
filesetfilelistdirset
+ mappersrcfile
+ targetfile
@@ -1782,7 +1810,8 @@ verbose="true" failonerror="false" />
commandString
- The command that is to be executed.
+ NOTE: This attribute is deprecated. Please use executable with nested args.
+ The command that is to be executed.n/aOne of the two
@@ -1892,6 +1921,24 @@ verbose="true" failonerror="false" />
verboseNo
+
+ resolveexecutable
+ Boolean
+ When this attribute is true, the name of the executable is resolved firstly against the
+ project basedir and if that does not exist, against the execution directory if specified.
+ On Unix systems, if you only want to allow execution of commands in the user's path,
+ set this to false.
+ false
+ No
+
+
+ searchpath
+ Boolean
+ When this attribute is true, then system path environment variables will be searched when
+ resolving the location of the executable.
+ false
+ No
+
@@ -1972,6 +2019,59 @@ verbose="true" failonerror="false" />
+ env
+ It is possible to specify environment variables to pass to the system command via nested
+ <env> elements.
+
+ Attributes
+
+
+
+
+
+
+
+
+ Name
+ Type
+ Description
+ Default
+ Required
+
+
+
+
+ key
+ String
+ The name of the environment variable.
+ n/a
+ Yes
+
+
+ value
+ String
+ The literal value for the environment variable.
+ n/a
+ One of these
+
+
+ file
+ String
+ The value for the environment variable. Will be replaced by the absolute
+ filename of the file by Phing.
+ n/a
+
+
+ path
+ String
+ The value for a PATH like environment variable. You can use ; or : as
+ path separators and Phing will convert it to the platform's local conventions.
+ n/a
+
+
+
+