| [ Index ] |
|
Code source de Symfony 1.0.0 |
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 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Fri Mar 16 22:42:14 2007 | par Balluche grâce à PHPXref 0.7 |