root/trunk/classes/phing/Project.php

Revision 312, 33.3 kB (checked in by hans, 1 year ago)

Refs #188 - Updating phpunit & coverage tasks to work w/ new namespaces.

  • Property svn:keywords set to author date id revision
Line 
1 <?php
2 /*
3  *  $Id$
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 namespace phing;
23
24 use phing::util::FileUtils;
25 use phing::listener::BuildListener;
26 use phing::input::InputHandler;
27 use phing::system::util::Properties;
28 use phing::system::io::File;
29 use phing::util::StringHelper;
30
31 /**
32  *  The Phing project class. Represents a completely configured Phing project.
33  *  The class defines the project and all tasks/targets. It also contains
34  *  methods to start a build as well as some properties and FileSystem
35  *  abstraction.
36  *
37  * @author    Andreas Aderhold <andi@binarycloud.com>
38  * @author    Hans Lellelid <hans@xmpl.org>
39  * @version   $Revision: 1.29 $
40  * @package   phing
41  */
42 class Project {
43
44     // Logging level constants.
45     const MSG_DEBUG = 4;
46     const MSG_VERBOSE = 3;
47     const MSG_INFO = 2;
48     const MSG_WARN = 1;
49     const MSG_ERR = 0;
50     
51     /** contains the targets */
52     private $targets         = array();
53     /** global filterset (future use) */
54     private $globalFilterSet = array();
55     /**  all globals filters (future use) */
56     private $globalFilters   = array();
57     
58     /** Project properties map (usually String to String). */
59     private $properties = array();
60     
61     /**
62      * Map of "user" properties (as created in the Ant task, for example).
63      * Note that these key/value pairs are also always put into the
64      * project properties, so only the project properties need to be queried.
65      * Mapping is String to String.
66      */
67     private $userProperties = array();
68     
69     /**
70      * Map of inherited "user" properties - that are those "user"
71      * properties that have been created by tasks and not been set
72      * from the command line or a GUI tool.
73      * Mapping is String to String.
74      */
75     private $inheritedProperties = array();
76     
77     /** task definitions for this project*/
78     private $taskdefs = array();
79     
80     /** type definitions for this project */
81     private $typedefs = array();
82     
83     /** holds ref names and a reference to the referred object*/
84     private $references = array();
85     
86     /** The InputHandler being used by this project. */
87     private $inputHandler;
88     
89     /* -- properties that come in via xml attributes -- */
90     
91     /** basedir (File object) */
92     private $basedir;
93     
94     /** the default target name */
95     private $defaultTarget = 'all';
96     
97     /** project name (required) */
98     private $name;
99     
100     /** project description */
101     private $description;
102
103     /** a FileUtils object */
104     private $fileUtils;
105     
106     /**  Build listeneers */
107     private $listeners = array();
108
109     /**
110      *  Constructor, sets any default vars.
111      */
112     function __construct() {
113         $this->inputHandler = new phing::input::DefaultInputHandler();
114     }
115
116     /**
117      * Sets the input handler
118      */
119     public function setInputHandler(InputHandler $handler) {
120         $this->inputHandler = $handler;
121     }
122
123     /**
124      * Retrieves the current input handler.
125      */
126     public function getInputHandler() {
127         return $this->inputHandler;
128     }
129
130     /** inits the project, called from main app */
131     function init() {
132         // set builtin properties
133         $this->setSystemProperties();
134         
135         // load default tasks
136         $taskdefs = Phing::getResourcePath("phing/tasks/defaults.properties");
137         
138         try { // try to load taskdefs
139             $props = new Properties();
140             $in = new File((string)$taskdefs);
141
142             if ($in === null) {
143                 throw new BuildException("Can't load default task list");
144             }
145             $props->load($in);
146
147             $enum = $props->propertyNames();
148             foreach($enum as $key) {
149                 $value = $props->getProperty($key);
150                 $this->addTaskDefinition($key, $value);
151             }
152         } catch (IOException $ioe) {
153             throw new BuildException("Can't load default task list");
154         }
155
156         // load default tasks
157         $typedefs = Phing::getResourcePath("phing/types/defaults.properties");
158
159         try { // try to load typedefs
160             $props = new Properties();
161             $in    = new File((string)$typedefs);
162             if ($in === null) {
163                 throw new BuildException("Can't load default datatype list");
164             }
165             $props->load($in);
166
167             $enum = $props->propertyNames();
168             foreach($enum as $key) {
169                 $value = $props->getProperty($key);
170                 $this->addDataTypeDefinition($key, $value);
171             }
172         } catch(IOException $ioe) {
173             throw new BuildException("Can't load default datatype list");
174         }
175     }
176
177     /** returns the global filterset (future use) */
178     function getGlobalFilterSet() {
179         return $this->globalFilterSet;
180     }
181
182     // ---------------------------------------------------------
183     // Property methods
184     // ---------------------------------------------------------
185     
186     /**
187      * Sets a property. Any existing property of the same name
188      * is overwritten, unless it is a user property.
189      * @param string $name The name of property to set.
190      *             Must not be <code>null</code>.
191      * @param string $value The new value of the property.
192      *              Must not be <code>null</code>.
193      * @return void
194      */
195     public function setProperty($name, $value) {
196     
197         // command line properties take precedence
198         if (isset($this->userProperties[$name])) {
199             $this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE);
200             return;
201         }
202
203         if (isset($this->properties[$name])) {
204             $this->log("Overriding previous definition of property " . $name, Project::MSG_VERBOSE);
205         }
206
207         $this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
208         $this->properties[$name] = $value;
209     }
210
211     /**
212      * Sets a property if no value currently exists. If the property
213      * exists already, a message is logged and the method returns with
214      * no other effect.
215      *
216      * @param string $name The name of property to set.
217      *             Must not be <code>null</code>.
218      * @param string $value The new value of the property.
219      *              Must not be <code>null</code>.
220      * @since 2.0
221      */
222     public function setNewProperty($name, $value) {
223         if (isset($this->properties[$name])) {
224             $this->log("Override ignored for property " . $name, Project::MSG_DEBUG);
225             return;
226         }
227         $this->log("Setting project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
228         $this->properties[$name] = $value;
229     }
230
231     /**
232      * Sets a user property, which cannot be overwritten by
233      * set/unset property calls. Any previous value is overwritten.
234      * @param string $name The name of property to set.
235      *             Must not be <code>null</code>.
236      * @param string $value The new value of the property.
237      *              Must not be <code>null</code>.
238      * @see #setProperty()
239      */
240     public function setUserProperty($name, $value) {
241         $this->log("Setting ro project property: " . $name . " -> " . $value, Project::MSG_DEBUG);
242         $this->userProperties[$name] = $value;
243         $this->properties[$name] = $value;
244     }
245
246     /**
247      * Sets a user property, which cannot be overwritten by set/unset
248      * property calls. Any previous value is overwritten. Also marks
249      * these properties as properties that have not come from the
250      * command line.
251      *
252      * @param string $name The name of property to set.
253      *             Must not be <code>null</code>.
254      * @param string $value The new value of the property.
255      *              Must not be <code>null</code>.
256      * @see #setProperty()
257      */
258     public function setInheritedProperty($name, $value) {
259         $this->inheritedProperties[$name] = $value;
260         $this->setUserProperty($name, $value);
261     }
262
263     /**
264      * Sets a property unless it is already defined as a user property
265      * (in which case the method returns silently).
266      *
267      * @param name The name of the property.
268      *             Must not be <code>null</code>.
269      * @param value The property value. Must not be <code>null</code>.
270      */
271     private function setPropertyInternal($name, $value) {
272         if (isset($this->userProperties[$name])) {
273             $this->log("Override ignored for user property " . $name, Project::MSG_VERBOSE);
274             return;
275         }
276         $this->properties[$name] = $value;
277     }
278
279     /**
280      * Returns the value of a property, if it is set.
281      *
282      * @param string $name The name of the property.
283      *             May be <code>null</code>, in which case
284      *             the return value is also <code>null</code>.
285      * @return string The property value, or <code>null</code> for no match
286      *         or if a <code>null</code> name is provided.
287      */
288     public function getProperty($name) {
289         if (!isset($this->properties[$name])) {
290             return null;
291         }
292         return $this->properties[$name];
293     }
294
295     /**
296      * Replaces ${} style constructions in the given value with the
297      * string value of the corresponding data types.
298      *
299      * @param value The string to be scanned for property references.
300      *              May be <code>null</code>.
301      *
302      * @return the given string with embedded property names replaced
303      *         by values, or <code>null</code> if the given string is
304      *         <code>null</code>.
305      *
306      * @exception BuildException if the given value has an unclosed
307      *                           property name, e.g. <code>${xxx</code>
308      */
309     public function replaceProperties($value) {
310         return ProjectConfigurator::replaceProperties($this, $value, $this->properties);
311     }
312
313     /**
314      * Returns the value of a user property, if it is set.
315      *
316      * @param string $name The name of the property.
317      *             May be <code>null</code>, in which case
318      *             the return value is also <code>null</code>.
319      * @return string  The property value, or <code>null</code> for no match
320      *         or if a <code>null</code> name is provided.
321      */
322      public function getUserProperty($name) {
323         if (!isset($this->userProperties[$name])) {
324             return null;
325         }
326         return $this->userProperties[$name];
327     }
328
329     /**
330      * Returns a copy of the properties table.
331      * @return array A hashtable containing all properties
332      *         (including user properties).
333      */
334     public function getProperties() {
335         return $this->properties;
336     }
337
338     /**
339      * Returns a copy of the user property hashtable
340      * @return a hashtable containing just the user properties
341      */
342     public function getUserProperties() {
343         return $this->userProperties;
344     }
345
346     /**
347      * Copies all user properties that have been set on the command
348      * line or a GUI tool from this instance to the Project instance
349      * given as the argument.
350      *
351      * <p>To copy all "user" properties, you will also have to call
352      * {@link #copyInheritedProperties copyInheritedProperties}.</p>
353      *
354      * @param Project $other the project to copy the properties to.  Must not be null.
355      * @return void
356      * @since phing 2.0
357      */
358     public function copyUserProperties(Project $other) {       
359         foreach($this->userProperties as $arg => $value) {
360             if (isset($this->inheritedProperties[$arg])) {
361                 continue;
362             }
363             $other->setUserProperty($arg, $value);
364         }
365     }
366
367     /**
368      * Copies all user properties that have not been set on the
369      * command line or a GUI tool from this instance to the Project
370      * instance given as the argument.
371      *
372      * <p>To copy all "user" properties, you will also have to call
373      * {@link #copyUserProperties copyUserProperties}.</p>
374      *
375      * @param other the project to copy the properties to.  Must not be null.
376      *
377      * @since phing 2.0
378      */
379     public function copyInheritedProperties(Project $other) {
380         foreach($this->userProperties as $arg => $value) {
381             if ($other->getUserProperty($arg) !== null) {
382                 continue;
383             }
384             $other->setInheritedProperty($arg, $value);
385         }       
386     }
387     
388     // ---------------------------------------------------------
389     //  END Properties methods
390     // ---------------------------------------------------------
391
392
393     function setDefaultTarget($targetName) {
394         $this->defaultTarget = (string) trim($targetName);
395     }
396
397     function getDefaultTarget() {
398         return (string) $this->defaultTarget;
399     }
400
401     /**
402      * Sets the name of the current project
403      *
404      * @param    string   name of project
405      * @return   void
406      * @access   public
407      * @author   Andreas Aderhold, andi@binarycloud.com
408      */
409
410     function setName($name) {
411         $this->name = (string) trim($name);
412         $this->setProperty("phing.project.name", $this->name);
413     }
414
415     /**
416      * Returns the name of this project
417      *
418      * @returns  string  projectname
419      * @access   public
420      * @author   Andreas Aderhold, andi@binarycloud.com
421      */
422     function getName() {
423         return (string) $this->name;
424     }
425
426     /** Set the projects description */
427     function setDescription($description) {
428         $this->description = (string) trim($description);
429     }
430
431     /** return the description, null otherwise */
432     function getDescription() {
433         return $this->description;
434     }
435
436     /** Set basedir object from xml*/
437     function setBasedir($dir) {
438         if ($dir instanceof File) {
439             $dir = $dir->getAbsolutePath();
440         }
441
442         $dir = FileUtils::normalize($dir);
443
444         $dir = new File((string) $dir);
445         if (!$dir->exists()) {
446             throw new BuildException("Basedir ".$dir->getAbsolutePath()." does not exist");
447         }
448         if (!$dir->isDirectory()) {
449             throw new BuildException("Basedir ".$dir->getAbsolutePath()." is not a directory");
450         }
451         $this->basedir = $dir;
452         $this->setPropertyInternal("project.basedir", $this->basedir->getAbsolutePath());
453         $this->log("Project base dir set to: " . $this->basedir->getPath(), Project::MSG_VERBOSE);
454         
455         // [HL] added this so that ./ files resolve correctly.  This may be a mistake ... or may be in wrong place.               
456         chdir($dir->getAbsolutePath());
457     }
458
459     /**
460      * Returns the basedir of this project
461      *
462      * @returns  File  Basedir File object
463      * @access   public
464      * @throws   BuildException
465      * @author   Andreas Aderhold, andi@binarycloud.com
466      */
467     function getBasedir() {
468         if ($this->basedir === null) {           
469             try { // try to set it
470                 $this->setBasedir(".");
471             } catch (BuildException $exc) {
472                 throw new BuildException("Can not set default basedir. ".$exc->getMessage());
473             }
474         }
475         return $this->basedir;
476     }
477
478     /**
479      * Sets system properties and the environment variables for this project.
480      *
481      * @return void
482      */
483     function setSystemProperties() {
484         
485         // first get system properties
486         $systemP = array_merge( self::getProperties(), Phing::getProperties() );
487         foreach($systemP as $name => $value) {
488             $this->setPropertyInternal($name, $value);
489         }
490         
491         // and now the env vars
492         foreach($_SERVER as $name => $value) {
493             // skip arrays
494             if (is_array($value)) {
495                 continue;
496             }
497             $this->setPropertyInternal('env.' . $name, $value);
498         }
499         return true;
500     }
501
502
503     /**
504      * Adds a task definition.
505      * @param string $name Name of tag.
506      * @param string $class The class path to use.
507      * @param string $classpath The classpat to use.
508      */
509     function addTaskDefinition($name, $class, $classpath = null) {
510         $name  = $name;
511         $class = $class;
512         if ($class === "") {
513             $this->log("Task $name has no class defined.", Project::MSG_ERR);
514         }  elseif (!isset($this->taskdefs[$name])) {
515             Phing::import($class, $classpath);
516             $this->taskdefs[$name] = $class;
517             $this->log("  +Task definiton: $name ($class)", Project::MSG_DEBUG);
518         } else {
519             $this->log("Task $name ($class) already registerd, skipping", Project::MSG_VERBOSE);
520         }
521     }
522
523     function &getTaskDefinitions() {
524         return $this->taskdefs;
525     }
526
527     /**
528      * Adds a data type definition.
529      * @param string $name Name of tag.
530      * @param string $class The class path to use.
531      * @param string $classpath The classpat to use.
532      */
533     function addDataTypeDefinition($typeName, $typeClass, $classpath = null) {   
534         if (!isset($this->typedefs[$typeName])) {       
535             Phing::import($typeClass, $classpath);
536             $this->typedefs[$typeName] = $typeClass;
537             $this->log("  +User datatype: $typeName ($typeClass)", Project::MSG_DEBUG);
538         } else {
539             $this->log("Type $name ($class) already registerd, skipping", Project::MSG_VERBOSE);
540         }
541     }
542
543     function getDataTypeDefinitions() {
544         return $this->typedefs;
545     }
546
547     /** add a new target to the project */
548     function addTarget($targetName, &$target) {
549         if (isset($this->targets[$targetName])) {
550             throw new BuildException("Duplicate target: $targetName");
551         }
552         $this->addOrReplaceTarget($targetName, $target);
553     }
554
555     function addOrReplaceTarget($targetName, &$target) {
556         $this->log("  +Target: $targetName", Project::MSG_DEBUG);
557         $target->setProject($this);
558         $this->targets[$targetName] = $target;
559     }
560
561     function getTargets() {
562         return $this->targets;
563     }
564
565     /**
566      * Create a new task instance and return reference to it. This method is
567      * sorta factory like. A _local_ instance is created and a reference returned to
568      * that instance. Usually PHP destroys local variables when the function call
569      * ends. But not if you return a reference to that variable.
570      * This is kinda error prone, because if no reference exists to the variable
571      * it is destroyed just like leaving the local scope with primitive vars. There's no
572      * central place where the instance is stored as in other OOP like languages.
573      *
574      * [HL] Well, ZE2 is here now, and this is  still working. We'll leave this alone
575      * unless there's any good reason not to.
576      *
577      * @param    string    $taskType    Task name
578      * @returns  Task                A task object
579      * @throws   BuildException
580      *           Exception
581      */
582     function createTask($taskType) {
583         try {
584             $cls = "";
585             $tasklwr = strtolower($taskType);
586             foreach ($this->taskdefs as $name => $class) {
587                 if (strtolower($name) === $tasklwr) {
588                     $cls = StringHelper::unqualify($class);                                   
589                     break;
590                 }
591             }
592             
593             if ($cls ===