| 1 |
lars |
1 |
<?php
|
|
|
2 |
/*
|
|
|
3 |
* $Id: Phing.php 385 2008-08-19 18:09:17Z mrook $
|
|
|
4 |
*
|
|
|
5 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
6 |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
7 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
8 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
9 |
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
10 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
11 |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
12 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
13 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
14 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
15 |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
16 |
*
|
|
|
17 |
* This software consists of voluntary contributions made by many individuals
|
|
|
18 |
* and is licensed under the LGPL. For more information please see
|
|
|
19 |
* <http://phing.info>.
|
|
|
20 |
*/
|
|
|
21 |
|
|
|
22 |
require_once 'phing/Project.php';
|
|
|
23 |
require_once 'phing/ProjectComponent.php';
|
|
|
24 |
require_once 'phing/Target.php';
|
|
|
25 |
require_once 'phing/Task.php';
|
|
|
26 |
|
|
|
27 |
include_once 'phing/BuildException.php';
|
|
|
28 |
include_once 'phing/ConfigurationException.php';
|
|
|
29 |
include_once 'phing/BuildEvent.php';
|
|
|
30 |
|
|
|
31 |
include_once 'phing/parser/Location.php';
|
|
|
32 |
include_once 'phing/parser/ExpatParser.php';
|
|
|
33 |
include_once 'phing/parser/AbstractHandler.php';
|
|
|
34 |
include_once 'phing/parser/ProjectConfigurator.php';
|
|
|
35 |
include_once 'phing/parser/RootHandler.php';
|
|
|
36 |
include_once 'phing/parser/ProjectHandler.php';
|
|
|
37 |
include_once 'phing/parser/TaskHandler.php';
|
|
|
38 |
include_once 'phing/parser/TargetHandler.php';
|
|
|
39 |
include_once 'phing/parser/DataTypeHandler.php';
|
|
|
40 |
include_once 'phing/parser/NestedElementHandler.php';
|
|
|
41 |
|
|
|
42 |
include_once 'phing/system/util/Properties.php';
|
|
|
43 |
include_once 'phing/util/StringHelper.php';
|
|
|
44 |
include_once 'phing/system/io/PhingFile.php';
|
|
|
45 |
include_once 'phing/system/io/OutputStream.php';
|
|
|
46 |
include_once 'phing/system/io/FileOutputStream.php';
|
|
|
47 |
include_once 'phing/system/io/FileReader.php';
|
|
|
48 |
include_once 'phing/system/util/Register.php';
|
|
|
49 |
|
|
|
50 |
/**
|
|
|
51 |
* Entry point into Phing. This class handles the full lifecycle of a build -- from
|
|
|
52 |
* parsing & handling commandline arguments to assembling the project to shutting down
|
|
|
53 |
* and cleaning up in the end.
|
|
|
54 |
*
|
|
|
55 |
* If you are invoking Phing from an external application, this is still
|
|
|
56 |
* the class to use. Your applicaiton can invoke the start() method, passing
|
|
|
57 |
* any commandline arguments or additional properties.
|
|
|
58 |
*
|
|
|
59 |
* @author Andreas Aderhold <andi@binarycloud.com>
|
|
|
60 |
* @author Hans Lellelid <hans@xmpl.org>
|
|
|
61 |
* @version $Revision: 1.51 $
|
|
|
62 |
* @package phing
|
|
|
63 |
*/
|
|
|
64 |
class Phing {
|
|
|
65 |
|
|
|
66 |
/** The default build file name */
|
|
|
67 |
const DEFAULT_BUILD_FILENAME = "build.xml";
|
|
|
68 |
|
|
|
69 |
/** Our current message output status. Follows Project::MSG_XXX */
|
|
|
70 |
private static $msgOutputLevel = Project::MSG_INFO;
|
|
|
71 |
|
|
|
72 |
/** PhingFile that we are using for configuration */
|
|
|
73 |
private $buildFile = null;
|
|
|
74 |
|
|
|
75 |
/** The build targets */
|
|
|
76 |
private $targets = array();
|
|
|
77 |
|
|
|
78 |
/**
|
|
|
79 |
* Set of properties that are passed in from commandline or invoking code.
|
|
|
80 |
* @var Properties
|
|
|
81 |
*/
|
|
|
82 |
private static $definedProps;
|
|
|
83 |
|
|
|
84 |
/** Names of classes to add as listeners to project */
|
|
|
85 |
private $listeners = array();
|
|
|
86 |
|
|
|
87 |
private $loggerClassname = null;
|
|
|
88 |
|
|
|
89 |
/** The class to handle input (can be only one). */
|
|
|
90 |
private $inputHandlerClassname;
|
|
|
91 |
|
|
|
92 |
/** Indicates if this phing should be run */
|
|
|
93 |
private $readyToRun = false;
|
|
|
94 |
|
|
|
95 |
/** Indicates we should only parse and display the project help information */
|
|
|
96 |
private $projectHelp = false;
|
|
|
97 |
|
|
|
98 |
/** Used by utility function getResourcePath() */
|
|
|
99 |
private static $importPaths;
|
|
|
100 |
|
|
|
101 |
/** System-wide static properties (moved from System) */
|
|
|
102 |
private static $properties = array();
|
|
|
103 |
|
|
|
104 |
/** Static system timer. */
|
|
|
105 |
private static $timer;
|
|
|
106 |
|
|
|
107 |
/** The current Project */
|
|
|
108 |
private static $currentProject;
|
|
|
109 |
|
|
|
110 |
/** Whether to capture PHP errors to buffer. */
|
|
|
111 |
private static $phpErrorCapture = false;
|
|
|
112 |
|
|
|
113 |
/** Array of captured PHP errors */
|
|
|
114 |
private static $capturedPhpErrors = array();
|
|
|
115 |
|
|
|
116 |
/**
|
|
|
117 |
* @var OUtputStream Stream for standard output.
|
|
|
118 |
*/
|
|
|
119 |
private static $out;
|
|
|
120 |
|
|
|
121 |
/**
|
|
|
122 |
* @var OutputStream Stream for error output.
|
|
|
123 |
*/
|
|
|
124 |
private static $err;
|
|
|
125 |
|
|
|
126 |
/**
|
|
|
127 |
* @var boolean Whether we are using a logfile.
|
|
|
128 |
*/
|
|
|
129 |
private static $isLogFileUsed = false;
|
|
|
130 |
|
|
|
131 |
/**
|
|
|
132 |
* Array to hold original ini settings that Phing changes (and needs
|
|
|
133 |
* to restore in restoreIni() method).
|
|
|
134 |
*
|
|
|
135 |
* @var array Struct of array(setting-name => setting-value)
|
|
|
136 |
* @see restoreIni()
|
|
|
137 |
*/
|
|
|
138 |
private static $origIniSettings = array();
|
|
|
139 |
|
|
|
140 |
/**
|
|
|
141 |
* Entry point allowing for more options from other front ends.
|
|
|
142 |
*
|
|
|
143 |
* This method encapsulates the complete build lifecycle.
|
|
|
144 |
*
|
|
|
145 |
* @param array $args The commandline args passed to phing shell script.
|
|
|
146 |
* @param array $additionalUserProperties Any additional properties to be passed to Phing (alternative front-end might implement this).
|
|
|
147 |
* These additional properties will be available using the getDefinedProperty() method and will
|
|
|
148 |
* be added to the project's "user" properties
|
|
|
149 |
* @see execute()
|
|
|
150 |
* @see runBuild()
|
|
|
151 |
* @throws Exception - if there is an error during build
|
|
|
152 |
*/
|
|
|
153 |
public static function start($args, array $additionalUserProperties = null) {
|
|
|
154 |
|
|
|
155 |
try {
|
|
|
156 |
$m = new Phing();
|
|
|
157 |
$m->execute($args);
|
|
|
158 |
} catch (Exception $exc) {
|
|
|
159 |
self::handleLogfile();
|
|
|
160 |
throw $exc;
|
|
|
161 |
}
|
|
|
162 |
|
|
|
163 |
if ($additionalUserProperties !== null) {
|
|
|
164 |
foreach($additionalUserProperties as $key => $value) {
|
|
|
165 |
$m->setDefinedProperty($key, $value);
|
|
|
166 |
}
|
|
|
167 |
}
|
|
|
168 |
|
|
|
169 |
try {
|
|
|
170 |
$m->runBuild();
|
|
|
171 |
} catch(Exception $exc) {
|
|
|
172 |
self::handleLogfile();
|
|
|
173 |
throw $exc;
|
|
|
174 |
}
|
|
|
175 |
|
|
|
176 |
// everything fine, shutdown
|
|
|
177 |
self::handleLogfile();
|
|
|
178 |
}
|
|
|
179 |
|
|
|
180 |
/**
|
|
|
181 |
* Prints the message of the Exception if it's not null.
|
|
|
182 |
* @param Exception $t
|
|
|
183 |
*/
|
|
|
184 |
public static function printMessage(Exception $t) {
|
|
|
185 |
if (self::$err === null) { // Make sure our error output is initialized
|
|
|
186 |
self::initializeOutputStreams();
|
|
|
187 |
}
|
|
|
188 |
if (self::getMsgOutputLevel() >= Project::MSG_VERBOSE) {
|
|
|
189 |
self::$err->write($t->__toString() . PHP_EOL);
|
|
|
190 |
} else {
|
|
|
191 |
self::$err->write($t->getMessage() . PHP_EOL);
|
|
|
192 |
}
|
|
|
193 |
}
|
|
|
194 |
|
|
|
195 |
/**
|
|
|
196 |
* Sets the stdout and stderr streams if they are not already set.
|
|
|
197 |
*/
|
|
|
198 |
private static function initializeOutputStreams() {
|
|
|
199 |
if (self::$out === null) {
|
|
|
200 |
self::$out = new OutputStream(fopen("php://stdout", "w"));
|
|
|
201 |
}
|
|
|
202 |
if (self::$err === null) {
|
|
|
203 |
self::$err = new OutputStream(fopen("php://stderr", "w"));
|
|
|
204 |
}
|
|
|
205 |
}
|
|
|
206 |
|
|
|
207 |
/**
|
|
|
208 |
* Sets the stream to use for standard (non-error) output.
|
|
|
209 |
* @param OutputStream $stream The stream to use for standard output.
|
|
|
210 |
*/
|
|
|
211 |
public static function setOutputStream(OutputStream $stream) {
|
|
|
212 |
self::$out = $stream;
|
|
|
213 |
}
|
|
|
214 |
|
|
|
215 |
/**
|
|
|
216 |
* Gets the stream to use for standard (non-error) output.
|
|
|
217 |
* @return OutputStream
|
|
|
218 |
*/
|
|
|
219 |
public static function getOutputStream() {
|
|
|
220 |
return self::$out;
|
|
|
221 |
}
|
|
|
222 |
|
|
|
223 |
/**
|
|
|
224 |
* Sets the stream to use for error output.
|
|
|
225 |
* @param OutputStream $stream The stream to use for error output.
|
|
|
226 |
*/
|
|
|
227 |
public static function setErrorStream(OutputStream $stream) {
|
|
|
228 |
self::$err = $stream;
|
|
|
229 |
}
|
|
|
230 |
|
|
|
231 |
/**
|
|
|
232 |
* Gets the stream to use for error output.
|
|
|
233 |
* @return OutputStream
|
|
|
234 |
*/
|
|
|
235 |
public static function getErrorStream() {
|
|
|
236 |
return self::$err;
|
|
|
237 |
}
|
|
|
238 |
|
|
|
239 |
/**
|
|
|
240 |
* Close logfiles, if we have been writing to them.
|
|
|
241 |
*
|
|
|
242 |
* @since Phing 2.3.0
|
|
|
243 |
*/
|
|
|
244 |
private static function handleLogfile() {
|
|
|
245 |
if (self::$isLogFileUsed) {
|
|
|
246 |
self::$err->close();
|
|
|
247 |
self::$out->close();
|
|
|
248 |
}
|
|
|
249 |
}
|
|
|
250 |
|
|
|
251 |
/**
|
|
|
252 |
* Making output level a static property so that this property
|
|
|
253 |
* can be accessed by other parts of the system, enabling
|
|
|
254 |
* us to display more information -- e.g. backtraces -- for "debug" level.
|
|
|
255 |
* @return int
|
|
|
256 |
*/
|
|
|
257 |
public static function getMsgOutputLevel() {
|
|
|
258 |
return self::$msgOutputLevel;
|
|
|
259 |
}
|
|
|
260 |
|
|
|
261 |
/**
|
|
|
262 |
* Command line entry point. This method kicks off the building
|
|
|
263 |
* of a project object and executes a build using either a given
|
|
|
264 |
* target or the default target.
|
|
|
265 |
*
|
|
|
266 |
* @param array $args Command line args.
|
|
|
267 |
* @return void
|
|
|
268 |
*/
|
|
|
269 |
public static function fire($args) {
|
|
|
270 |
self::start($args, null);
|
|
|
271 |
}
|
|
|
272 |
|
|
|
273 |
/**
|
|
|
274 |
* Setup/initialize Phing environment from commandline args.
|
|
|
275 |
* @param array $args commandline args passed to phing shell.
|
|
|
276 |
* @return void
|
|
|
277 |
*/
|
|
|
278 |
public function execute($args) {
|
|
|
279 |
|
|
|
280 |
self::$definedProps = new Properties();
|
|
|
281 |
$this->searchForThis = null;
|
|
|
282 |
|
|
|
283 |
// 1) First handle any options which should always
|
|
|
284 |
// Note: The order in which these are executed is important (if multiple of these options are specified)
|
|
|
285 |
|
|
|
286 |
if (in_array('-help', $args) || in_array('-h', $args)) {
|
|
|
287 |
$this->printUsage();
|
|
|
288 |
return;
|
|
|
289 |
}
|
|
|
290 |
|
|
|
291 |
if (in_array('-version', $args) || in_array('-v', $args)) {
|
|
|
292 |
$this->printVersion();
|
|
|
293 |
return;
|
|
|
294 |
}
|
|
|
295 |
|
|
|
296 |
// 2) Next pull out stand-alone args.
|
|
|
297 |
// Note: The order in which these are executed is important (if multiple of these options are specified)
|
|
|
298 |
|
|
|
299 |
if (false !== ($key = array_search('-quiet', $args, true))) {
|
|
|
300 |
self::$msgOutputLevel = Project::MSG_WARN;
|
|
|
301 |
unset($args[$key]);
|
|
|
302 |
}
|
|
|
303 |
|
|
|
304 |
if (false !== ($key = array_search('-verbose', $args, true))) {
|
|
|
305 |
self::$msgOutputLevel = Project::MSG_VERBOSE;
|
|
|
306 |
unset($args[$key]);
|
|
|
307 |
}
|
|
|
308 |
|
|
|
309 |
if (false !== ($key = array_search('-debug', $args, true))) {
|
|
|
310 |
self::$msgOutputLevel = Project::MSG_DEBUG;
|
|
|
311 |
unset($args[$key]);
|
|
|
312 |
}
|
|
|
313 |
|
|
|
314 |
// 3) Finally, cycle through to parse remaining args
|
|
|
315 |
//
|
|
|
316 |
$keys = array_keys($args); // Use keys and iterate to max(keys) since there may be some gaps
|
|
|
317 |
$max = $keys ? max($keys) : -1;
|
|
|
318 |
for($i=0; $i <= $max; $i++) {
|
|
|
319 |
|
|
|
320 |
if (!array_key_exists($i, $args)) {
|
|
|
321 |
// skip this argument, since it must have been removed above.
|
|
|
322 |
continue;
|
|
|
323 |
}
|
|
|
324 |
|
|
|
325 |
$arg = $args[$i];
|
|
|
326 |
|
|
|
327 |
if ($arg == "-logfile") {
|
|
|
328 |
try {
|
|
|
329 |
// see: http://phing.info/trac/ticket/65
|
|
|
330 |
if (!isset($args[$i+1])) {
|
|
|
331 |
$msg = "You must specify a log file when using the -logfile argument\n";
|
|
|
332 |
throw new ConfigurationException($msg);
|
|
|
333 |
} else {
|
|
|
334 |
$logFile = new PhingFile($args[++$i]);
|
|
|
335 |
$out = new FileOutputStream($logFile); // overwrite
|
|
|
336 |
self::setOutputStream($out);
|
|
|
337 |
self::setErrorStream($out);
|
|
|
338 |
self::$isLogFileUsed = true;
|
|
|
339 |
}
|
|
|
340 |
} catch (IOException $ioe) {
|
|
|
341 |
$msg = "Cannot write on the specified log file. Make sure the path exists and you have write permissions.";
|
|
|
342 |
throw new ConfigurationException($msg, $ioe);
|
|
|
343 |
}
|
|
|
344 |
} elseif ($arg == "-buildfile" || $arg == "-file" || $arg == "-f") {
|
|
|
345 |
if (!isset($args[$i+1])) {
|
|
|
346 |
$msg = "You must specify a buildfile when using the -buildfile argument.";
|
|
|
347 |
throw new ConfigurationException($msg);
|
|
|
348 |
} else {
|
|
|
349 |
$this->buildFile = new PhingFile($args[++$i]);
|
|
|
350 |
}
|
|
|
351 |
} elseif ($arg == "-listener") {
|
|
|
352 |
if (!isset($args[$i+1])) {
|
|
|
353 |
$msg = "You must specify a listener class when using the -listener argument";
|
|
|
354 |
throw new ConfigurationException($msg);
|
|
|
355 |
} else {
|
|
|
356 |
$this->listeners[] = $args[++$i];
|
|
|
357 |
}
|
|
|
358 |
} elseif (StringHelper::startsWith("-D", $arg)) {
|
|
|
359 |
$name = substr($arg, 2);
|
|
|
360 |
$value = null;
|
|
|
361 |
$posEq = strpos($name, "=");
|
|
|
362 |
if ($posEq !== false) {
|
|
|
363 |
$value = substr($name, $posEq+1);
|
|
|
364 |
$name = substr($name, 0, $posEq);
|
|
|
365 |
} elseif ($i < count($args)-1) {
|
|
|
366 |
$value = $args[++$i];
|
|
|
367 |
}
|
|
|
368 |
self::$definedProps->setProperty($name, $value);
|
|
|
369 |
} elseif ($arg == "-logger") {
|
|
|
370 |
if (!isset($args[$i+1])) {
|
|
|
371 |
$msg = "You must specify a classname when using the -logger argument";
|
|
|
372 |
throw new ConfigurationException($msg);
|
|
|
373 |
} else {
|
|
|
374 |
$this->loggerClassname = $args[++$i];
|
|
|
375 |
}
|
|
|
376 |
} elseif ($arg == "-inputhandler") {
|
|
|
377 |
if ($this->inputHandlerClassname !== null) {
|
|
|
378 |
throw new ConfigurationException("Only one input handler class may be specified.");
|
|
|
379 |
}
|
|
|
380 |
if (!isset($args[$i+1])) {
|
|
|
381 |
$msg = "You must specify a classname when using the -inputhandler argument";
|
|
|
382 |
throw new ConfigurationException($msg);
|
|
|
383 |
} else {
|
|
|
384 |
$this->inputHandlerClassname = $args[++$i];
|
|
|
385 |
}
|
|
|
386 |
} elseif ($arg == "-projecthelp" || $arg == "-targets" || $arg == "-list" || $arg == "-l" || $arg == "-p") {
|
|
|
387 |
// set the flag to display the targets and quit
|
|
|
388 |
$this->projectHelp = true;
|
|
|
389 |
} elseif ($arg == "-find") {
|
|
|
390 |
// eat up next arg if present, default to build.xml
|
|
|
391 |
if ($i < count($args)-1) {
|
|
|
392 |
$this->searchForThis = $args[++$i];
|
|
|
393 |
} else {
|
|
|
394 |
$this->searchForThis = self::DEFAULT_BUILD_FILENAME;
|
|
|
395 |
}
|
|
|
396 |
} elseif (substr($arg,0,1) == "-") {
|
|
|
397 |
// we don't have any more args
|
|
|
398 |
self::$err->write("Unknown argument: $arg" . PHP_EOL);
|
|
|
399 |
self::printUsage();
|
|
|
400 |
return;
|
|
|
401 |
} else {
|
|
|
402 |
// if it's no other arg, it may be the target
|
|
|
403 |
array_push($this->targets, $arg);
|
|
|
404 |
}
|
|
|
405 |
}
|
|
|
406 |
|
|
|
407 |
// if buildFile was not specified on the command line,
|
|
|
408 |
if ($this->buildFile === null) {
|
|
|
409 |
// but -find then search for it
|
|
|
410 |
if ($this->searchForThis !== null) {
|
|
|
411 |
$this->buildFile = $this->_findBuildFile(self::getProperty("user.dir"), $this->searchForThis);
|
|
|
412 |
} else {
|
|
|
413 |
$this->buildFile = new PhingFile(self::DEFAULT_BUILD_FILENAME);
|
|
|
414 |
}
|
|
|
415 |
}
|
|
|
416 |
// make sure buildfile exists
|
|
|
417 |
if (!$this->buildFile->exists()) {
|
|
|
418 |
throw new ConfigurationException("Buildfile: " . $this->buildFile->__toString() . " does not exist!");
|
|
|
419 |
}
|
|
|
420 |
|
|
|
421 |
// make sure it's not a directory
|
|
|
422 |
if ($this->buildFile->isDirectory()) {
|
|
|
423 |
throw new ConfigurationException("Buildfile: " . $this->buildFile->__toString() . " is a dir!");
|
|
|
424 |
}
|
|
|
425 |
|
|
|
426 |
$this->readyToRun = true;
|
|
|
427 |
}
|
|
|
428 |
|
|
|
429 |
/**
|
|
|
430 |
* Helper to get the parent file for a given file.
|
|
|
431 |
*
|
|
|
432 |
* @param PhingFile $file
|
|
|
433 |
* @return PhingFile Parent file or null if none
|
|
|
434 |
*/
|
|
|
435 |
private function _getParentFile(PhingFile $file) {
|
|
|
436 |
$filename = $file->getAbsolutePath();
|
|
|
437 |
$file = new PhingFile($filename);
|
|
|
438 |
$filename = $file->getParent();
|
|
|
439 |
return ($filename === null) ? null : new PhingFile($filename);
|
|
|
440 |
}
|
|
|
441 |
|
|
|
442 |
/**
|
|
|
443 |
* Search parent directories for the build file.
|
|
|
444 |
*
|
|
|
445 |
* Takes the given target as a suffix to append to each
|
|
|
446 |
* parent directory in search of a build file. Once the
|
|
|
447 |
* root of the file-system has been reached an exception
|
|
|
448 |
* is thrown.
|
|
|
449 |
*
|
|
|
450 |
* @param string $start Start file path.
|
|
|
451 |
* @param string $suffix Suffix filename to look for in parents.
|
|
|
452 |
* @return PhingFile A handle to the build file
|
|
|
453 |
*
|
|
|
454 |
* @throws BuildException Failed to locate a build file
|
|
|
455 |
*/
|
|
|
456 |
private function _findBuildFile($start, $suffix) {
|
|
|
457 |
$startf = new PhingFile($start);
|
|
|
458 |
$parent = new PhingFile($startf->getAbsolutePath());
|
|
|
459 |
$file = new PhingFile($parent, $suffix);
|
|
|
460 |
|
|
|
461 |
// check if the target file exists in the current directory
|
|
|
462 |
while (!$file->exists()) {
|
|
|
463 |
// change to parent directory
|
|
|
464 |
$parent = $this->_getParentFile($parent);
|
|
|
465 |
|
|
|
466 |
// if parent is null, then we are at the root of the fs,
|
|
|
467 |
// complain that we can't find the build file.
|
|
|
468 |
if ($parent === null) {
|
|
|
469 |
throw new ConfigurationException("Could not locate a build file!");
|
|
|
470 |
}
|
|
|
471 |
// refresh our file handle
|
|
|
472 |
$file = new PhingFile($parent, $suffix);
|
|
|
473 |
}
|
|
|
474 |
return $file;
|
|
|
475 |
}
|
|
|
476 |
|
|
|
477 |
/**
|
|
|
478 |
* Executes the build.
|
|
|
479 |
* @return void
|
|
|
480 |
*/
|
|
|
481 |
function runBuild() {
|
|
|
482 |
|
|
|
483 |
if (!$this->readyToRun) {
|
|
|
484 |
return;
|
|
|
485 |
}
|
|
|
486 |
|
|
|
487 |
$project = new Project();
|
|
|
488 |
|
|
|
489 |
self::setCurrentProject($project);
|
|
|
490 |
set_error_handler(array('Phing', 'handlePhpError'));
|
|
|
491 |
|
|
|
492 |
$error = null;
|
|
|
493 |
|
|
|
494 |
$this->addBuildListeners($project);
|
|
|
495 |
$this->addInputHandler($project);
|
|
|
496 |
|
|
|
497 |
// set this right away, so that it can be used in logging.
|
|
|
498 |
$project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath());
|
|
|
499 |
|
|
|
500 |
try {
|
|
|
501 |
$project->fireBuildStarted();
|
|
|
502 |
$project->init();
|
|
|
503 |
} catch (Exception $exc) {
|
|
|
504 |
$project->fireBuildFinished($exc);
|
|
|
505 |
throw $exc;
|
|
|
506 |
}
|
|
|
507 |
|
|
|
508 |
$project->setUserProperty("phing.version", $this->getPhingVersion());
|
|
|
509 |
|
|
|
510 |
$e = self::$definedProps->keys();
|
|
|
511 |
while (count($e)) {
|
|
|
512 |
$arg = (string) array_shift($e);
|
|
|
513 |
$value = (string) self::$definedProps->getProperty($arg);
|
|
|
514 |
$project->setUserProperty($arg, $value);
|
|
|
515 |
}
|
|
|
516 |
unset($e);
|
|
|
517 |
|
|
|
518 |
$project->setUserProperty("phing.file", $this->buildFile->getAbsolutePath());
|
|
|
519 |
|
|
|
520 |
// first use the Configurator to create the project object
|
|
|
521 |
// from the given build file.
|
|
|
522 |
|
|
|
523 |
try {
|
|
|
524 |
ProjectConfigurator::configureProject($project, $this->buildFile);
|
|
|
525 |
} catch (Exception $exc) {
|
|
|
526 |
$project->fireBuildFinished($exc);
|
|
|
527 |
restore_error_handler();
|
|
|
528 |
self::unsetCurrentProject();
|
|
|
529 |
throw $exc;
|
|
|
530 |
}
|
|
|
531 |
|
|
|
532 |
// make sure that we have a target to execute
|
|
|
533 |
if (count($this->targets) === 0) {
|
|
|
534 |
$this->targets[] = $project->getDefaultTarget();
|
|
|
535 |
}
|
|
|
536 |
|
|
|
537 |
// execute targets if help param was not given
|
|
|
538 |
if (!$this->projectHelp) {
|
|
|
539 |
|
|
|
540 |
try {
|
|
|
541 |
$project->executeTargets($this->targets);
|
|
|
542 |
} catch (Exception $exc) {
|
|
|
543 |
$project->fireBuildFinished($exc);
|
|
|
544 |
restore_error_handler();
|
|
|
545 |
self::unsetCurrentProject();
|
|
|
546 |
throw $exc;
|
|
|
547 |
}
|
|
|
548 |
}
|
|
|
549 |
// if help is requested print it
|
|
|
550 |
if ($this->projectHelp) {
|
|
|
551 |
try {
|
|
|
552 |
$this->printDescription($project);
|
|
|
553 |
$this->printTargets($project);
|
|
|
554 |
} catch (Exception $exc) {
|
|
|
555 |
$project->fireBuildFinished($exc);
|
|
|
556 |
restore_error_handler();
|
|
|
557 |
self::unsetCurrentProject();
|
|
|
558 |
throw $exc;
|
|
|
559 |
}
|
|
|
560 |
}
|
|
|
561 |
|
|
|
562 |
// finally {
|
|
|
563 |
if (!$this->projectHelp) {
|
|
|
564 |
$project->fireBuildFinished(null);
|
|
|
565 |
}
|
|
|
566 |
|
|
|
567 |
restore_error_handler();
|
|
|
568 |
self::unsetCurrentProject();
|
|
|
569 |
}
|
|
|
570 |
|
|
|
571 |
/**
|
|
|
572 |
* Bind any registered build listeners to this project.
|
|
|
573 |
*
|
|
|
574 |
* This means adding the logger and any build listeners that were specified
|
|
|
575 |
* with -listener arg.
|
|
|
576 |
*
|
|
|
577 |
* @param Project $project
|
|
|
578 |
* @return void
|
|
|
579 |
*/
|
|
|
580 |
private function addBuildListeners(Project $project) {
|
|
|
581 |
// Add the default listener
|
|
|
582 |
$project->addBuildListener($this->createLogger());
|
|
|
583 |
|
|
|
584 |
foreach($this->listeners as $listenerClassname) {
|
|
|
585 |
try {
|
|
|
586 |
$clz = Phing::import($listenerClassname);
|
|
|
587 |
} catch (Exception $x) {
|
|
|
588 |
$msg = "Unable to instantiate specified listener "
|
|
|
589 |
. "class " . $listenerClassname . " : "
|
|
|
590 |
. $e->getMessage();
|
|
|
591 |
throw new ConfigurationException($msg);
|
|
|
592 |
}
|
|
|
593 |
|
|
|
594 |
$listener = new $clz();
|
|
|
595 |
|
|
|
596 |
if ($listener instanceof StreamRequiredBuildLogger) {
|
|
|
597 |
throw new ConfigurationException("Unable to add " . $listenerClassname . " as a listener, since it requires explicit error/output streams. (You can specify it as a -logger.)");
|
|
|
598 |
}
|
|
|
599 |
$project->addBuildListener($listener);
|
|
|
600 |
}
|
|
|
601 |
}
|
|
|
602 |
|
|
|
603 |
/**
|
|
|
604 |
* Creates the InputHandler and adds it to the project.
|
|
|
605 |
*
|
|
|
606 |
* @param Project $project the project instance.
|
|
|
607 |
*
|
|
|
608 |
* @throws BuildException if a specified InputHandler
|
|
|
609 |
* class could not be loaded.
|
|
|
610 |
*/
|
|
|
611 |
private function addInputHandler(Project $project) {
|
|
|
612 |
if ($this->inputHandlerClassname === null) {
|
|
|
613 |
$handler = new DefaultInputHandler();
|
|
|
614 |
} else {
|
|
|
615 |
try {
|
|
|
616 |
$clz = Phing::import($this->inputHandlerClassname);
|
|
|
617 |
$handler = new $clz();
|
|
|
618 |
if ($project !== null && method_exists($handler, 'setProject')) {
|
|
|
619 |
$handler->setProject($project);
|
|
|
620 |
}
|
|
|
621 |
} catch (Exception $e) {
|
|
|
622 |
$msg = "Unable to instantiate specified input handler "
|
|
|
623 |
. "class " . $this->inputHandlerClassname . " : "
|
|
|
624 |
. $e->getMessage();
|
|
|
625 |
throw new ConfigurationException($msg);
|
|
|
626 |
}
|
|
|
627 |
}
|
|
|
628 |
$project->setInputHandler($handler);
|
|
|
629 |
}
|
|
|
630 |
|
|
|
631 |
/**
|
|
|
632 |
* Creates the default build logger for sending build events to the log.
|
|
|
633 |
* @return BuildLogger The created Logger
|
|
|
634 |
*/
|
|
|
635 |
private function createLogger() {
|
|
|
636 |
if ($this->loggerClassname !== null) {
|
|
|
637 |
self::import($this->loggerClassname);
|
|
|
638 |
// get class name part
|
|
|
639 |
$classname = self::import($this->loggerClassname);
|
|
|
640 |
$logger = new $classname;
|
|
|
641 |
if (!($logger instanceof BuildLogger)) {
|
|
|
642 |
throw new BuildException($classname . ' does not implement the BuildLogger interface.');
|
|
|
643 |
}
|
|
|
644 |
} else {
|
|
|
645 |
require_once 'phing/listener/DefaultLogger.php';
|
|
|
646 |
$logger = new DefaultLogger();
|
|
|
647 |
}
|
|
|
648 |
$logger->setMessageOutputLevel(self::$msgOutputLevel);
|
|
|
649 |
$logger->setOutputStream(self::$out);
|
|
|
650 |
$logger->setErrorStream(self::$err);
|
|
|
651 |
return $logger;
|
|
|
652 |
}
|
|
|
653 |
|
|
|
654 |
/**
|
|
|
655 |
* Sets the current Project
|
|
|
656 |
* @param Project $p
|
|
|
657 |
*/
|
|
|
658 |
public static function setCurrentProject($p) {
|
|
|
659 |
self::$currentProject = $p;
|
|
|
660 |
}
|
|
|
661 |
|
|
|
662 |
/**
|
|
|
663 |
* Unsets the current Project
|
|
|
664 |
*/
|
|
|
665 |
public static function unsetCurrentProject() {
|
|
|
666 |
self::$currentProject = null;
|
|
|
667 |
}
|
|
|
668 |
|
|
|
669 |
/**
|
|
|
670 |
* Gets the current Project.
|
|
|
671 |
* @return Project Current Project or NULL if none is set yet/still.
|
|
|
672 |
*/
|
|
|
673 |
public static function getCurrentProject() {
|
|
|
674 |
return self::$currentProject;
|
|
|
675 |
}
|
|
|
676 |
|
|
|
677 |
/**
|
|
|
678 |
* A static convenience method to send a log to the current (last-setup) Project.
|
|
|
679 |
* If there is no currently-configured Project, then this will do nothing.
|
|
|
680 |
* @param string $message
|
|
|
681 |
* @param int $priority Project::MSG_INFO, etc.
|
|
|
682 |
*/
|
|
|
683 |
public static function log($message, $priority = Project::MSG_INFO) {
|
|
|
684 |
$p = self::getCurrentProject();
|
|
|
685 |
if ($p) {
|
|
|
686 |
$p->log($message, $priority);
|
|
|
687 |
}
|
|
|
688 |
}
|
|
|
689 |
|
|
|
690 |
/**
|
|
|
691 |
* Error handler for PHP errors encountered during the build.
|
|
|
692 |
* This uses the logging for the currently configured project.
|
|
|
693 |
*/
|
|
|
694 |
public static function handlePhpError($level, $message, $file, $line) {
|
|
|
695 |
|
|
|
696 |
// don't want to print supressed errors
|
|
|
697 |
if (error_reporting() > 0) {
|
|
|
698 |
|
|
|
699 |
if (self::$phpErrorCapture) {
|
|
|
700 |
|
|
|
701 |
self::$capturedPhpErrors[] = array('message' => $message, 'level' => $level, 'line' => $line, 'file' => $file);
|
|
|
702 |
|
|
|
703 |
} else {
|
|
|
704 |
|
|
|
705 |
$message = '[PHP Error] ' . $message;
|
|
|
706 |
$message .= ' [line ' . $line . ' of ' . $file . ']';
|
|
|
707 |
|
|
|
708 |
switch ($level) {
|
|
|
709 |
|
|
|
710 |
case E_STRICT:
|
|
|
711 |
case E_NOTICE:
|
|
|
712 |
case E_USER_NOTICE:
|
|
|
713 |
self::log($message, Project::MSG_VERBOSE);
|
|
|
714 |
break;
|
|
|
715 |
case E_WARNING:
|
|
|
716 |
case E_USER_WARNING:
|
|
|
717 |
self::log($message, Project::MSG_WARN);
|
|
|
718 |
break;
|
|
|
719 |
case E_ERROR:
|
|
|
720 |
case E_USER_ERROR:
|
|
|
721 |
default:
|
|
|
722 |
self::log($message, Project::MSG_ERR);
|
|
|
723 |
|
|
|
724 |
} // switch
|
|
|
725 |
|
|
|
726 |
} // if phpErrorCapture
|
|
|
727 |
|
|
|
728 |
} // if not @
|
|
|
729 |
|
|
|
730 |
}
|
|
|
731 |
|
|
|
732 |
/**
|
|
|
733 |
* Begins capturing PHP errors to a buffer.
|
|
|
734 |
* While errors are being captured, they are not logged.
|
|
|
735 |
*/
|
|
|
736 |
public static function startPhpErrorCapture() {
|
|
|
737 |
self::$phpErrorCapture = true;
|
|
|
738 |
self::$capturedPhpErrors = array();
|
|
|
739 |
}
|
|
|
740 |
|
|
|
741 |
/**
|
|
|
742 |
* Stops capturing PHP errors to a buffer.
|
|
|
743 |
* The errors will once again be logged after calling this method.
|
|
|
744 |
*/
|
|
|
745 |
public static function stopPhpErrorCapture() {
|
|
|
746 |
self::$phpErrorCapture = false;
|
|
|
747 |
}
|
|
|
748 |
|
|
|
749 |
/**
|
|
|
750 |
* Clears the captured errors without affecting the starting/stopping of the capture.
|
|
|
751 |
*/
|
|
|
752 |
public static function clearCapturedPhpErrors() {
|
|
|
753 |
self::$capturedPhpErrors = array();
|
|
|
754 |
}
|
|
|
755 |
|
|
|
756 |
/**
|
|
|
757 |
* Gets any PHP errors that were captured to buffer.
|
|
|
758 |
* @return array array('message' => message, 'line' => line number, 'file' => file name, 'level' => error level)
|
|
|
759 |
*/
|
|
|
760 |
public static function getCapturedPhpErrors() {
|
|
|
761 |
return self::$capturedPhpErrors;
|
|
|
762 |
}
|
|
|
763 |
|
|
|
764 |
/** Prints the usage of how to use this class */
|
|
|
765 |
public static function printUsage() {
|
|
|
766 |
|
|
|
767 |
$msg = "";
|
|
|
768 |
$msg .= "phing [options] [target [target2 [target3] ...]]" . PHP_EOL;
|
|
|
769 |
$msg .= "Options: " . PHP_EOL;
|
|
|
770 |
$msg .= " -h -help print this message" . PHP_EOL;
|
|
|
771 |
$msg .= " -l -list list available targets in this project" . PHP_EOL;
|
|
|
772 |
$msg .= " -v -version print the version information and exit" . PHP_EOL;
|
|
|
773 |
$msg .= " -q -quiet be extra quiet" . PHP_EOL;
|
|
|
774 |
$msg .= " -verbose be extra verbose" . PHP_EOL;
|
|
|
775 |
$msg .= " -debug print debugging information" . PHP_EOL;
|
|
|
776 |
$msg .= " -logfile <file> use given file for log" . PHP_EOL;
|
|
|
777 |
$msg .= " -logger <classname> the class which is to perform logging" . PHP_EOL;
|
|
|
778 |
$msg .= " -f -buildfile <file> use given buildfile" . PHP_EOL;
|
|
|
779 |
$msg .= " -D<property>=<value> use value for given property" . PHP_EOL;
|
|
|
780 |
$msg .= " -find <file> search for buildfile towards the root of the" . PHP_EOL;
|
|
|
781 |
$msg .= " filesystem and use it" . PHP_EOL;
|
|
|
782 |
$msg .= " -inputhandler <file> the class to use to handle user input" . PHP_EOL;
|
|
|
783 |
//$msg .= " -recursive <file> search for buildfile downwards and use it" . PHP_EOL;
|
|
|
784 |
$msg .= PHP_EOL;
|
|
|
785 |
$msg .= "Report bugs to <dev@phing.tigris.org>".PHP_EOL;
|
|
|
786 |
self::$err->write($msg);
|
|
|
787 |
}
|
|
|
788 |
|
|
|
789 |
/**
|
|
|
790 |
* Prints the current Phing version.
|
|
|
791 |
*/
|
|
|
792 |
public static function printVersion() {
|
|
|
793 |
self::$out->write(self::getPhingVersion().PHP_EOL);
|
|
|
794 |
}
|
|
|
795 |
|
|
|
796 |
/**
|
|
|
797 |
* Gets the current Phing version based on VERSION.TXT file.
|
|
|
798 |
* @return string
|
|
|
799 |
* @throws BuildException - if unable to find version file.
|
|
|
800 |
*/
|
|
|
801 |
public static function getPhingVersion() {
|
|
|
802 |
$versionPath = self::getResourcePath("phing/etc/VERSION.TXT");
|
|
|
803 |
if ($versionPath === null) {
|
|
|
804 |
$versionPath = self::getResourcePath("etc/VERSION.TXT");
|
|
|
805 |
}
|
|
|
806 |
if ($versionPath === null) {
|
|
|
807 |
throw new ConfigurationException("No VERSION.TXT file found; try setting phing.home environment variable.");
|
|
|
808 |
}
|
|
|
809 |
try { // try to read file
|
|
|
810 |
$buffer = null;
|
|
|
811 |
$file = new PhingFile($versionPath);
|
|
|
812 |
$reader = new FileReader($file);
|
|
|
813 |
$reader->readInto($buffer);
|
|
|
814 |
$buffer = trim($buffer);
|
|
|
815 |
//$buffer = "PHING version 1.0, Released 2002-??-??";
|
|
|
816 |
$phingVersion = $buffer;
|
|
|
817 |
} catch (IOException $iox) {
|
|
|
818 |
throw new ConfigurationException("Can't read version information file");
|
|
|
819 |
}
|
|
|
820 |
return $phingVersion;
|
|
|
821 |
}
|
|
|
822 |
|
|
|
823 |
/**
|
|
|
824 |
* Print the project description, if any
|
|
|
825 |
*/
|
|
|
826 |
public static function printDescription(Project $project) {
|
|
|
827 |
if ($project->getDescription() !== null) {
|
|
|
828 |
self::$out->write($project->getDescription() . PHP_EOL);
|
|
|
829 |
}
|
|
|
830 |
}
|
|
|
831 |
|
|
|
832 |
/** Print out a list of all targets in the current buildfile */
|
|
|
833 |
function printTargets($project) {
|
|
|
834 |
// find the target with the longest name
|
|
|
835 |
$maxLength = 0;
|
|
|
836 |
$targets = $project->getTargets();
|
|
|
837 |
$targetNames = array_keys($targets);
|
|
|
838 |
$targetName = null;
|
|
|
839 |
$targetDescription = null;
|
|
|
840 |
$currentTarget = null;
|
|
|
841 |
|
|
|
842 |
// split the targets in top-level and sub-targets depending
|
|
|
843 |
// on the presence of a description
|
|
|
844 |
|
|
|
845 |
$subNames = array();
|
|
|
846 |
$topNameDescMap = array();
|
|
|
847 |
|
|
|
848 |
foreach($targets as $currentTarget) {
|
|
|
849 |
$targetName = $currentTarget->getName();
|
|
|
850 |
$targetDescription = $currentTarget->getDescription();
|
|
|
851 |
|
|
|
852 |
// subtargets are targets w/o descriptions
|
|
|
853 |
if ($targetDescription === null) {
|
|
|
854 |
$subNames[] = $targetName;
|
|
|
855 |
} else {
|
|
|
856 |
// topNames and topDescriptions are handled later
|
|
|
857 |
// here we store in hash map (for sorting purposes)
|
|
|
858 |
$topNameDescMap[$targetName] = $targetDescription;
|
|
|
859 |
if (strlen($targetName) > $maxLength) {
|
|
|
860 |
$maxLength = strlen($targetName);
|
|
|
861 |
}
|
|
|
862 |
}
|
|
|
863 |
}
|
|
|
864 |
|
|
|
865 |
// Sort the arrays
|
|
|
866 |
sort($subNames); // sort array values, resetting keys (which are numeric)
|
|
|
867 |
ksort($topNameDescMap); // sort the keys (targetName) keeping key=>val associations
|
|
|
868 |
|
|
|
869 |
$topNames = array_keys($topNameDescMap);
|
|
|
870 |
$topDescriptions = array_values($topNameDescMap);
|
|
|
871 |
|
|
|
872 |
$defaultTarget = $project->getDefaultTarget();
|
|
|
873 |
|
|
|
874 |
if ($defaultTarget !== null && $defaultTarget !== "") {
|
|
|
875 |
$defaultName = array();
|
|
|
876 |
$defaultDesc = array();
|
|
|
877 |
$defaultName[] = $defaultTarget;
|
|
|
878 |
|
|
|
879 |
$indexOfDefDesc = array_search($defaultTarget, $topNames, true);
|
|
|
880 |
if ($indexOfDefDesc !== false && $indexOfDefDesc >= 0) {
|
|
|
881 |
$defaultDesc = array();
|
|
|
882 |
$defaultDesc[] = $topDescriptions[$indexOfDefDesc];
|
|
|
883 |
}
|
|
|
884 |
|
|
|
885 |
$this->_printTargets($defaultName, $defaultDesc, "Default target:", $maxLength);
|
|
|
886 |
|
|
|
887 |
}
|
|
|
888 |
$this->_printTargets($topNames, $topDescriptions, "Main targets:", $maxLength);
|
|
|
889 |
$this->_printTargets($subNames, null, "Subtargets:", 0);
|
|
|
890 |
}
|
|
|
891 |
|
|
|
892 |
/**
|
|
|
893 |
* Writes a formatted list of target names with an optional description.
|
|
|
894 |
*
|
|
|
895 |
* @param array $names The names to be printed.
|
|
|
896 |
* Must not be <code>null</code>.
|
|
|
897 |
* @param array $descriptions The associated target descriptions.
|
|
|
898 |
* May be <code>null</code>, in which case
|
|
|
899 |
* no descriptions are displayed.
|
|
|
900 |
* If non-<code>null</code>, this should have
|
|
|
901 |
* as many elements as <code>names</code>.
|
|
|
902 |
* @param string $heading The heading to display.
|
|
|
903 |
* Should not be <code>null</code>.
|
|
|
904 |
* @param int $maxlen The maximum length of the names of the targets.
|
|
|
905 |
* If descriptions are given, they are padded to this
|
|
|
906 |
* position so they line up (so long as the names really
|
|
|
907 |
* <i>are</i> shorter than this).
|
|
|
908 |
*/
|
|
|
909 |
private function _printTargets($names, $descriptions, $heading, $maxlen) {
|
|
|
910 |
|
|
|
911 |
$spaces = ' ';
|
|
|
912 |
while (strlen($spaces) < $maxlen) {
|
|
|
913 |
$spaces .= $spaces;
|
|
|
914 |
}
|
|
|
915 |
$msg = "";
|
|
|
916 |
$msg .= $heading . PHP_EOL;
|
|
|
917 |
$msg .= str_repeat("-",79) . PHP_EOL;
|
|
|
918 |
|
|
|
919 |
$total = count($names);
|
|
|
920 |
for($i=0; $i < $total; $i++) {
|
|
|
921 |
$msg .= " ";
|
|
|
922 |
$msg .= $names[$i];
|
|
|
923 |
if (!empty($descriptions)) {
|
|
|
924 |
$msg .= substr($spaces, 0, $maxlen - strlen($names[$i]) + 2);
|
|
|
925 |
$msg .= $descriptions[$i];
|
|
|
926 |
}
|
|
|
927 |
$msg .= PHP_EOL;
|
|
|
928 |
}
|
|
|
929 |
if ($total > 0) {
|
|
|
930 |
self::$out->write($msg . PHP_EOL);
|
|
|
931 |
}
|
|
|
932 |
}
|
|
|
933 |
|
|
|
934 |
/**
|
|
|
935 |
* Import a dot-path notation class path.
|
|
|
936 |
* @param string $dotPath
|
|
|
937 |
* @param mixed $classpath String or object supporting __toString()
|
|
|
938 |
* @return string The unqualified classname (which can be instantiated).
|
|
|
939 |
* @throws BuildException - if cannot find the specified file
|
|
|
940 |
*/
|
|
|
941 |
public static function import($dotPath, $classpath = null) {
|
|
|
942 |
|
|
|
943 |
// first check to see that the class specified hasn't already been included.
|
|
|
944 |
// (this also handles case where this method is called w/ a classname rather than dotpath)
|
|
|
945 |
$classname = StringHelper::unqualify($dotPath);
|
|
|
946 |
if (class_exists($classname, false)) {
|
|
|
947 |
return $classname;
|
|
|
948 |
}
|
|
|
949 |
|
|
|
950 |
$dotClassname = basename($dotPath);
|
|
|
951 |
$dotClassnamePos = strlen($dotPath) - strlen($dotClassname);
|
|
|
952 |
|
|
|
953 |
// 1- temporarily replace escaped '.' with another illegal char (#)
|
|
|
954 |
$tmp = str_replace('\.', '##', $dotClassname);
|
|
|
955 |
// 2- swap out the remaining '.' with DIR_SEP
|
|
|
956 |
$tmp = strtr($tmp, '.', DIRECTORY_SEPARATOR);
|
|
|
957 |
// 3- swap back the escaped '.'
|
|
|
958 |
$tmp = str_replace('##', '.', $tmp);
|
|
|
959 |
|
|
|
960 |
$classFile = $tmp . ".php";
|
|
|
961 |
|
|
|
962 |
$path = substr_replace($dotPath, $classFile, $dotClassnamePos);
|
|
|
963 |
|
|
|
964 |
Phing::__import($path, $classpath);
|
|
|
965 |
|
|
|
966 |
return $classname;
|
|
|
967 |
}
|
|
|
968 |
|
|
|
969 |
/**
|
|
|
970 |
* Import a PHP file
|
|
|
971 |
* @param string $path Path to the PHP file
|
|
|
972 |
* @param mixed $classpath String or object supporting __toString()
|
|
|
973 |
* @throws BuildException - if cannot find the specified file
|
|
|
974 |
*/
|
|
|
975 |
public static function __import($path, $classpath = null) {
|
|
|
976 |
|
|
|
977 |
if ($classpath) {
|
|
|
978 |
|
|
|
979 |
// Apparently casting to (string) no longer invokes __toString() automatically.
|
|
|
980 |
if (is_object($classpath)) {
|
|
|
981 |
$classpath = $classpath->__toString();
|
|
|
982 |
}
|
|
|
983 |
|
|
|
984 |
// classpaths are currently additive, but we also don't want to just
|
|
|
985 |
// indiscriminantly prepand/append stuff to the include_path. This means
|
|
|
986 |
// we need to parse current incldue_path, and prepend any
|
|
|
987 |
// specified classpath locations that are not already in the include_path.
|
|
|
988 |
//
|
|
|
989 |
// NOTE: the reason why we do it this way instead of just changing include_path
|
|
|
990 |
// and then changing it back, is that in many cases applications (e.g. Propel) will
|
|
|
991 |
// include/require class files from within method calls. This means that not all
|
|
|
992 |
// necessary files will be included in this import() call, and hence we can't
|
|
|
993 |
// change the include_path back without breaking those apps. While this method could
|
|
|
994 |
// be more expensive than switching & switching back (not sure, but maybe), it makes it
|
|
|
995 |
// possible to write far less expensive run-time applications (e.g. using Propel), which is
|
|
|
996 |
// really where speed matters more.
|
|
|
997 |
|
|
|
998 |
$curr_parts = explode(PATH_SEPARATOR, get_include_path());
|
|
|
999 |
$add_parts = explode(PATH_SEPARATOR, $classpath);
|
|
|
1000 |
$new_parts = array_diff($add_parts, $curr_parts);
|
|
|
1001 |
if ($new_parts) {
|
|
|
1002 |
set_include_path(implode(PATH_SEPARATOR, array_merge($new_parts, $curr_parts)));
|
|
|
1003 |
}
|
|
|
1004 |
}
|
|
|
1005 |
|
|
|
1006 |
$ret = include_once($path);
|
|
|
1007 |
|
|
|
1008 |
if ($ret === false) {
|
|
|
1009 |
$msg = "Error importing $path";
|
|
|
1010 |
if (self::getMsgOutputLevel() >= Project::MSG_DEBUG) {
|
|
|
1011 |
$x = new Exception("for-path-trace-only");
|
|
|
1012 |
$msg .= $x->getTraceAsString();
|
|
|
1013 |
}
|
|
|
1014 |
throw new ConfigurationException($msg);
|
|
|
1015 |
}
|
|
|
1016 |
}
|
|
|
1017 |
|
|
|
1018 |
/**
|
|
|
1019 |
* Looks on include path for specified file.
|
|
|
1020 |
* @return string File found (null if no file found).
|
|
|
1021 |
*/
|
|
|
1022 |
public static function getResourcePath($path) {
|
|
|
1023 |
|
|
|
1024 |
if (self::$importPaths === null) {
|
|
|
1025 |
$paths = get_include_path();
|
|
|
1026 |
self::$importPaths = explode(PATH_SEPARATOR, ini_get("include_path"));
|
|
|
1027 |
}
|
|
|
1028 |
|
|
|
1029 |
$path = str_replace('\\', DIRECTORY_SEPARATOR, $path);
|
|
|
1030 |
$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
|
|
|
1031 |
|
|
|
1032 |
foreach (self::$importPaths as $prefix) {
|
|
|
1033 |
$testPath = $prefix . DIRECTORY_SEPARATOR . $path;
|
|
|
1034 |
if (file_exists($testPath)) {
|
|
|
1035 |
return $testPath;
|
|
|
1036 |
}
|
|
|
1037 |
}
|
|
|
1038 |
|
|
|
1039 |
// Check for the property phing.home
|
|
|
1040 |
$homeDir = self::getProperty('phing.home');
|
|
|
1041 |
if ($homeDir) {
|
|
|
1042 |
$testPath = $homeDir . DIRECTORY_SEPARATOR . $path;
|
|
|
1043 |
if (file_exists($testPath)) {
|
|
|
1044 |
return $testPath;
|
|
|
1045 |
}
|
|
|
1046 |
}
|
|
|
1047 |
|
|
|
1048 |
// If we are using this via PEAR then check for the file in the data dir
|
|
|
1049 |
// This is a bit of a hack, but works better than previous solution of assuming
|
|
|
1050 |
// data_dir is on the include_path.
|
|
|
1051 |
$dataDir = '@DATA-DIR@';
|
|
|
1052 |
if ($dataDir{0} != '@') { // if we're using PEAR then the @ DATA-DIR @ token will have been substituted.
|
|
|
1053 |
$testPath = $dataDir . DIRECTORY_SEPARATOR . $path;
|
|
|
1054 |
if (file_exists($testPath)) {
|
|
|
1055 |
return $testPath;
|
|
|
1056 |
}
|
|
|
1057 |
} else {
|
|
|
1058 |
// We're not using PEAR, so do one additional check based on path of
|
|
|
1059 |
// current file (Phing.php)
|
|
|
1060 |
$maybeHomeDir = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..');
|
|
|
1061 |
$testPath = $maybeHomeDir . DIRECTORY_SEPARATOR . $path;
|
|
|
1062 |
if (file_exists($testPath)) {
|
|
|
1063 |
return $testPath;
|
|
|
1064 |
}
|
|
|
1065 |
}
|
|
|
1066 |
|
|
|
1067 |
return null;
|
|
|
1068 |
}
|
|
|
1069 |
|
|
|
1070 |
// -------------------------------------------------------------------------------------------
|
|
|
1071 |
// System-wide methods (moved from System class, which had namespace conflicts w/ PEAR System)
|
|
|
1072 |
// -------------------------------------------------------------------------------------------
|
|
|
1073 |
|
|
|
1074 |
/**
|
|
|
1075 |
* Set System constants which can be retrieved by calling Phing::getProperty($propName).
|
|
|
1076 |
* @return void
|
|
|
1077 |
*/
|
|
|
1078 |
private static function setSystemConstants() {
|
|
|
1079 |
|
|
|
1080 |
/*
|
|
|
1081 |
* PHP_OS returns on
|
|
|
1082 |
* WindowsNT4.0sp6 => WINNT
|
|
|
1083 |
* Windows2000 => WINNT
|
|
|
1084 |
* Windows ME => WIN32
|
|
|
1085 |
* Windows 98SE => WIN32
|
|
|
1086 |
* FreeBSD 4.5p7 => FreeBSD
|
|
|
1087 |
* Redhat Linux => Linux
|
|
|
1088 |
* Mac OS X => Darwin
|
|
|
1089 |
*/
|
|
|
1090 |
self::setProperty('host.os', PHP_OS);
|
|
|
1091 |
|
|
|
1092 |
// this is used by some tasks too
|
|
|
1093 |
self::setProperty('os.name', PHP_OS);
|
|
|
1094 |
|
|
|
1095 |
// it's still possible this won't be defined,
|
|
|
1096 |
// e.g. if Phing is being included in another app w/o
|
|
|
1097 |
// using the phing.php script.
|
|
|
1098 |
if (!defined('PHP_CLASSPATH')) {
|
|
|
1099 |
define('PHP_CLASSPATH', get_include_path());
|
|
|
1100 |
}
|
|
|
1101 |
|
|
|
1102 |
self::setProperty('php.classpath', PHP_CLASSPATH);
|
|
|
1103 |
|
|
|
1104 |
// try to determine the host filesystem and set system property
|
|
|
1105 |
// used by Fileself::getFileSystem to instantiate the correct
|
|
|
1106 |
// abstraction layer
|
|
|
1107 |
|
|
|
1108 |
switch (strtoupper(PHP_OS)) {
|
|
|
1109 |
case 'WINNT':
|
|
|
1110 |
self::setProperty('host.fstype', 'WINNT');
|
|
|
1111 |
self::setProperty('php.interpreter', getenv('PHP_COMMAND'));
|
|
|
1112 |
break;
|
|
|
1113 |
case 'WIN32':
|
|
|
1114 |
self::setProperty('host.fstype', 'WIN32');
|
|
|
1115 |
break;
|
|
|
1116 |
default:
|
|
|
1117 |
self::setProperty('host.fstype', 'UNIX');
|
|
|
1118 |
break;
|
|
|
1119 |
}
|
|
|
1120 |
|
|
|
1121 |
self::setProperty('line.separator', PHP_EOL);
|
|
|
1122 |
self::setProperty('php.version', PHP_VERSION);
|
|
|
1123 |
self::setProperty('user.home', getenv('HOME'));
|
|
|
1124 |
self::setProperty('application.startdir', getcwd());
|
|
|
1125 |
self::setProperty('phing.startTime', gmdate('D, d M Y H:i:s', time()) . ' GMT');
|
|
|
1126 |
|
|
|
1127 |
// try to detect machine dependent information
|
|
|
1128 |
$sysInfo = array();
|
|
|
1129 |
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && function_exists("posix_uname")) {
|
|
|
1130 |
$sysInfo = posix_uname();
|
|
|
1131 |
} else {
|
|
|
1132 |
$sysInfo['nodename'] = php_uname('n');
|
|
|
1133 |
$sysInfo['machine']= php_uname('m') ;
|
|
|
1134 |
//this is a not so ideal substition, but maybe better than nothing
|
|
|
1135 |
$sysInfo['domain'] = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : "unknown";
|
|
|
1136 |
$sysInfo['release'] = php_uname('r');
|
|
|
1137 |
$sysInfo['version'] = php_uname('v');
|
|
|
1138 |
}
|
|
|
1139 |
|
|
|
1140 |
|
|
|
1141 |
self::setProperty("host.name", isset($sysInfo['nodename']) ? $sysInfo['nodename'] : "unknown");
|
|
|
1142 |
self::setProperty("host.arch", isset($sysInfo['machine']) ? $sysInfo['machine'] : "unknown");
|
|
|
1143 |
self::setProperty("host.domain",isset($sysInfo['domain']) ? $sysInfo['domain'] : "unknown");
|
|
|
1144 |
self::setProperty("host.os.release", isset($sysInfo['release']) ? $sysInfo['release'] : "unknown");
|
|
|
1145 |
self::setProperty("host.os.version", isset($sysInfo['version']) ? $sysInfo['version'] : "unknown");
|
|
|
1146 |
unset($sysInfo);
|
|
|
1147 |
}
|
|
|
1148 |
|
|
|
1149 |
/**
|
|
|
1150 |
* This gets a property that was set via command line or otherwise passed into Phing.
|
|
|
1151 |
* "Defined" in this case means "externally defined". The reason this method exists is to
|
|
|
1152 |
* provide a public means of accessing commandline properties for (e.g.) logger or listener
|
|
|
1153 |
* scripts. E.g. to specify which logfile to use, PearLogger needs to be able to access
|
|
|
1154 |
* the pear.log.name property.
|
|
|
1155 |
*
|
|
|
1156 |
* @param string $name
|
|
|
1157 |
* @return string value of found property (or null, if none found).
|
|
|
1158 |
*/
|
|
|
1159 |
public static function getDefinedProperty($name) {
|
|
|
1160 |
return self::$definedProps->getProperty($name);
|
|
|
1161 |
}
|
|
|
1162 |
|
|
|
1163 |
/**
|
|
|
1164 |
* This sets a property that was set via command line or otherwise passed into Phing.
|
|
|
1165 |
*
|
|
|
1166 |
* @param string $name
|
|
|
1167 |
* @return string value of found property (or null, if none found).
|
|
|
1168 |
*/
|
|
|
1169 |
public static function setDefinedProperty($name, $value) {
|
|
|
1170 |
return self::$definedProps->setProperty($name, $value);
|
|
|
1171 |
}
|
|
|
1172 |
|
|
|
1173 |
/**
|
|
|
1174 |
* Returns property value for a System property.
|
|
|
1175 |
* System properties are "global" properties like application.startdir,
|
|
|
1176 |
* and user.dir. Many of these correspond to similar properties in Java
|
|
|
1177 |
* or Ant.
|
|
|
1178 |
*
|
|
|
1179 |
* @param string $paramName
|
|
|
1180 |
* @return string Value of found property (or null, if none found).
|
|
|
1181 |
*/
|
|
|
1182 |
public static function getProperty($propName) {
|
|
|
1183 |
|
|
|
1184 |
// some properties are detemined on each access
|
|
|
1185 |
// some are cached, see below
|
|
|
1186 |
|
|
|
1187 |
// default is the cached value:
|
|
|
1188 |
$val = isset(self::$properties[$propName]) ? self::$properties[$propName] : null;
|
|
|
1189 |
|
|
|
1190 |
// special exceptions
|
|
|
1191 |
switch($propName) {
|
|
|
1192 |
case 'user.dir':
|
|
|
1193 |
$val = getcwd();
|
|
|
1194 |
break;
|
|
|
1195 |
}
|
|
|
1196 |
|
|
|
1197 |
return $val;
|
|
|
1198 |
}
|
|
|
1199 |
|
|
|
1200 |
/** Retuns reference to all properties*/
|
|
|
1201 |
public static function &getProperties() {
|
|
|
1202 |
return self::$properties;
|
|
|
1203 |
}
|
|
|
1204 |
|
|
|
1205 |
public static function setProperty($propName, $propValue) {
|
|
|
1206 |
$propName = (string) $propName;
|
|
|
1207 |
$oldValue = self::getProperty($propName);
|
|
|
1208 |
self::$properties[$propName] = $propValue;
|
|
|
1209 |
return $oldValue;
|
|
|
1210 |
}
|
|
|
1211 |
|
|
|
1212 |
public static function currentTimeMillis() {
|
|
|
1213 |
list($usec, $sec) = explode(" ",microtime());
|
|
|
1214 |
return ((float)$usec + (float)$sec);
|
|
|
1215 |
}
|
|
|
1216 |
|
|
|
1217 |
/**
|
|
|
1218 |
* Sets the include path to PHP_CLASSPATH constant (if this has been defined).
|
|
|
1219 |
* @return void
|
|
|
1220 |
* @throws ConfigurationException - if the include_path could not be set (for some bizarre reason)
|
|
|
1221 |
*/
|
|
|
1222 |
private static function setIncludePaths() {
|
|
|
1223 |
if (defined('PHP_CLASSPATH')) {
|
|
|
1224 |
$result = set_include_path(PHP_CLASSPATH);
|
|
|
1225 |
if ($result === false) {
|
|
|
1226 |
throw new ConfigurationException("Could not set PHP include_path.");
|
|
|
1227 |
}
|
|
|
1228 |
self::$origIniSettings['include_path'] = $result; // save original value for setting back later
|
|
|
1229 |
}
|
|
|
1230 |
}
|
|
|
1231 |
|
|
|
1232 |
/**
|
|
|
1233 |
* Sets PHP INI values that Phing needs.
|
|
|
1234 |
* @return void
|
|
|
1235 |
*/
|
|
|
1236 |
private static function setIni() {
|
|
|
1237 |
|
|
|
1238 |
self::$origIniSettings['error_reporting'] = error_reporting(E_ALL);
|
|
|
1239 |
|
|
|
1240 |
// We won't bother storing original max_execution_time, since 1) the value in
|
|
|
1241 |
// php.ini may be wrong (and there's no way to get the current value) and
|
|
|
1242 |
// 2) it would mean something very strange to set it to a value less than time script
|
|
|
1243 |
// has already been running, which would be the likely change.
|
|
|
1244 |
|
|
|
1245 |
set_time_limit(0);
|
|
|
1246 |
|
|
|
1247 |
self::$origIniSettings['magic_quotes_gpc'] = ini_set('magic_quotes_gpc', 'off');
|
|
|
1248 |
self::$origIniSettings['short_open_tag'] = ini_set('short_open_tag', 'off');
|
|
|
1249 |
self::$origIniSettings['default_charset'] = ini_set('default_charset', 'iso-8859-1');
|
|
|
1250 |
self::$origIniSettings['register_globals'] = ini_set('register_globals', 'off');
|
|
|
1251 |
self::$origIniSettings['allow_call_time_pass_reference'] = ini_set('allow_call_time_pass_reference', 'on');
|
|
|
1252 |
self::$origIniSettings['track_errors'] = ini_set('track_errors', 1);
|
|
|
1253 |
|
|
|
1254 |
// should return memory limit in MB
|
|
|
1255 |
$mem_limit = (int) ini_get('memory_limit');
|
|
|
1256 |
if ($mem_limit < 32) {
|
|
|
1257 |
// We do *not* need to save the original value here, since we don't plan to restore
|
|
|
1258 |
// this after shutdown (we don't trust the effectiveness of PHP's garbage collection).
|
|
|
1259 |
ini_set('memory_limit', '32M'); // nore: this may need to be higher for many projects
|
|
|
1260 |
}
|
|
|
1261 |
}
|
|
|
1262 |
|
|
|
1263 |
/**
|
|
|
1264 |
* Restores [most] PHP INI values to their pre-Phing state.
|
|
|
1265 |
*
|
|
|
1266 |
* Currently the following settings are not restored:
|
|
|
1267 |
* - max_execution_time (because getting current time limit is not possible)
|
|
|
1268 |
* - memory_limit (which may have been increased by Phing)
|
|
|
1269 |
*
|
|
|
1270 |
* @return void
|
|
|
1271 |
*/
|
|
|
1272 |
private static function restoreIni()
|
|
|
1273 |
{
|
|
|
1274 |
foreach(self::$origIniSettings as $settingName => $settingValue) {
|
|
|
1275 |
switch($settingName) {
|
|
|
1276 |
case 'error_reporting':
|
|
|
1277 |
error_reporting($settingValue);
|
|
|
1278 |
break;
|
|
|
1279 |
default:
|
|
|
1280 |
ini_set($settingName, $settingValue);
|
|
|
1281 |
}
|
|
|
1282 |
}
|
|
|
1283 |
}
|
|
|
1284 |
|
|
|
1285 |
/**
|
|
|
1286 |
* Returns reference to Timer object.
|
|
|
1287 |
* @return Timer
|
|
|
1288 |
*/
|
|
|
1289 |
public static function getTimer() {
|
|
|
1290 |
if (self::$timer === null) {
|
|
|
1291 |
include_once 'phing/system/util/Timer.php';
|
|
|
1292 |
self::$timer= new Timer();
|
|
|
1293 |
}
|
|
|
1294 |
return self::$timer;
|
|
|
1295 |
}
|
|
|
1296 |
|
|
|
1297 |
/**
|
|
|
1298 |
* Start up Phing.
|
|
|
1299 |
* Sets up the Phing environment but does not initiate the build process.
|
|
|
1300 |
* @return void
|
|
|
1301 |
* @throws Exception - If the Phing environment cannot be initialized.
|
|
|
1302 |
*/
|
|
|
1303 |
public static function startup() {
|
|
|
1304 |
|
|
|
1305 |
// setup STDOUT and STDERR defaults
|
|
|
1306 |
self::initializeOutputStreams();
|
|
|
1307 |
|
|
|
1308 |
// some init stuff
|
|
|
1309 |
self::getTimer()->start();
|
|
|
1310 |
|
|
|
1311 |
self::setSystemConstants();
|
|
|
1312 |
self::setIncludePaths();
|
|
|
1313 |
self::setIni();
|
|
|
1314 |
}
|
|
|
1315 |
|
|
|
1316 |
/**
|
|
|
1317 |
* Halts the system.
|
|
|
1318 |
* @deprecated This method is deprecated and is no longer called by Phing internally. Any
|
|
|
1319 |
* normal shutdown routines are handled by the shutdown() method.
|
|
|
1320 |
* @see shutdown()
|
|
|
1321 |
*/
|
|
|
1322 |
public static function halt() {
|
|
|
1323 |
self::shutdown();
|
|
|
1324 |
}
|
|
|
1325 |
|
|
|
1326 |
/**
|
|
|
1327 |
* Performs any shutdown routines, such as stopping timers.
|
|
|
1328 |
* @return void
|
|
|
1329 |
*/
|
|
|
1330 |
public static function shutdown() {
|
|
|
1331 |
self::restoreIni();
|
|
|
1332 |
self::getTimer()->stop();
|
|
|
1333 |
}
|
|
|
1334 |
|
|
|
1335 |
}
|