root/branches/2.3/classes/phing/Project.php

Revision 345, 32.5 kB (checked in by mrook, 1 year ago)

Remove deprecated logging constants (patch by Markus Fischer)

  • 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 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