| 1 |
lars |
1 |
<?php
|
|
|
2 |
/*
|
|
|
3 |
* $Id: Project.php 345 2008-01-30 19:46:32Z 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 |
include_once 'phing/system/io/PhingFile.php';
|
|
|
23 |
include_once 'phing/util/FileUtils.php';
|
|
|
24 |
include_once 'phing/TaskAdapter.php';
|
|
|
25 |
include_once 'phing/util/StringHelper.php';
|
|
|
26 |
include_once 'phing/BuildEvent.php';
|
|
|
27 |
include_once 'phing/input/DefaultInputHandler.php';
|
|
|
28 |
|
|
|
29 |
/**
|
|
|
30 |
* The Phing project class. Represents a completely configured Phing project.
|
|
|
31 |
* The class defines the project and all tasks/targets. It also contains
|
|
|
32 |
* methods to start a build as well as some properties and FileSystem
|
|
|
33 |
* abstraction.
|
|
|
34 |
*
|
|
|
35 |
* @author Andreas Aderhold <andi@binarycloud.com>
|
|
|
36 |
* @author Hans Lellelid <hans@xmpl.org>
|
|
|
37 |
* @version $Revision: 1.29 $
|
|
|
38 |
* @package phing
|
|
|
39 |
*/
|
|
|
40 |
class Project {
|
|
|
41 |
|
|
|
42 |
// Logging level constants.
|
|
|
43 |
const MSG_DEBUG = 4;
|
|
|
44 |
const MSG_VERBOSE = 3;
|
|
|
45 |
const MSG_INFO = 2;
|
|
|
46 |
const MSG_WARN = 1;
|
|
|
47 |
const MSG_ERR = 0;
|
|
|
48 |
|
|
|
49 |
/** contains the targets */
|
|
|
50 |
private $targets = array();
|
|
|
51 |
/** global filterset (future use) */
|
|
|
52 |
private $globalFilterSet = array();
|
|
|
53 |
/** all globals filters (future use) */
|
|
|
54 |
private $globalFilters = array();
|
|
|
55 |
|
|
|
56 |
/** Project properties map (usually String to String). */
|
|
|
57 |
private $properties = array();
|
|
|
58 |
|
|
|
59 |
/**
|
|
|
60 |
* Map of "user" properties (as created in the Ant task, for example).
|
|
|
61 |
* Note that these key/value pairs are also always put into the
|
|
|
62 |
* project properties, so only the project properties need to be queried.
|
|
|
63 |
* Mapping is String to String.
|
|
|
64 |
*/
|
|
|
65 |
private $userProperties = array();
|
|
|
66 |
|
|
|
67 |
/**
|
|
|
68 |
* Map of inherited "user" properties - that are those "user"
|
|
|
69 |
* properties that have been created by tasks and not been set
|
|
|
70 |
* from the command line or a GUI tool.
|
|
|
71 |
* Mapping is String to String.
|
|
|
72 |
*/
|
|
|
73 |
private $inheritedProperties = array();
|
|
|
74 |
|
|
|
75 |
/** task definitions for this project*/
|
|
|
76 |
private $taskdefs = array();
|
|
|
77 |
|
|
|
78 |
/** type definitions for this project */
|
|
|
79 |
private $typedefs = array();
|
|
|
80 |
|
|
|
81 |
/** holds ref names and a reference to the referred object*/
|
|
|
82 |
private $references = array();
|
|
|
83 |
|
|
|
84 |
/** The InputHandler being used by this project. */
|
|
|
85 |
private $inputHandler;
|
|
|
86 |
|
|
|
87 |
/* -- properties that come in via xml attributes -- */
|
|
|
88 |
|
|
|
89 |
/** basedir (PhingFile object) */
|
|
|
90 |
private $basedir;
|
|
|
91 |
|
|
|
92 |
/** the default target name */
|
|
|
93 |
private $defaultTarget = 'all';
|
|
|
94 |
|
|
|
95 |
/** project name (required) */
|
|
|
96 |
private $name;
|
|
|
97 |
|
|
|
98 |
/** project description */
|
|
|
99 |
private $description;
|
|
|
100 |
|
|
|
101 |
/** a FileUtils object */
|
|
|
102 |
private $fileUtils;
|
|
|
103 |
|
|
|
104 |
/** Build listeneers */
|
|
|
105 |
private $listeners = array();
|
|
|
106 |
|
|
|
107 |
/**
|
|
|
108 |
* Constructor, sets any default vars.
|
|
|
109 |
*/
|
|
|
110 |
function __construct() {
|
|
|
111 |
$this->fileUtils = new FileUtils();
|
|
|
112 |
$this->inputHandler = new DefaultInputHandler();
|
|
|
113 |
}
|
|
|
114 |
|
|
|
115 |
/**
|
|
|
116 |
* Sets the input handler
|
|
|
117 |
*/
|
|
|
118 |
public function setInputHandler(InputHandler $handler) {
|
|
|
119 |
$this->inputHandler = $handler;
|
|
|
120 |
}
|
|
|
121 |
|
|
|
122 |
/**
|
|
|
123 |
* Retrieves the current input handler.
|
|
|
124 |
*/
|
|
|
125 |
public function getInputHandler() {
|
|
|
126 |
return $this->inputHandler;
|
|
|
127 |
}
|
|
|
128 |
|
|
|
129 |
/** inits the project, called from main app */
|
|
|
130 |
function init() {
|
|
|
131 |
// set builtin properties
|
|
|
132 |
$this->setSystemProperties();
|
|
|
133 |
|
|
|
134 |
// load default tasks
|
|
|
135 |
$taskdefs = Phing::getResourcePath("phing/tasks/defaults.properties");
|
|
|
136 |
|
|
|
137 |
try { // try to load taskdefs
|
|
|
138 |
$props = new Properties();
|
|
|
139 |
$in = new PhingFile((string)$taskdefs);
|
|
|
140 |
|
|
|
141 |
if ($in === null) {
|
|
|
142 |
throw new BuildException("Can't load default task list");
|
|
|
143 |
}
|
|
|
144 |
$props->load($in);
|
|
|
145 |
|
|
|
146 |
$enum = $props->propertyNames();
|
|
|
147 |
foreach($enum as $key) {
|
|
|
148 |
$value = $props->getProperty($key);
|
|
|
149 |
$this->addTaskDefinition($key, $value);
|
|
|
150 |
}
|
|
|
151 |
} catch (IOException $ioe) {
|
|
|
152 |
throw new BuildException("Can't load default task list");
|
|
|
153 |
}
|
|
|
154 |
|
|
|
155 |
// load default tasks
|
|
|
156 |
$typedefs = Phing::getResourcePath("phing/types/defaults.properties");
|
|
|
157 |
|
|
|
158 |
try { // try to load typedefs
|
|
|
159 |
$props = new Properties();
|
|
|
160 |
$in = new PhingFile((string)$typedefs);
|
|
|
161 |
if ($in === null) {
|
|
|
162 |
throw new BuildException("Can't load default datatype list");
|
|
|
163 |
}
|
|
|
164 |
$props->load($in);
|
|
|
165 |
|
|
|
166 |
$enum = $props->propertyNames();
|
|
|
167 |
foreach($enum as $key) {
|
|
|
168 |
$value = $props->getProperty($key);
|
|
|
169 |
$this->addDataTypeDefinition($key, $value);
|
|
|
170 |
}
|
|
|
171 |
} catch(IOException $ioe) {
|
|
|
172 |
throw new BuildException("Can't load default datatype list");
|
|
|
173 |
}
|
|
|
174 |
}
|
|
|
175 |
|
|
|
176 |
/** returns the global filterset (future use) */
|
|
|
177 |
function getGlobalFilterSet() {
|
|
|
178 |
return $this->globalFilterSet;
|
|
|
179 |
}
|
|
|
180 |
|
|
|
181 |
// ---------------------------------------------------------
|
|
|
182 |
// Property methods
|
|
|
183 |
// ---------------------------------------------------------
|
|
|
184 |
|
|
|
185 |
/**
|
|
|
186 |
* Sets a property. Any existing property of the same name
|
|
|
187 |
* is overwritten, unless it is a user property.
|
|
|
188 |
* @param string $name The name of property to set.
|
|
|
189 |
* Must not be <code>null</code>.
|
|
|
190 |
* @param string $value The new value of the property.
|
|
|
191 |
* Must not be <code>null</code>.
|
|
|
192 |
* @return void
|
|
|
193 |
*/
|
|
|
194 |
public function setProperty($name, $value) {
|
|
|
195 |
|
|
|
196 |
// command line properties take precedence
|
|
|
197 |
if (isset($this->userProperties[$name])) {
|
|
|
198 |
$this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE);
|
|
|
199 |
return;
|
|
|
200 |
}
|
|
|
201 |
|
|
|
202 |
if (isset($this->properties[$name])) {
|
|
|
203 |
$this->log("Overriding previous definition of property " . $name, Project::MSG_VERBOSE);
|
|
|
204 |
}
|
|
|
205 |
|
|
|
206 |
$this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
|
|
|
207 |
$this->properties[$name] = $value;
|
|
|
208 |
}
|
|
|
209 |
|
|
|
210 |
/**
|
|
|
211 |
* Sets a property if no value currently exists. If the property
|
|
|
212 |
* exists already, a message is logged and the method returns with
|
|
|
213 |
* no other effect.
|
|
|
214 |
*
|
|
|
215 |
* @param string $name The name of property to set.
|
|
|
216 |
* Must not be <code>null</code>.
|
|
|
217 |
* @param string $value The new value of the property.
|
|
|
218 |
* Must not be <code>null</code>.
|
|
|
219 |
* @since 2.0
|
|
|
220 |
*/
|
|
|
221 |
public function setNewProperty($name, $value) {
|
|
|
222 |
if (isset($this->properties[$name])) {
|
|
|
223 |
$this->log("Override ignored for property " . $name, Project::MSG_DEBUG);
|
|
|
224 |
return;
|
|
|
225 |
}
|
|
|
226 |
$this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
|
|
|
227 |
$this->properties[$name] = $value;
|
|
|
228 |
}
|
|
|
229 |
|
|
|
230 |
/**
|
|
|
231 |
* Sets a user property, which cannot be overwritten by
|
|
|
232 |
* set/unset property calls. Any previous value is overwritten.
|
|
|
233 |
* @param string $name The name of property to set.
|
|
|
234 |
* Must not be <code>null</code>.
|
|
|
235 |
* @param string $value The new value of the property.
|
|
|
236 |
* Must not be <code>null</code>.
|
|
|
237 |
* @see #setProperty()
|
|
|
238 |
*/
|
|
|
239 |
public function setUserProperty($name, $value) {
|
|
|
240 |
$this->log("Setting ro project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
|
|
|
241 |
$this->userProperties[$name] = $value;
|
|
|
242 |
$this->properties[$name] = $value;
|
|
|
243 |
}
|
|
|
244 |
|
|
|
245 |
/**
|
|
|
246 |
* Sets a user property, which cannot be overwritten by set/unset
|
|
|
247 |
* property calls. Any previous value is overwritten. Also marks
|
|
|
248 |
* these properties as properties that have not come from the
|
|
|
249 |
* command line.
|
|
|
250 |
*
|
|
|
251 |
* @param string $name The name of property to set.
|
|
|
252 |
* Must not be <code>null</code>.
|
|
|
253 |
* @param string $value The new value of the property.
|
|
|
254 |
* Must not be <code>null</code>.
|
|
|
255 |
* @see #setProperty()
|
|
|
256 |
*/
|
|
|
257 |
public function setInheritedProperty($name, $value) {
|
|
|
258 |
$this->inheritedProperties[$name] = $value;
|
|
|
259 |
$this->setUserProperty($name, $value);
|
|
|
260 |
}
|
|
|
261 |
|
|
|
262 |
/**
|
|
|
263 |
* Sets a property unless it is already defined as a user property
|
|
|
264 |
* (in which case the method returns silently).
|
|
|
265 |
*
|
|
|
266 |
* @param name The name of the property.
|
|
|
267 |
* Must not be <code>null</code>.
|
|
|
268 |
* @param value The property value. Must not be <code>null</code>.
|
|
|
269 |
*/
|
|
|
270 |
private function setPropertyInternal($name, $value) {
|
|
|
271 |
if (isset($this->userProperties[$name])) {
|
|
|
272 |
$this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE);
|
|
|
273 |
return;
|
|
|
274 |
}
|
|
|
275 |
$this->properties[$name] = $value;
|
|
|
276 |
}
|
|
|
277 |
|
|
|
278 |
/**
|
|
|
279 |
* Returns the value of a property, if it is set.
|
|
|
280 |
*
|
|
|
281 |
* @param string $name The name of the property.
|
|
|
282 |
* May be <code>null</code>, in which case
|
|
|
283 |
* the return value is also <code>null</code>.
|
|
|
284 |
* @return string The property value, or <code>null</code> for no match
|
|
|
285 |
* or if a <code>null</code> name is provided.
|
|
|
286 |
*/
|
|
|
287 |
public function getProperty($name) {
|
|
|
288 |
if (!isset($this->properties[$name])) {
|
|
|
289 |
return null;
|
|
|
290 |
}
|
|
|
291 |
return $this->properties[$name];
|
|
|
292 |
}
|
|
|
293 |
|
|
|
294 |
/**
|
|
|
295 |
* Replaces ${} style constructions in the given value with the
|
|
|
296 |
* string value of the corresponding data types.
|
|
|
297 |
*
|
|
|
298 |
* @param value The string to be scanned for property references.
|
|
|
299 |
* May be <code>null</code>.
|
|
|
300 |
*
|
|
|
301 |
* @return the given string with embedded property names replaced
|
|
|
302 |
* by values, or <code>null</code> if the given string is
|
|
|
303 |
* <code>null</code>.
|
|
|
304 |
*
|
|
|
305 |
* @exception BuildException if the given value has an unclosed
|
|
|
306 |
* property name, e.g. <code>${xxx</code>
|
|
|
307 |
*/
|
|
|
308 |
public function replaceProperties($value) {
|
|
|
309 |
return ProjectConfigurator::replaceProperties($this, $value, $this->properties);
|
|
|
310 |
}
|
|
|
311 |
|
|
|
312 |
/**
|
|
|
313 |
* Returns the value of a user property, if it is set.
|
|
|
314 |
*
|
|
|
315 |
* @param string $name The name of the property.
|
|
|
316 |
* May be <code>null</code>, in which case
|
|
|
317 |
* the return value is also <code>null</code>.
|
|
|
318 |
* @return string The property value, or <code>null</code> for no match
|
|
|
319 |
* or if a <code>null</code> name is provided.
|
|
|
320 |
*/
|
|
|
321 |
public function getUserProperty($name) {
|
|
|
322 |
if (!isset($this->userProperties[$name])) {
|
|
|
323 |
return null;
|
|
|
324 |
}
|
|
|
325 |
return $this->userProperties[$name];
|
|
|
326 |
}
|
|
|
327 |
|
|
|
328 |
/**
|
|
|
329 |
* Returns a copy of the properties table.
|
|
|
330 |
* @return array A hashtable containing all properties
|
|
|
331 |
* (including user properties).
|
|
|
332 |
*/
|
|
|
333 |
public function getProperties() {
|
|
|
334 |
return $this->properties;
|
|
|
335 |
}
|
|
|
336 |
|
|
|
337 |
/**
|
|
|
338 |
* Returns a copy of the user property hashtable
|
|
|
339 |
* @return a hashtable containing just the user properties
|
|
|
340 |
*/
|
|
|
341 |
public function getUserProperties() {
|
|
|
342 |
return $this->userProperties;
|
|
|
343 |
}
|
|
|
344 |
|
|
|
345 |
/**
|
|
|
346 |
* Copies all user properties that have been set on the command
|
|
|
347 |
* line or a GUI tool from this instance to the Project instance
|
|
|
348 |
* given as the argument.
|
|
|
349 |
*
|
|
|
350 |
* <p>To copy all "user" properties, you will also have to call
|
|
|
351 |
* {@link #copyInheritedProperties copyInheritedProperties}.</p>
|
|
|
352 |
*
|
|
|
353 |
* @param Project $other the project to copy the properties to. Must not be null.
|
|
|
354 |
* @return void
|
|
|
355 |
* @since phing 2.0
|
|
|
356 |
*/
|
|
|
357 |
public function copyUserProperties(Project $other) {
|
|
|
358 |
foreach($this->userProperties as $arg => $value) {
|
|
|
359 |
if (isset($this->inheritedProperties[$arg])) {
|
|
|
360 |
continue;
|
|
|
361 |
}
|
|
|
362 |
$other->setUserProperty($arg, $value);
|
|
|
363 |
}
|
|
|
364 |
}
|
|
|
365 |
|
|
|
366 |
/**
|
|
|
367 |
* Copies all user properties that have not been set on the
|
|
|
368 |
* command line or a GUI tool from this instance to the Project
|
|
|
369 |
* instance given as the argument.
|
|
|
370 |
*
|
|
|
371 |
* <p>To copy all "user" properties, you will also have to call
|
|
|
372 |
* {@link #copyUserProperties copyUserProperties}.</p>
|
|
|
373 |
*
|
|
|
374 |
* @param other the project to copy the properties to. Must not be null.
|
|
|
375 |
*
|
|
|
376 |
* @since phing 2.0
|
|
|
377 |
*/
|
|
|
378 |
public function copyInheritedProperties(Project $other) {
|
|
|
379 |
foreach($this->userProperties as $arg => $value) {
|
|
|
380 |
if ($other->getUserProperty($arg) !== null) {
|
|
|
381 |
continue;
|
|
|
382 |
}
|
|
|
383 |
$other->setInheritedProperty($arg, $value);
|
|
|
384 |
}
|
|
|
385 |
}
|
|
|
386 |
|
|
|
387 |
// ---------------------------------------------------------
|
|
|
388 |
// END Properties methods
|
|
|
389 |
// ---------------------------------------------------------
|
|
|
390 |
|
|
|
391 |
|
|
|
392 |
function setDefaultTarget($targetName) {
|
|
|
393 |
$this->defaultTarget = (string) trim($targetName);
|
|
|
394 |
}
|
|
|
395 |
|
|
|
396 |
function getDefaultTarget() {
|
|
|
397 |
return (string) $this->defaultTarget;
|
|
|
398 |
}
|
|
|
399 |
|
|
|
400 |
/**
|
|
|
401 |
* Sets the name of the current project
|
|
|
402 |
*
|
|
|
403 |
* @param string name of project
|
|
|
404 |
* @return void
|
|
|
405 |
* @access public
|
|
|
406 |
* @author Andreas Aderhold, andi@binarycloud.com
|
|
|
407 |
*/
|
|
|
408 |
|
|
|
409 |
function setName($name) {
|
|
|
410 |
$this->name = (string) trim($name);
|
|
|
411 |
$this->setProperty("phing.project.name", $this->name);
|
|
|
412 |
}
|
|
|
413 |
|
|
|
414 |
/**
|
|
|
415 |
* Returns the name of this project
|
|
|
416 |
*
|
|
|
417 |
* @returns string projectname
|
|
|
418 |
* @access public
|
|
|
419 |
* @author Andreas Aderhold, andi@binarycloud.com
|
|
|
420 |
*/
|
|
|
421 |
function getName() {
|
|
|
422 |
return (string) $this->name;
|
|
|
423 |
}
|
|
|
424 |
|
|
|
425 |
/** Set the projects description */
|
|
|
426 |
function setDescription($description) {
|
|
|
427 |
$this->description = (string) trim($description);
|
|
|
428 |
}
|
|
|
429 |
|
|
|
430 |
/** return the description, null otherwise */
|
|
|
431 |
function getDescription() {
|
|
|
432 |
return $this->description;
|
|
|
433 |
}
|
|
|
434 |
|
|
|
435 |
/** Set basedir object from xml*/
|
|
|
436 |
function setBasedir($dir) {
|
|
|
437 |
if ($dir instanceof PhingFile) {
|
|
|
438 |
$dir = $dir->getAbsolutePath();
|
|
|
439 |
}
|
|
|
440 |
|
|
|
441 |
$dir = $this->fileUtils->normalize($dir);
|
|
|
442 |
|
|
|
443 |
$dir = new PhingFile((string) $dir);
|
|
|
444 |
if (!$dir->exists()) {
|
|
|
445 |
throw new BuildException("Basedir ".$dir->getAbsolutePath()." does not exist");
|
|
|
446 |
}
|
|
|
447 |
if (!$dir->isDirectory()) {
|
|
|
448 |
throw new BuildException("Basedir ".$dir->getAbsolutePath()." is not a directory");
|
|
|
449 |
}
|
|
|
450 |
$this->basedir = $dir;
|
|
|
451 |
$this->setPropertyInternal("project.basedir", $this->basedir->getAbsolutePath());
|
|
|
452 |
$this->log("Project base dir set to: " . $this->basedir->getPath(), Project::MSG_VERBOSE);
|
|
|
453 |
|
|
|
454 |
// [HL] added this so that ./ files resolve correctly. This may be a mistake ... or may be in wrong place.
|
|
|
455 |
chdir($dir->getAbsolutePath());
|
|
|
456 |
}
|
|
|
457 |
|
|
|
458 |
/**
|
|
|
459 |
* Returns the basedir of this project
|
|
|
460 |
*
|
|
|
461 |
* @returns PhingFile Basedir PhingFile object
|
|
|
462 |
* @access public
|
|
|
463 |
* @throws BuildException
|
|
|
464 |
* @author Andreas Aderhold, andi@binarycloud.com
|
|
|
465 |
*/
|
|
|
466 |
function getBasedir() {
|
|
|
467 |
if ($this->basedir === null) {
|
|
|
468 |
try { // try to set it
|
|
|
469 |
$this->setBasedir(".");
|
|
|
470 |
} catch (BuildException $exc) {
|
|
|
471 |
throw new BuildException("Can not set default basedir. ".$exc->getMessage());
|
|
|
472 |
}
|
|
|
473 |
}
|
|
|
474 |
return $this->basedir;
|
|
|
475 |
}
|
|
|
476 |
|
|
|
477 |
/**
|
|
|
478 |
* Sets system properties and the environment variables for this project.
|
|
|
479 |
*
|
|
|
480 |
* @return void
|
|
|
481 |
*/
|
|
|
482 |
function setSystemProperties() {
|
|
|
483 |
|
|
|
484 |
// first get system properties
|
|
|
485 |
$systemP = array_merge( self::getProperties(), Phing::getProperties() );
|
|
|
486 |
foreach($systemP as $name => $value) {
|
|
|
487 |
$this->setPropertyInternal($name, $value);
|
|
|
488 |
}
|
|
|
489 |
|
|
|
490 |
// and now the env vars
|
|
|
491 |
foreach($_SERVER as $name => $value) {
|
|
|
492 |
// skip arrays
|
|
|
493 |
if (is_array($value)) {
|
|
|
494 |
continue;
|
|
|
495 |
}
|
|
|
496 |
$this->setPropertyInternal('env.' . $name, $value);
|
|
|
497 |
}
|
|
|
498 |
return true;
|
|
|
499 |
}
|
|
|
500 |
|
|
|
501 |
|
|
|
502 |
/**
|
|
|
503 |
* Adds a task definition.
|
|
|
504 |
* @param string $name Name of tag.
|
|
|
505 |
* @param string $class The class path to use.
|
|
|
506 |
* @param string $classpath The classpat to use.
|
|
|
507 |
*/
|
|
|
508 |
function addTaskDefinition($name, $class, $classpath = null) {
|
|
|
509 |
$name = $name;
|
|
|
510 |
$class = $class;
|
|
|
511 |
if ($class === "") {
|
|
|
512 |
$this->log("Task $name has no class defined.", Project::MSG_ERR);
|
|
|
513 |
} elseif (!isset($this->taskdefs[$name])) {
|
|
|
514 |
Phing::import($class, $classpath);
|
|
|
515 |
$this->taskdefs[$name] = $class;
|
|
|
516 |
$this->log(" +Task definiton: $name ($class)", Project::MSG_DEBUG);
|
|
|
517 |
} else {
|
|
|
518 |
$this->log("Task $name ($class) already registerd, skipping", Project::MSG_VERBOSE);
|
|
|
519 |
}
|
|
|
520 |
}
|
|
|
521 |
|
|
|
522 |
function &getTaskDefinitions() {
|
|
|
523 |
return $this->taskdefs;
|
|
|
524 |
}
|
|
|
525 |
|
|
|
526 |
/**
|
|
|
527 |
* Adds a data type definition.
|
|
|
528 |
* @param string $name Name of tag.
|
|
|
529 |
* @param string $class The class path to use.
|
|
|
530 |
* @param string $classpath The classpat to use.
|
|
|
531 |
*/
|
|
|
532 |
function addDataTypeDefinition($typeName, $typeClass, $classpath = null) {
|
|
|
533 |
if (!isset($this->typedefs[$typeName])) {
|
|
|
534 |
Phing::import($typeClass, $classpath);
|
|
|
535 |
$this->typedefs[$typeName] = $typeClass;
|
|
|
536 |
$this->log(" +User datatype: $typeName ($typeClass)", Project::MSG_DEBUG);
|
|
|
537 |
} else {
|
|
|
538 |
$this->log("Type $name ($class) already registerd, skipping", Project::MSG_VERBOSE);
|
|
|
539 |
}
|
|
|
540 |
}
|
|
|
541 |
|
|
|
542 |
function getDataTypeDefinitions() {
|
|
|
543 |
return $this->typedefs;
|
|
|
544 |
}
|
|
|
545 |
|
|
|
546 |
/** add a new target to the project */
|
|
|
547 |
function addTarget($targetName, &$target) {
|
|
|
548 |
if (isset($this->targets[$targetName])) {
|
|
|
549 |
throw new BuildException("Duplicate target: $targetName");
|
|
|
550 |
}
|
|
|
551 |
$this->addOrReplaceTarget($targetName, $target);
|
|
|
552 |
}
|
|
|
553 |
|
|
|
554 |
function addOrReplaceTarget($targetName, &$target) {
|
|
|
555 |
$this->log(" +Target: $targetName", Project::MSG_DEBUG);
|
|
|
556 |
$target->setProject($this);
|
|
|
557 |
$this->targets[$targetName] = $target;
|
|
|
558 |
}
|
|
|
559 |
|
|
|
560 |
function getTargets() {
|
|
|
561 |
return $this->targets;
|
|
|
562 |
}
|
|
|
563 |
|
|
|
564 |
/**
|
|
|
565 |
* Create a new task instance and return reference to it. This method is
|
|
|
566 |
* sorta factory like. A _local_ instance is created and a reference returned to
|
|
|
567 |
* that instance. Usually PHP destroys local variables when the function call
|
|
|
568 |
* ends. But not if you return a reference to that variable.
|
|
|
569 |
* This is kinda error prone, because if no reference exists to the variable
|
|
|
570 |
* it is destroyed just like leaving the local scope with primitive vars. There's no
|
|
|
571 |
* central place where the instance is stored as in other OOP like languages.
|
|
|
572 |
*
|
|
|
573 |
* [HL] Well, ZE2 is here now, and this is still working. We'll leave this alone
|
|
|
574 |
* unless there's any good reason not to.
|
|
|
575 |
*
|
|
|
576 |
* @param string $taskType Task name
|
|
|
577 |
* @returns Task A task object
|
|
|
578 |
* @throws BuildException
|
|
|
579 |
* Exception
|
|
|
580 |
*/
|
|
|
581 |
function createTask($taskType) {
|
|
|
582 |
try {
|
|
|
583 |
$cls = "";
|
|
|
584 |
$tasklwr = strtolower($taskType);
|
|
|
585 |
foreach ($this->taskdefs as $name => $class) {
|
|
|
586 |
if (strtolower($name) === $tasklwr) {
|
|
|
587 |
$cls = StringHelper::unqualify($class);
|
|
|
588 |
break;
|
|
|
589 |
}
|
|
|
590 |
}
|
|
|
591 |
|
|
|
592 |
if ($cls === "") {
|
|
|
593 |
return null;
|
|
|
594 |
}
|
|
|
595 |
|
|
|
596 |
if (!class_exists($cls)) {
|
|
|
597 |
throw new BuildException("Could not instantiate class $cls, even though a class was specified. (Make sure that the specified class file contains a class with the correct name.)");
|
|
|
598 |
}
|
|
|
599 |
|
|
|
600 |
$o = new $cls();
|
|
|
601 |
|
|
|
602 |
if ($o instanceof Task) {
|
|
|
603 |
$task = $o;
|
|
|
604 |
} else {
|
|
|
605 |
$this->log (" (Using TaskAdapter for: $taskType)", Project::MSG_DEBUG);
|
|
|
606 |
// not a real task, try adapter
|
|
|
607 |
$taskA = new TaskAdapter();
|
|
|
608 |
$taskA->setProxy($o);
|
|
|
609 |
$task = $taskA;
|
|
|
610 |
}
|
|
|
611 |
$task->setProject($this);
|
|
|
612 |
$task->setTaskType($taskType);
|
|
|
613 |
// set default value, can be changed by the user
|
|
|
614 |
$task->setTaskName($taskType);
|
|
|
615 |
$this->log (" +Task: " . $taskType, Project::MSG_DEBUG);
|
|
|
616 |
} catch (Exception $t) {
|
|
|
617 |
throw new BuildException("Could not create task of type: " . $taskType, $t);
|
|
|
618 |
}
|
|
|
619 |
// everything fine return reference
|
|
|
620 |
return $task;
|
|
|
621 |
}
|
|
|
622 |
|
|
|
623 |
/**
|
|
|
624 |
* Create a task instance and return reference to it
|
|
|
625 |
* See createTask() for explanation how this works
|
|
|
626 |
*
|
|
|
627 |
* @param string Type name
|
|
|
628 |
* @returns object A datatype object
|
|
|
629 |
* @throws BuildException
|
|
|
630 |
* Exception
|
|
|
631 |
*/
|
|
|
632 |
function createDataType($typeName) {
|
|
|
633 |
try {
|
|
|
634 |
$cls = "";
|
|
|
635 |
$typelwr = strtolower($typeName);
|
|
|
636 |
foreach ($this->typedefs as $name => $class) {
|
|
|
637 |
if (strtolower($name) === $typelwr) {
|
|
|
638 |
$cls = StringHelper::unqualify($class);
|
|
|
639 |
break;
|
|
|
640 |
}
|
|
|
641 |
}
|
|
|
642 |
|
|
|
643 |
if ($cls === "") {
|
|
|
644 |
return null;
|
|
|
645 |
}
|
|
|
646 |
|
|
|
647 |
if (!class_exists($cls)) {
|
|
|
648 |
throw new BuildException("Could not instantiate class $cls, even though a class was specified. (Make sure that the specified class file contains a class with the correct name.)");
|
|
|
649 |
}
|
|
|
650 |
|
|
|
651 |
$type = new $cls();
|
|
|
652 |
$this->log(" +Type: $typeName", Project::MSG_DEBUG);
|
|
|
653 |
if (!($type instanceof DataType)) {
|
|
|
654 |
throw new Exception("$class is not an instance of phing.types.DataType");
|
|
|
655 |
}
|
|
|
656 |
if ($type instanceof ProjectComponent) {
|
|
|
657 |
$type->setProject($this);
|
|
|
658 |
}
|
|
|
659 |
} catch (Exception $t) {
|
|
|
660 |
throw new BuildException("Could not create type: $typeName", $t);
|
|
|
661 |
}
|
|
|
662 |
// everything fine return reference
|
|
|
663 |
return $type;
|
|
|
664 |
}
|
|
|
665 |
|
|
|
666 |
/**
|
|
|
667 |
* Executes a list of targets
|
|
|
668 |
*
|
|
|
669 |
* @param array List of target names to execute
|
|
|
670 |
* @returns void
|
|
|
671 |
* @throws BuildException
|
|
|
672 |
*/
|
|
|
673 |
function executeTargets($targetNames) {
|
|
|
674 |
foreach($targetNames as $tname) {
|
|
|
675 |
$this->executeTarget($tname);
|
|
|
676 |
}
|
|
|
677 |
}
|
|
|
678 |
|
|
|
679 |
/**
|
|
|
680 |
* Executes a target
|
|
|
681 |
*
|
|
|
682 |
* @param string Name of Target to execute
|
|
|
683 |
* @returns void
|
|
|
684 |
* @throws BuildException
|
|
|
685 |
*/
|
|
|
686 |
function executeTarget($targetName) {
|
|
|
687 |
|
|
|
688 |
// complain about executing void
|
|
|
689 |
if ($targetName === null) {
|
|
|
690 |
throw new BuildException("No target specified");
|
|
|
691 |
}
|
|
|
692 |
|
|
|
693 |
// invoke topological sort of the target tree and run all targets
|
|
|
694 |
// until targetName occurs.
|
|
|
695 |
$sortedTargets = $this->_topoSort($targetName, $this->targets);
|
|
|
696 |
|
|
|
697 |
$curIndex = (int) 0;
|
|
|
698 |
$curTarget = null;
|
|
|
699 |
do {
|
|
|
700 |
try {
|
|
|
701 |
$curTarget = $sortedTargets[$curIndex++];
|
|
|
702 |
$curTarget->performTasks();
|
|
|
703 |
} catch (BuildException $exc) {
|
|
|
704 |
$this->log("Execution of target \"".$curTarget->getName()."\" failed for the following reason: ".$exc->getMessage(), Project::MSG_ERR);
|
|
|
705 |
throw $exc;
|
|
|
706 |
}
|
|
|
707 |
} while ($curTarget->getName() !== $targetName);
|
|
|
708 |
}
|
|
|
709 |
|
|
|
710 |
|
|
|
711 |
function resolveFile($fileName, $rootDir = null) {
|
|
|
712 |
if ($rootDir === null) {
|
|
|
713 |
return $this->fileUtils->resolveFile($this->basedir, $fileName);
|
|
|
714 |
} else {
|
|
|
715 |
return $this->fileUtils->resolveFile($rootDir, $fileName);
|
|
|
716 |
}
|
|
|
717 |
}
|
|
|
718 |
|
|
|
719 |
/**
|
|
|
720 |
* Topologically sort a set of Targets.
|
|
|
721 |
* @param $root is the (String) name of the root Target. The sort is
|
|
|
722 |
* created in such a way that the sequence of Targets until the root
|
|
|
723 |
* target is the minimum possible such sequence.
|
|
|
724 |
* @param $targets is a array representing a "name to Target" mapping
|
|
|
725 |
* @return An array of Strings with the names of the targets in
|
|
|
726 |
* sorted order.
|
|
|
727 |
*/
|
|
|
728 |
function _topoSort($root, &$targets) {
|
|
|
729 |
|
|
|
730 |
$root = (string) $root;
|
|
|
731 |
$ret = array();
|
|
|
732 |
$state = array();
|
|
|
733 |
$visiting = array();
|
|
|
734 |
|
|
|
735 |
// We first run a DFS based sort using the root as the starting node.
|
|
|
736 |
// This creates the minimum sequence of Targets to the root node.
|
|
|
737 |
// We then do a sort on any remaining unVISITED targets.
|
|
|
738 |
// This is unnecessary for doing our build, but it catches
|
|
|
739 |
// circular dependencies or missing Targets on the entire
|
|
|
740 |
// dependency tree, not just on the Targets that depend on the
|
|
|
741 |
// build Target.
|
|
|
742 |
|
|
|
743 |
$this->_tsort($root, $targets, $state, $visiting, $ret);
|
|
|
744 |
|
|
|
745 |
$retHuman = "";
|
|
|
746 |
for ($i=0, $_i=count($ret); $i < $_i; $i++) {
|
|
|
747 |
$retHuman .= $ret[$i]->toString()." ";
|
|
|
748 |
}
|
|
|
749 |
$this->log("Build sequence for target '$root' is: $retHuman", Project::MSG_VERBOSE);
|
|
|
750 |
|
|
|
751 |
$keys = array_keys($targets);
|
|
|
752 |
while($keys) {
|
|
|
753 |
$curTargetName = (string) array_shift($keys);
|
|
|
754 |
if (!isset($state[$curTargetName])) {
|
|
|
755 |
$st = null;
|
|
|
756 |
} else {
|
|
|
757 |
$st = (string) $state[$curTargetName];
|
|
|
758 |
}
|
|
|
759 |
|
|
|
760 |
if ($st === null) {
|
|
|
761 |
$this->_tsort($curTargetName, $targets, $state, $visiting, $ret);
|
|
|
762 |
} elseif ($st === "VISITING") {
|
|
|
763 |
throw new Exception("Unexpected node in visiting state: $curTargetName");
|
|
|
764 |
}
|
|
|
765 |
}
|
|
|
766 |
|
|
|
767 |
$retHuman = "";
|
|
|
768 |
for ($i=0,$_i=count($ret); $i < $_i; $i++) {
|
|
|
769 |
$retHuman .= $ret[$i]->toString()." ";
|
|
|
770 |
}
|
|
|
771 |
$this->log("Complete build sequence is: $retHuman", Project::MSG_VERBOSE);
|
|
|
772 |
|
|
|
773 |
return $ret;
|
|
|
774 |
}
|
|
|
775 |
|
|
|
776 |
// one step in a recursive DFS traversal of the target dependency tree.
|
|
|
777 |
// - The array "state" contains the state (VISITED or VISITING or null)
|
|
|
778 |
// of all the target names.
|
|
|
779 |
// - The stack "visiting" contains a stack of target names that are
|
|
|
780 |
// currently on the DFS stack. (NB: the target names in "visiting" are
|
|
|
781 |
// exactly the target names in "state" that are in the VISITING state.)
|
|
|
782 |
// 1. Set the current target to the VISITING state, and push it onto
|
|
|
783 |
// the "visiting" stack.
|
|
|
784 |
// 2. Throw a BuildException if any child of the current node is
|
|
|
785 |
// in the VISITING state (implies there is a cycle.) It uses the
|
|
|
786 |
// "visiting" Stack to construct the cycle.
|
|
|
787 |
// 3. If any children have not been VISITED, tsort() the child.
|
|
|
788 |
// 4. Add the current target to the Vector "ret" after the children
|
|
|
789 |
// have been visited. Move the current target to the VISITED state.
|
|
|
790 |
// "ret" now contains the sorted sequence of Targets upto the current
|
|
|
791 |
// Target.
|
|
|
792 |
|
|
|
793 |
function _tsort($root, &$targets, &$state, &$visiting, &$ret) {
|
|
|
794 |
$state[$root] = "VISITING";
|
|
|
795 |
$visiting[] = $root;
|
|
|
796 |
|
|
|
797 |
if (!isset($targets[$root]) || !($targets[$root] instanceof Target)) {
|
|
|
798 |
$target = null;
|
|
|
799 |
} else {
|
|
|
800 |
$target = $targets[$root];
|
|
|
801 |
}
|
|
|
802 |
|
|
|
803 |
// make sure we exist
|
|
|
804 |
if ($target === null) {
|
|
|
805 |
$sb = "Target '$root' does not exist in this project.";
|
|
|
806 |
array_pop($visiting);
|
|
|
807 |
if (!empty($visiting)) {
|
|
|
808 |
$parent = (string) $visiting[count($visiting)-1];
|
|
|
809 |
$sb .= "It is used from target '$parent'.";
|
|
|
810 |
}
|
|
|
811 |
throw new BuildException($sb);
|
|
|
812 |
}
|
|
|
813 |
|
|
|
814 |
$deps = $target->getDependencies();
|
|
|
815 |
|
|
|
816 |
while($deps) {
|
|
|
817 |
$cur = (string) array_shift($deps);
|
|
|
818 |
if (!isset($state[$cur])) {
|
|
|
819 |
$m = null;
|
|
|
820 |
} else {
|
|
|
821 |
$m = (string) $state[$cur];
|
|
|
822 |
}
|
|
|
823 |
if ($m === null) {
|
|
|
824 |
// not been visited
|
|
|
825 |
$this->_tsort($cur, $targets, $state, $visiting, $ret);
|
|
|
826 |
} elseif ($m == "VISITING") {
|
|
|
827 |
// currently visiting this node, so have a cycle
|
|
|
828 |
throw $this->_makeCircularException($cur, $visiting);
|
|
|
829 |
}
|
|
|
830 |
}
|
|
|
831 |
|
|
|
832 |
$p = (string) array_pop($visiting);
|
|
|
833 |
if ($root !== $p) {
|
|
|
834 |
throw new Exception("Unexpected internal error: expected to pop $root but got $p");
|
|
|
835 |
}
|
|
|
836 |
|
|
|
837 |
$state[$root] = "VISITED";
|
|
|
838 |
$ret[] = $target;
|
|
|
839 |
}
|
|
|
840 |
|
|
|
841 |
function _makeCircularException($end, $stk) {
|
|
|
842 |
$sb = "Circular dependency: $end";
|
|
|
843 |
do {
|
|
|
844 |
$c = (string) array_pop($stk);
|
|
|
845 |
$sb .= " <- ".$c;
|
|
|
846 |
} while($c != $end);
|
|
|
847 |
return new BuildException($sb);
|
|
|
848 |
}
|
|
|
849 |
|
|
|
850 |
/**
|
|
|
851 |
* Adds a reference to an object. This method is called when the parser
|
|
|
852 |
* detects a id="foo" attribute. It passes the id as $name and a reference
|
|
|
853 |
* to the object assigned to this id as $value
|
|
|
854 |
*/
|
|
|
855 |
function addReference($name, $object) {
|
|
|
856 |
if (isset($this->references[$name])) {
|
|
|
857 |
$this->log("Overriding previous definition of reference to $name", Project::MSG_WARN);
|
|
|
858 |
}
|
|
|
859 |
$this->log("Adding reference: $name -> ".get_class($object), Project::MSG_DEBUG);
|
|
|
860 |
$this->references[$name] = $object;
|
|
|
861 |
}
|
|
|
862 |
|
|
|
863 |
/**
|
|
|
864 |
* Returns the references array.
|
|
|
865 |
* @return array
|
|
|
866 |
*/
|
|
|
867 |
function getReferences() {
|
|
|
868 |
return $this->references;
|
|
|
869 |
}
|
|
|
870 |
|
|
|
871 |
/**
|
|
|
872 |
* Returns a specific reference.
|
|
|
873 |
* @param string $key The reference id/key.
|
|
|
874 |
* @return Reference or null if not defined
|
|
|
875 |
*/
|
|
|
876 |
function getReference($key)
|
|
|
877 |
{
|
|
|
878 |
if (isset($this->references[$key])) {
|
|
|
879 |
return $this->references[$key];
|
|
|
880 |
}
|
|
|
881 |
return null; // just to be explicit
|
|
|
882 |
}
|
|
|
883 |
|
|
|
884 |
/**
|
|
|
885 |
* Abstracting and simplifyling Logger calls for project messages
|
|
|
886 |
*/
|
|
|
887 |
function log($msg, $level = Project::MSG_INFO) {
|
|
|
888 |
$this->logObject($this, $msg, $level);
|
|
|
889 |
}
|
|
|
890 |
|
|
|
891 |
function logObject($obj, $msg, $level) {
|
|
|
892 |
$this->fireMessageLogged($obj, $msg, $level);
|
|
|
893 |
}
|
|
|
894 |
|
|
|
895 |
function addBuildListener(BuildListener $listener) {
|
|
|
896 |
$this->listeners[] = $listener;
|
|
|
897 |
}
|
|
|
898 |
|
|
|
899 |
function removeBuildListener(BuildListener $listener) {
|
|
|
900 |
$newarray = array();
|
|
|
901 |
for ($i=0, $size=count($this->listeners); $i < $size; $i++) {
|
|
|
902 |
if ($this->listeners[$i] !== $listener) {
|
|
|
903 |
$newarray[] = $this->listeners[$i];
|
|
|
904 |
}
|
|
|
905 |
}
|
|
|
906 |
$this->listeners = $newarray;
|
|
|
907 |
}
|
|
|
908 |
|
|
|
909 |
function getBuildListeners() {
|
|
|
910 |
return $this->listeners;
|
|
|
911 |
}
|
|
|
912 |
|
|
|
913 |
function fireBuildStarted() {
|
|
|
914 |
$event = new BuildEvent($this);
|
|
|
915 |
foreach($this->listeners as $listener) {
|
|
|
916 |
$listener->buildStarted($event);
|
|
|
917 |
}
|
|
|
918 |
}
|
|
|
919 |
|
|
|
920 |
function fireBuildFinished($exception) {
|
|
|
921 |
$event = new BuildEvent($this);
|
|
|
922 |
$event->setException($exception);
|
|
|
923 |
foreach($this->listeners as $listener) {
|
|
|
924 |
$listener->buildFinished($event);
|
|
|
925 |
}
|
|
|
926 |
}
|
|
|
927 |
|
|
|
928 |
function fireTargetStarted($target) {
|
|
|
929 |
$event = new BuildEvent($target);
|
|
|
930 |
foreach($this->listeners as $listener) {
|
|
|
931 |
$listener->targetStarted($event);
|
|
|
932 |
}
|
|
|
933 |
}
|
|
|
934 |
|
|
|
935 |
function fireTargetFinished($target, $exception) {
|
|
|
936 |
$event = new BuildEvent($target);
|
|
|
937 |
$event->setException($exception);
|
|
|
938 |
foreach($this->listeners as $listener) {
|
|
|
939 |
$listener->targetFinished($event);
|
|
|
940 |
}
|
|
|
941 |
}
|
|
|
942 |
|
|
|
943 |
function fireTaskStarted($task) {
|
|
|
944 |
$event = new BuildEvent($task);
|
|
|
945 |
foreach($this->listeners as $listener) {
|
|
|
946 |
$listener->taskStarted($event);
|
|
|
947 |
}
|
|
|
948 |
}
|
|
|
949 |
|
|
|
950 |
function fireTaskFinished($task, $exception) {
|
|
|
951 |
$event = new BuildEvent($task);
|
|
|
952 |
$event->setException($exception);
|
|
|
953 |
foreach($this->listeners as $listener) {
|
|
|
954 |
$listener->taskFinished($event);
|
|
|
955 |
}
|
|
|
956 |
}
|
|
|
957 |
|
|
|
958 |
function fireMessageLoggedEvent($event, $message, $priority) {
|
|
|
959 |
$event->setMessage($message, $priority);
|
|
|
960 |
foreach($this->listeners as $listener) {
|
|
|
961 |
$listener->messageLogged($event);
|
|
|
962 |
}
|
|
|
963 |
}
|
|
|
964 |
|
|
|
965 |
function fireMessageLogged($object, $message, $priority) {
|
|
|
966 |
$this->fireMessageLoggedEvent(new BuildEvent($object), $message, $priority);
|
|
|
967 |
}
|
|
|
968 |
}
|