[ Index ]
 

Code source de Symfony 1.0.0

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/vendor/phing/ -> Project.php (source)

   1  <?php
   2  /*
   3   *  $Id: Project.php 3076 2006-12-18 08:52:12Z fabien $
   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 $cls();        
 600      
 601              if ($o instanceof Task) {
 602                  $task = $o;
 603              } else {
 604                  $this->log ("  (Using TaskAdapter for: $taskType)", PROJECT_MSG_DEBUG);
 605                  // not a real task, try adapter
 606                  $taskA = new TaskAdapter();
 607                  $taskA->setProxy($o);
 608                  $task = $taskA;
 609              }
 610              $task->setProject($this);
 611              $task->setTaskType($taskType);
 612              // set default value, can be changed by the user
 613              $task->setTaskName($taskType);
 614              $this->log ("  +Task: " . $taskType, PROJECT_MSG_DEBUG);
 615          } catch (Exception $t) {
 616              throw new BuildException("Could not create task of type: " . $taskType, $t);
 617          }
 618          // everything fine return reference
 619          return $task;
 620      }
 621  
 622      /**
 623       * Create a task instance and return reference to it
 624       * See createTask() for explanation how this works
 625       *
 626       * @param    string   Type name
 627       * @returns  object   A datatype object
 628       * @throws   BuildException
 629       *           Exception
 630       */
 631      function createDataType($typeName) {        
 632          try {
 633              $cls = "";
 634              $typelwr = strtolower($typeName);
 635              foreach ($this->typedefs as $name => $class) {
 636                  if (strtolower($name) === $typelwr) {
 637                      $cls = StringHelper::unqualify($class);                                    
 638                      break;
 639                  }
 640              }
 641              
 642              if ($cls === "") {
 643                  return null;
 644              }
 645              
 646              if (!class_exists($cls)) {
 647                  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.)");
 648              }
 649              
 650              $type = new $cls();
 651              $this->log("  +Type: $typeName", PROJECT_MSG_DEBUG);
 652              if (!($type instanceof DataType)) {
 653                  throw new Exception("$class is not an instance of phing.types.DataType");
 654              }
 655              if ($type instanceof ProjectComponent) {
 656                  $type->setProject($this);
 657              }
 658          } catch (Exception $t) {
 659              throw new BuildException("Could not create type: $typeName", $t);
 660          }
 661          // everything fine return reference
 662          return $type;
 663      }
 664  
 665      /**
 666       * Executes a list of targets
 667       *
 668       * @param    array  List of target names to execute
 669       * @returns  void
 670       * @throws   BuildException
 671       */
 672      function executeTargets($targetNames) {
 673          foreach($targetNames as $tname) {
 674              $this->executeTarget($tname);
 675          }
 676      }
 677  
 678      /**
 679       * Executes a target
 680       *
 681       * @param    string  Name of Target to execute
 682       * @returns  void
 683       * @throws   BuildException
 684       */
 685      function executeTarget($targetName) {
 686  
 687          // complain about executing void
 688          if ($targetName === null) {
 689              throw new BuildException("No target specified");
 690          }
 691  
 692          // invoke topological sort of the target tree and run all targets
 693          // until targetName occurs.
 694          $sortedTargets = $this->_topoSort($targetName, $this->targets);        
 695  
 696          $curIndex = (int) 0;
 697          $curTarget = null;
 698          do {
 699              try {
 700                  $curTarget = $sortedTargets[$curIndex++];
 701                  $curTarget->performTasks();
 702              } catch (BuildException $exc) {
 703                  $this->log("Execution of target \"".$curTarget->getName()."\" failed for the following reason: ".$exc->getMessage(), PROJECT_MSG_ERR);
 704                  throw $exc;
 705              }
 706          } while ($curTarget->getName() !== $targetName);
 707      }
 708  
 709  
 710      function resolveFile($fileName, $rootDir = null) {
 711          if ($rootDir === null) {
 712              return $this->fileUtils->resolveFile($this->basedir, $fileName);
 713          } else {
 714              return $this->fileUtils->resolveFile($rootDir, $fileName);
 715          }
 716      }    
 717  
 718      /**
 719       * Topologically sort a set of Targets.
 720       * @param  $root is the (String) name of the root Target. The sort is
 721       *         created in such a way that the sequence of Targets until the root
 722       *         target is the minimum possible such sequence.
 723       * @param  $targets is a array representing a "name to Target" mapping
 724       * @return An array of Strings with the names of the targets in
 725       *         sorted order.
 726       */
 727      function _topoSort($root, &$targets) {
 728  
 729          $root     = (string) $root;
 730          $ret      = array();
 731          $state    = array();
 732          $visiting = array();
 733  
 734          // We first run a DFS based sort using the root as the starting node.
 735          // This creates the minimum sequence of Targets to the root node.
 736          // We then do a sort on any remaining unVISITED targets.
 737          // This is unnecessary for doing our build, but it catches
 738          // circular dependencies or missing Targets on the entire
 739          // dependency tree, not just on the Targets that depend on the
 740          // build Target.
 741  
 742          $this->_tsort($root, $targets, $state, $visiting, $ret);
 743  
 744          $retHuman = "";
 745          for ($i=0, $_i=count($ret); $i < $_i; $i++) {
 746              $retHuman .= $ret[$i]->toString()." ";
 747          }
 748          $this->log("Build sequence for target '$root' is: $retHuman", PROJECT_MSG_VERBOSE);
 749  
 750          $keys = array_keys($targets);
 751          while($keys) {
 752              $curTargetName = (string) array_shift($keys);
 753              if (!isset($state[$curTargetName])) {
 754                  $st = null;
 755              } else {
 756                  $st = (string) $state[$curTargetName];
 757              }
 758  
 759              if ($st === null) {
 760                  $this->_tsort($curTargetName, $targets, $state, $visiting, $ret);
 761              } elseif ($st === "VISITING") {
 762                  throw new Exception("Unexpected node in visiting state: $curTargetName");
 763              }
 764          }
 765  
 766          $retHuman = "";
 767          for ($i=0,$_i=count($ret); $i < $_i; $i++) {
 768              $retHuman .= $ret[$i]->toString()." ";
 769          }
 770          $this->log("Complete build sequence is: $retHuman", PROJECT_MSG_VERBOSE);
 771  
 772          return $ret;
 773      }
 774  
 775      // one step in a recursive DFS traversal of the target dependency tree.
 776      // - The array "state" contains the state (VISITED or VISITING or null)
 777      //   of all the target names.
 778      // - The stack "visiting" contains a stack of target names that are
 779      //   currently on the DFS stack. (NB: the target names in "visiting" are
 780      //    exactly the target names in "state" that are in the VISITING state.)
 781      // 1. Set the current target to the VISITING state, and push it onto
 782      //    the "visiting" stack.
 783      // 2. Throw a BuildException if any child of the current node is
 784      //    in the VISITING state (implies there is a cycle.) It uses the
 785      //    "visiting" Stack to construct the cycle.
 786      // 3. If any children have not been VISITED, tsort() the child.
 787      // 4. Add the current target to the Vector "ret" after the children
 788      //    have been visited. Move the current target to the VISITED state.
 789      //    "ret" now contains the sorted sequence of Targets upto the current
 790      //    Target.
 791  
 792      function _tsort($root, &$targets, &$state, &$visiting, &$ret) {
 793          $state[$root] = "VISITING";
 794          $visiting[]  = $root;
 795  
 796          if (!isset($targets[$root]) || !($targets[$root] instanceof Target)) {
 797              $target = null;
 798          } else {
 799              $target = $targets[$root];
 800          }
 801  
 802          // make sure we exist
 803          if ($target === null) {
 804              $sb = "Target '$root' does not exist in this project.";
 805              array_pop($visiting);
 806              if (!empty($visiting)) {
 807                  $parent = (string) $visiting[count($visiting)-1];
 808                  $sb .= "It is used from target '$parent'.";
 809              }
 810              throw new BuildException($sb);
 811          }
 812  
 813          $deps = $target->getDependencies();
 814  
 815          while($deps) {
 816              $cur = (string) array_shift($deps);
 817              if (!isset($state[$cur])) {
 818                  $m = null;
 819              } else {
 820                  $m = (string) $state[$cur];
 821              }
 822              if ($m === null) {
 823                  // not been visited
 824                  $this->_tsort($cur, $targets, $state, $visiting, $ret);
 825              } elseif ($m == "VISITING") {
 826                  // currently visiting this node, so have a cycle
 827                  throw $this->_makeCircularException($cur, $visiting);
 828              }
 829          }
 830  
 831          $p = (string) array_pop($visiting);
 832          if ($root !== $p) {
 833              throw new Exception("Unexpected internal error: expected to pop $root but got $p");
 834          }
 835  
 836          $state[$root] = "VISITED";
 837          $ret[] = $target;
 838      }
 839  
 840      function _makeCircularException($end, $stk) {
 841          $sb = "Circular dependency: $end";
 842          do {
 843              $sb .= " <- ".(string) array_pop($stk);
 844          } while($c != $end);
 845          return new BuildException($sb);
 846      }
 847  
 848      /**
 849       * Adds a reference to an object. This method is called when the parser
 850       * detects a id="foo" attribute. It passes the id as $name and a reference
 851       * to the object assigned to this id as $value
 852       */
 853      function addReference($name, $object) {
 854          if (isset($this->references[$name])) {
 855              $this->log("Overriding previous definition of reference to $name", PROJECT_MSG_WARN);
 856          }
 857          $this->log("Adding reference: $name -> ".get_class($object), PROJECT_MSG_DEBUG);
 858          $this->references[$name] = $object;
 859      }
 860  
 861      /**
 862       * Returns the references array.
 863       * @return array
 864       */
 865      function getReferences() {
 866          return $this->references;
 867      }
 868      
 869      /**
 870       * Returns a specific reference.
 871       * @param string $key The reference id/key.
 872       * @return object or null if not defined
 873       */
 874  	function getReference($key)
 875      {
 876          if (isset($this->references[$key])) {
 877              return $this->references[$key];
 878          }
 879          return null; // just to be explicit
 880      }
 881  
 882      /**
 883       * Abstracting and simplifyling Logger calls for project messages
 884       */
 885      function log($msg, $level = PROJECT_MSG_INFO) {
 886          $this->logObject($this, $msg, $level);
 887      }
 888  
 889      function logObject($obj, $msg, $level) {
 890          $this->fireMessageLogged($obj, $msg, $level);
 891      }
 892  
 893      function addBuildListener(BuildListener $listener) {
 894          $this->listeners[] = $listener;
 895      }
 896  
 897      function removeBuildListener(BuildListener $listener) {
 898          $newarray = array();
 899          for ($i=0, $size=count($this->listeners); $i < $size; $i++) {
 900              if ($this->listeners[$i] !== $listener) {
 901                  $newarray[] = $this->listeners[$i];
 902              }
 903          }
 904          $this->listeners = $newarray;
 905      }
 906  
 907      function getBuildListeners() {
 908          return $this->listeners;
 909      }
 910  
 911      function fireBuildStarted() {
 912          $event = new BuildEvent($this);        
 913          foreach($this->listeners as $listener) {
 914              $listener->buildStarted($event);
 915          }
 916      }
 917  
 918      function fireBuildFinished($exception) {        
 919          $event = new BuildEvent($this);
 920          $event->setException($exception);
 921          foreach($this->listeners as $listener) {
 922              $listener->buildFinished($event);
 923          }
 924      }
 925  
 926      function fireTargetStarted($target) {
 927          $event = new BuildEvent($target);        
 928             foreach($this->listeners as $listener) {
 929              $listener->targetStarted($event);
 930          }
 931      }
 932  
 933      function fireTargetFinished($target, $exception) {
 934          $event = new BuildEvent($target);        
 935          $event->setException($exception);
 936          foreach($this->listeners as $listener) {
 937              $listener->targetFinished($event);
 938          }
 939      }
 940  
 941      function fireTaskStarted($task) {
 942          $event = new BuildEvent($task);        
 943          foreach($this->listeners as $listener) {
 944              $listener->taskStarted($event);
 945          }
 946      }
 947  
 948      function fireTaskFinished($task, $exception) {
 949          $event = new BuildEvent($task);        
 950          $event->setException($exception);
 951          foreach($this->listeners as $listener) {
 952              $listener->taskFinished($event);
 953          }
 954      }
 955  
 956      function fireMessageLoggedEvent($event, $message, $priority) {
 957          $event->setMessage($message, $priority);
 958          foreach($this->listeners as $listener) {
 959              $listener->messageLogged($event);
 960          }
 961      }
 962  
 963      function fireMessageLogged($object, $message, $priority) {
 964          $this->fireMessageLoggedEvent(new BuildEvent($object), $message, $priority);
 965      }
 966  }


Généré le : Fri Mar 16 22:42:14 2007 par Balluche grâce à PHPXref 0.7