root/tags/2.2.0RC1/classes/phing/Project.php

Revision 1, 32.5 kB (checked in by hans, 3 years ago)

Initial checkin

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