Changeset 368
- Timestamp:
- 05/18/08 17:21:02 (8 months ago)
- Files:
-
- branches/2.4/classes/phing/Project.php (modified) (2 diffs)
- branches/2.4/classes/phing/parser/Location.php (modified) (1 diff)
- branches/2.4/classes/phing/parser/PhingXMLContext.php (added)
- branches/2.4/classes/phing/parser/ProjectConfigurator.php (modified) (5 diffs)
- branches/2.4/classes/phing/parser/ProjectHandler.php (modified) (2 diffs)
- branches/2.4/classes/phing/parser/TargetHandler.php (modified) (3 diffs)
- branches/2.4/classes/phing/tasks/defaults.properties (modified) (1 diff)
- branches/2.4/classes/phing/tasks/system/ImportTask.php (added)
- branches/2.4/docs/phing_guide/book/chapters/appendixes/AppendixB-CoreTasks.html (modified) (1 diff)
- branches/2.4/test/classes/phing/tasks/ImportTaskTest.php (added)
- branches/2.4/test/etc/tasks/importing.xml (added)
- branches/2.4/test/etc/tasks/imports (added)
- branches/2.4/test/etc/tasks/imports/imported.properties (added)
- branches/2.4/test/etc/tasks/imports/imported.xml (added)
- branches/2.4/test/etc/tasks/imports/importedImport.xml (added)
- branches/2.4/test/run-tests.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/2.4/classes/phing/Project.php
r345 r368 536 536 $this->log(" +User datatype: $typeName ($typeClass)", Project::MSG_DEBUG); 537 537 } else { 538 $this->log("Type $ name ($class) already registerd, skipping", Project::MSG_VERBOSE);538 $this->log("Type $typeName ($typeClass) already registerd, skipping", Project::MSG_VERBOSE); 539 539 } 540 540 } … … 556 556 $target->setProject($this); 557 557 $this->targets[$targetName] = $target; 558 559 $ctx = $this->getReference("phing.parsing.context"); 560 $current = $ctx->getConfigurator()->getCurrentTargets(); 561 $current[$targetName] = $target; 558 562 } 559 563 branches/2.4/classes/phing/parser/Location.php
r123 r368 70 70 return (string) $buf; 71 71 } 72 73 function __toString () { 74 return $this->toString(); 75 } 72 76 } branches/2.4/classes/phing/parser/ProjectConfigurator.php
r147 r368 25 25 include_once 'phing/system/lang/FileNotFoundException.php'; 26 26 include_once 'phing/system/io/PhingFile.php'; 27 include_once 'phing/parser/PhingXMLContext.php'; 28 include_once 'phing/IntrospectionHelper.php'; 27 29 28 30 /** … … 45 47 public $buildFile; 46 48 public $buildFileParent; 47 49 50 /** Targets in current file */ 51 private $currentTargets; 52 53 /** Synthetic target that will be called at the end to the parse phase */ 54 private $parseEndTarget; 55 56 /** Name of the current project */ 57 private $currentProjectName; 58 59 private $isParsing = true; 60 61 /** 62 * Indicates whether the project tag attributes are to be ignored 63 * when processing a particular build file. 64 */ 65 private $ignoreProjectTag = false; 66 48 67 /** 49 68 * Static call to ProjectConfigurator. Use this to configure a … … 72 91 $this->buildFile = new PhingFile($buildFile->getAbsolutePath()); 73 92 $this->buildFileParent = new PhingFile($this->buildFile->getParent()); 93 $this->currentTargets = array(); 94 $this->parseEndTarget = new Target(); 95 } 96 97 /** 98 * find out the build file 99 * @return the build file to which the xml context belongs 100 */ 101 public function getBuildFile() { 102 return $this->buildFile; 103 } 104 105 /** 106 * find out the parent build file of this build file 107 * @return the parent build file of this build file 108 */ 109 public function getBuildFileParent() { 110 return $this->buildFileParent; 111 } 112 113 /** 114 * find out the current project name 115 * @return current project name 116 */ 117 public function getCurrentProjectName() { 118 return $this->currentProjectName; 119 } 120 121 /** 122 * set the name of the current project 123 * @param name name of the current project 124 */ 125 public function setCurrentProjectName($name) { 126 $this->currentProjectName = $name; 127 } 128 129 /** 130 * tells whether the project tag is being ignored 131 * @return whether the project tag is being ignored 132 */ 133 public function isIgnoringProjectTag() { 134 return $this->ignoreProjectTag; 135 } 136 137 /** 138 * sets the flag to ignore the project tag 139 * @param flag to ignore the project tag 140 */ 141 public function setIgnoreProjectTag($flag) { 142 $this->ignoreProjectTag = $flag; 143 } 144 145 public function &getCurrentTargets () { 146 return $this->currentTargets; 147 } 148 149 public function isParsing () { 150 return $this->isParsing; 74 151 } 75 152 … … 83 160 */ 84 161 protected function parse() { 85 try { 162 try { 163 // get parse context 164 $ctx = $this->project->getReference("phing.parsing.context"); 165 if (null == $ctx) { 166 // make a new context and register it with project 167 $ctx = new PhingXMLContext($this->project); 168 $this->project->addReference("phing.parsing.context", $ctx); 169 } 170 171 //record this parse with context 172 $ctx->addImport($this->buildFile); 173 174 if (count($ctx->getImportStack()) > 1) { 175 // this is an imported file 176 // modify project tag parse behavior 177 $this->setIgnoreProjectTag(true); 178 } 179 // push action onto global stack 180 $ctx->startConfigure($this); 181 86 182 $reader = new BufferedReader(new FileReader($this->buildFile)); 87 183 $parser = new ExpatParser($reader); … … 91 187 $parser->parse(); 92 188 $reader->close(); 189 190 // mark parse phase as completed 191 $this->isParsing = false; 192 // execute delayed tasks 193 $this->parseEndTarget->main(); 194 // pop this action from the global stack 195 $ctx->endConfigure(); 93 196 } catch (Exception $exc) { 94 197 throw new BuildException("Error reading project file", $exc); 95 198 } 199 } 200 201 /** 202 * Delay execution of a task until after the current parse phase has 203 * completed. 204 * 205 * @param Task $task Task to execute after parse 206 */ 207 public function delayTaskUntilParseEnd ($task) { 208 $this->parseEndTarget->addTask($task); 96 209 } 97 210 branches/2.4/classes/phing/parser/ProjectHandler.php
r132 r368 90 90 } 91 91 } 92 if ($def === null) { 93 throw new ExpatParseException("The default attribute of project is required"); 92 // these things get done no matter what 93 if (null != $name) { 94 $canonicalName = self::canonicalName($name); 95 $this->configurator->setCurrentProjectName($canonicalName); 96 $project->setUserProperty("phing.file.{$canonicalName}", 97 (string) $this->configurator->getBuildFile()); 94 98 } 95 $project->setDefaultTarget($def);96 99 97 if ($name !== null) { 100 if (!$this->configurator->isIgnoringProjectTag()) { 101 if ($def === null) { 102 throw new ExpatParseException( 103 "The default attribute of project is required"); 104 } 105 $project->setDefaultTarget($def); 106 107 if ($name !== null) { 98 108 $project->setName($name); 99 109 $project->addReference($name, $project); 100 }101 110 102 if ($id !== null) { 111 } 112 113 if ($id !== null) { 103 114 $project->addReference($id, $project); 104 }105 106 if ($desc !== null) {115 } 116 117 if ($desc !== null) { 107 118 $project->setDescription($desc); 108 }119 } 109 120 110 if ($project->getProperty("project.basedir") !== null) {121 if ($project->getProperty("project.basedir") !== null) { 111 122 $project->setBasedir($project->getProperty("project.basedir")); 112 } else {123 } else { 113 124 if ($baseDir === null) { 114 $project->setBasedir($buildFileParent->getAbsolutePath());125 $project->setBasedir($buildFileParent->getAbsolutePath()); 115 126 } else { 116 // check whether the user has specified an absolute path117 $f = new PhingFile($baseDir);118 if ($f->isAbsolute()) {119 $project->setBasedir($baseDir);120 } else {121 $project->setBaseDir($project->resolveFile($baseDir, $buildFileParent));122 }127 // check whether the user has specified an absolute path 128 $f = new PhingFile($baseDir); 129 if ($f->isAbsolute()) { 130 $project->setBasedir($baseDir); 131 } else { 132 $project->setBaseDir($project->resolveFile($baseDir, $buildFileParent)); 133 } 123 134 } 135 } 124 136 } 125 137 } … … 150 162 } 151 163 } 164 165 static function canonicalName ($name) { 166 return preg_replace('/\W/', '_', strtolower($name)); 167 } 152 168 } 153 169 branches/2.4/classes/phing/parser/TargetHandler.php
r123 r368 109 109 $project = $this->configurator->project; 110 110 111 // check to see if this target is a dup within the same file 112 if (isset($this->configurator->getCurrentTargets[$name])) { 113 throw new BuildException("Duplicate target: $targetName", 114 $this->parser->getLocation()); 115 } 116 111 117 $this->target = new Target(); 112 118 $this->target->setName($name); … … 114 120 $this->target->setUnless($unlessCond); 115 121 $this->target->setDescription($description); 116 117 $project->addTarget($name, $this->target);118 119 if ($id !== null && $id !== "") {120 $project->addReference($id, $this->target);121 }122 122 // take care of dependencies 123 123 if (strlen($depends) > 0) { … … 125 125 } 126 126 127 $usedTarget = false; 128 // check to see if target with same name is already defined 129 $projectTargets = $project->getTargets(); 130 if (isset($projectTargets[$name])) { 131 $project->log("Already defined in main or a previous import, " . 132 "ignore {$name}", Project::MSG_VERBOSE); 133 } else { 134 $project->addTarget($name, $this->target); 135 if ($id !== null && $id !== "") { 136 $project->addReference($id, $this->target); 137 } 138 $usedTarget = true; 139 } 140 141 if ($this->configurator->isIgnoringProjectTag() && 142 $this->configurator->getCurrentProjectName() != null && 143 strlen($this->configurator->getCurrentProjectName()) != 0) { 144 // In an impored file (and not completely 145 // ignoring the project tag) 146 $newName = $this->configurator->getCurrentProjectName() . "." . $name; 147 if ($usedTarget) { 148 // clone needs to make target->children a shared reference 149 $newTarget = clone $this->target; 150 } else { 151 $newTarget = $this->target; 152 } 153 $newTarget->setName($newName); 154 $ct = $this->configurator->getCurrentTargets(); 155 $ct[$newName] = $newTarget; 156 $project->addTarget($newName, $newTarget); 157 } 127 158 } 128 159 branches/2.4/classes/phing/tasks/defaults.properties
r353 r368 41 41 if=phing.tasks.system.IfTask 42 42 warn=phing.tasks.system.WarnTask 43 import=phing.tasks.system.ImportTask 43 44 44 45 ; "Core" contributed tasks branches/2.4/docs/phing_guide/book/chapters/appendixes/AppendixB-CoreTasks.html
r367 r368 901 901 </if> 902 902 </pre> 903 904 <h2><a name="ImportTask"></a>ImportTask</h2> 905 <p>Imports another build file into the current project.</p> 906 <p>On execution it will read another Phing file into the same Project. 907 Functionally it is nearly the same as copy and pasting the imported file onto 908 the end of the importing file.</p> 909 <h3>Target Overriding</h3> 910 <p>If a target in the main file is also present in at least one of the imported files, the one from the main file takes precedence.</p> 911 912 <p>So if I import for example a <em>docs/build.xml</em> file named 913 <strong>builddocs</strong>, that contains a "<strong>docs</strong>" target, I 914 can redefine it in my main buildfile and that is the one that will be called. 915 This makes it easy to keep the same target name, so that the overriding target 916 is still called by any other targets--in either the main or imported 917 buildfile(s)--for which it is a dependency, with a different implementation. 918 The target from <em>docs/build.xml</em> is made available by the name 919 "<strong>builddocs.docs</strong>". This enables the new implementation to call 920 the old target, thus enhancing it with tasks called before or after it.</p> 921 <h3>Special Properties</h3> 922 <p>Imported files are treated as they are present in the main buildfile. This makes it easy to understand, but it makes it impossible for them to reference files and resources relative to their path. Because of this, for every imported file, Phing adds a property that contains the path to the imported buildfile. With this path, the imported buildfile can keep resources and be able to reference them relative to its position.</p> 923 924 <p>So if I import for example a <em>docs/build.xml</em> file named <strong>builddocs</strong>, I can get its path as <strong>phing.file.builddocs</strong>, similarly to the <strong>phing.file</strong> property of the main buildfile.</p> 925 926 <p>Note that "builddocs" is not the filename, but the name attribute present in the imported project tag.</p> 927 928 <p>If import file does not have a name attribute, the phing.file.projectname property will not be set.</p> 929 <h3>Resolving Files Against the Imported File</h3> 930 <p>Suppose your main build file called <code>importing.xml</code> imports a build file <code>imported.xml</code>, located anywhere on the file system, and <code>imported.xml</code> reads a set of properties from <code>imported.properties</code>: 931 <pre> 932 <!-- importing.xml --> 933 <project name="importing" basedir="." default="..."> 934 <import file="${path_to_imported}/imported.xml"/> 935 </project> 936 937 <!-- imported.xml --> 938 <project name="imported" basedir="." default="..."> 939 <property file="imported.properties"/> 940 </project> 941 </pre> 942 943 <p>This snippet however will resolve <code>imported.properties</code> against the basedir of <code>importing.xml</code>, because the basedir of <code>imported.xml</code> is ignored by Phing. The right way to use <code>imported.properties</code> is: 944 <pre> 945 <!-- imported.xml --> 946 <project name="imported" basedir="." default="..."> 947 <php function="dirname" returnProperty="imported.basedir"> 948 <param value="${phing.file.imported}"/> 949 </php> 950 <property file="${imported.basedir}/imported.properties"/> 951 </project> 952 </pre> 953 <p>As explained above <code>${phing.file.imported}</code> stores the path of the build script, that defines the project called <strong>imported</strong>, (in short it stores the path to <em>imported.xml</em>) and <php function="dirname"> takes its directory. This technique also allows <em>imported.xml</em> to be used as a standalone file (without being imported in other project).</p> 954 <h3>Example</h3> 955 <pre> 956 <import file="path/to/build.xml"/> 957 <import file="path/to/build.xml" optional="true"/> 958 </pre> 959 <h3>Attributes</h3> 960 <table> 961 <thead> 962 <tr> 963 <th>Name</th> 964 <th>Type</th> 965 <th>Description</th> 966 <th>Default</th> 967 <th>Required</th> 968 </tr> 969 </thead> 970 <tbody> 971 <tr> 972 <td>file</td> 973 <td>String</td> 974 <td>The file to import.</td> 975 <td>n/a</td> 976 <td>Yes</td> 977 </tr> 978 <tr> 979 <td>optional</td> 980 <td>Boolean</td> 981 <td>If true, do not stop the build if the file does not exist.</td> 982 <td>false</td> 983 <td>No</td> 984 </tr> 985 </tbody> 986 </table> 903 987 904 988 <h2><a name="IncludePathTask"></a>IncludePathTask</h2> branches/2.4/test/run-tests.php
r367 r368 72 72 73 73 74 include_once 'phing/tasks/ImportTaskTest.php'; 75 $tasksSuite->addTestSuite(new ReflectionClass('ImportTaskTest')); 74 76 75 77 $suite = new PHPUnit2_Framework_TestSuite('Phing Tests');
