Ticket #247: importtask.patch

File importtask.patch, 30.9 kB (added by Bryan Davis <bender@casadebender.com>, 4 months ago)
  • test/run-tests.php

    old new  
    6971$tasksSuite->addTestSuite(new ReflectionClass('PropertyTaskTest')); 
    7072 
    7173 
     74include_once 'phing/tasks/ImportTaskTest.php'; 
     75$tasksSuite->addTestSuite(new ReflectionClass('ImportTaskTest')); 
    7276  
    7377$suite = new PHPUnit2_Framework_TestSuite('Phing Tests'); 
    7478$suite->addTest($coreSuite); 
  • test/etc/tasks/imports/imported.xml

    old new  
     1<?xml version="1.0"?> 
     2<project name="imported" default="" basedir="."> 
     3 
     4  <php function="dirname" returnProperty="imported.basedir"> 
     5    <param value="${phing.file.imported}"/> 
     6  </php> 
     7  <property file="${imported.basedir}/imported.properties"/> 
     8 
     9  <target name="main"> 
     10    <echo>${imported.echo}</echo> 
     11    <echo>This is ${phing.file.imported} main target.</echo> 
     12  </target> 
     13 
     14  <target name="imported"> 
     15    <echo>phing.file.imported=${phing.file.imported}</echo> 
     16    <echo>imported.basedir=${imported.basedir}</echo> 
     17  </target> 
     18 
     19  <target name="flip" depends="flop"> 
     20    <echo>This is ${phing.file.imported} flip target.</echo> 
     21  </target> 
     22  <target name="flop"> 
     23    <echo>This is ${phing.file.imported} flop target.</echo> 
     24  </target> 
     25 
     26  <import file="imports/importedImport.xml"/> 
     27</project> 
  • test/etc/tasks/imports/importedImport.xml

    old new  
     1<?xml version="1.0"?> 
     2<project name="imported2" default="" basedir="."> 
     3 
     4  <target name="imported2"> 
     5    <echo>This is ${phing.file.imported2} imported2 target.</echo> 
     6  </target> 
     7 
     8  <target name="main"> 
     9    <echo>This is ${phing.file.imported2} main target.</echo> 
     10  </target> 
     11</project> 
     12 
  • test/etc/tasks/imports/imported.properties

    old new  
  • test/etc/tasks/importing.xml

    old new  
     1<?xml version="1.0"?> 
     2<project name="importing" default="" basedir="."> 
     3 
     4  <import file="imports/imported.xml"/> 
     5 
     6  <target name="main"> 
     7    <echo>This is ${phing.file} main target.</echo> 
     8  </target> 
     9 
     10  <target name="cascade" depends="imported.main"> 
     11    <echo>This is ${phing.file} cascade target.</echo> 
     12  </target> 
     13 
     14  <target name="flipflop" depends="flip"> 
     15    <echo>This is ${phing.file} flipflop target.</echo> 
     16  </target> 
     17  <target name="flop"> 
     18    <echo>This is ${phing.file} flop target.</echo> 
     19  </target> 
     20</project> 
  • test/classes/phing/tasks/ImportTaskTest.php

    old new  
     1<?php 
     2 
     3/* 
     4 *  $Id$ 
     5 * 
     6 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
     7 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
     8 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
     9 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
     10 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
     11 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
     12 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
     13 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
     14 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
     15 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
     16 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
     17 * 
     18 * This software consists of voluntary contributions made by many individuals 
     19 * and is licensed under the LGPL. For more information please see 
     20 * <http://phing.info>. 
     21 */ 
     22  
     23require_once 'phing/BuildFileTest.php'; 
     24 
     25/** 
     26 * @author Bryan Davis <bender@casadebender.com> 
     27 */ 
     28class ImportTaskTest extends BuildFileTest {  
     29         
     30    public function setUp() {  
     31        $this->configureProject(PHING_TEST_BASE . "/etc/tasks/importing.xml"); 
     32    } 
     33 
     34    public function testOverloadedTarget () { 
     35      $this->executeTarget("main"); 
     36      $this->assertInLogs("This is " . PHING_TEST_BASE . "/etc/tasks/importing.xml main target."); 
     37    } 
     38         
     39    public function testImportedTarget () { 
     40      $this->executeTarget("imported"); 
     41      $this->assertInLogs("phing.file.imported=" . PHING_TEST_BASE . "/etc/tasks/imports/imported.xml"); 
     42      $this->assertInLogs("imported.basedir=" . PHING_TEST_BASE . "/etc/tasks/imports"); 
     43    } 
     44 
     45    public function testImported2Target () { 
     46      $this->executeTarget("imported2"); 
     47      $this->assertInLogs("This is " . PHING_TEST_BASE . "/etc/tasks/imports/importedImport.xml imported2 target."); 
     48    } 
     49         
     50    public function testCascadeTarget () { 
     51      $this->executeTarget("cascade"); 
     52      $this->assertInLogs("This comes from the imported.properties file"); 
     53      $this->assertInLogs("This is " . PHING_TEST_BASE . "/etc/tasks/imports/imported.xml main target."); 
     54      $this->assertInLogs("This is " . PHING_TEST_BASE . "/etc/tasks/importing.xml cascade target."); 
     55    } 
     56 
     57    public function testFlipFlopTarget () { 
     58      // calls target in main that depends on target in import that depends on  
     59      // target orverridden in main 
     60      $this->executeTarget("flipflop"); 
     61      $this->assertInLogs("This is " . PHING_TEST_BASE . "/etc/tasks/importing.xml flop target."); 
     62      $this->assertInLogs("This is " . PHING_TEST_BASE . "/etc/tasks/imports/imported.xml flip target."); 
     63      $this->assertInLogs("This is " . PHING_TEST_BASE . "/etc/tasks/importing.xml flipflop target."); 
     64 
     65    } 
     66} 
  • docs/phing_guide/book/chapters/appendixes/AppendixB-CoreTasks.html

    old new  
    901901&lt;/if&gt; 
    902902</pre> 
    903903 
     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.  
     907Functionally it is nearly the same as copy and pasting the imported file onto  
     908the 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  
     914can redefine it in my main buildfile and that is the one that will be called.  
     915This makes it easy to keep the same target name, so that the overriding target  
     916is still called by any other targets--in either the main or imported  
     917buildfile(s)--for which it is a dependency, with a different implementation.  
     918The 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  
     920the 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&lt;!-- importing.xml --&gt; 
     933&lt;project name=&quot;importing&quot; basedir=&quot;.&quot; default=&quot;...&quot;&gt; 
     934  &lt;import file=&quot;${path_to_imported}/imported.xml&quot;/&gt; 
     935&lt;/project&gt; 
     936 
     937&lt;!-- imported.xml --&gt; 
     938&lt;project name=&quot;imported&quot; basedir=&quot;.&quot; default=&quot;...&quot;&gt; 
     939  &lt;property file=&quot;imported.properties&quot;/&gt; 
     940&lt;/project&gt; 
     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&lt;!-- imported.xml --&gt; 
     946&lt;project name=&quot;imported&quot; basedir=&quot;.&quot; default=&quot;...&quot;&gt; 
     947  &lt;php function=&quot;dirname&quot; returnProperty=&quot;imported.basedir&quot;&gt; 
     948    &lt;param value=&quot;${phing.file.imported}&quot;/&gt; 
     949  &lt;/php&gt; 
     950  &lt;property file=&quot;${imported.basedir}/imported.properties&quot;/&gt; 
     951&lt;/project&gt; 
     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 &lt;php function="dirname"&gt; 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&lt;import file=&quot;path/to/build.xml&quot;/&gt; 
     957&lt;import file=&quot;path/to/build.xml&quot; optional=&quot;true&quot;/&gt; 
     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> 
     987 
    904988<h2><a name="IncludePathTask"></a>IncludePathTask</h2> 
    905989<p>Sets the PHP include_path configuration option for the duration of this phing run.</p> 
    906990<h3>Example</h3> 
  • classes/phing/tasks/defaults.properties

    old new  
    4040xslt=phing.tasks.system.XsltTask 
    4141if=phing.tasks.system.IfTask 
    4242warn=phing.tasks.system.WarnTask 
     43import=phing.tasks.system.ImportTask 
    4344 
    4445; "Core" contributed tasks 
    4546; -- i.e. no taskdef needed. 
  • classes/phing/tasks/system/ImportTask.php

    old new  
     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 
     22require_once 'phing/Task.php'; 
     23require_once 'phing/system/io/FileSystem.php'; 
     24require_once 'phing/system/io/PhingFile.php'; 
     25require_once 'phing/parser/ProjectConfigurator.php'; 
     26 
     27/** 
     28 * Imports another build file into the current project. 
     29 * 
     30 * Targets and properties of the imported file can be overrridden  
     31 * by targets and properties of the same name declared in the importing file.  
     32 * 
     33 * The imported file will have a new synthetic property of  
     34 * "phing.file.<projectname>" declared which gives the full path to the  
     35 * imported file. Additionally each target in the imported file will be  
     36 * declared twice: once with the normal name and once with "<projectname>."  
     37 * prepended. The "<projectname>.<targetname>" synthetic targets allow the  
     38 * importing file a mechanism to call the imported files targets as  
     39 * dependencies or via the <phing> or <phingcall> task mechanisms. 
     40 * 
     41 * @author Bryan Davis <bender@casadebender.com> 
     42 * @version $Revision$ 
     43 * @package phing.tasks.system 
     44 */ 
     45class ImportTask extends Task { 
     46 
     47  /** 
     48   * @var FileSystem 
     49   */ 
     50  protected $fs; 
     51 
     52  /** 
     53   * @var PhingFile 
     54   */ 
     55  protected $file = null; 
     56 
     57  /** 
     58   * @var bool 
     59   */ 
     60  protected $optional = false; 
     61 
     62  /** 
     63   * Initialize task. 
     64   * @return void 
     65   */ 
     66  public function init () { 
     67    $this->fs = FileSystem::getFileSystem(); 
     68  } //end init 
     69 
     70 
     71  /** 
     72   * Set the file to import. 
     73   * @param string $f Path to file 
     74   * @return void 
     75   */ 
     76  public function setFile ($f) { 
     77    $this->file = $f; 
     78  } 
     79 
     80  /** 
     81   * Is this include optional? 
     82   * @param bool $opt If true, do not stop the build if the file does not  
     83   * exist 
     84   * @return void 
     85   */ 
     86  public function setOptional ($opt) { 
     87    $this->optional = $opt; 
     88  } 
     89 
     90  /** 
     91   * Parse a Phing build file and copy the properties, tasks, data types and  
     92   * targets it defines into the current project. 
     93   * 
     94   * @return void 
     95   */ 
     96  public function main () { 
     97    if (!isset($this->file)) { 
     98      throw new BuildException("Missing attribute 'file'"); 
     99    } 
     100 
     101    $base = $this->project->getBasedir(); 
     102    $file = new PhingFile($this->project->getBasedir(), $this->file); 
     103    if (!$file->exists()) { 
     104      $msg = "Unable to find build file: {$this->file->getName()}"; 
     105      if ($this->optional) { 
     106        $this->log($msg . '... skipped'); 
     107      } else { 
     108        throw new BuildException($msg); 
     109      } 
     110    } 
     111 
     112    $ctx = $this->project->getReference("phing.parsing.context"); 
     113    $cfg = $ctx->getConfigurator(); 
     114    if (null !== $cfg && $cfg->isParsing()) { 
     115      // because there isn't a top level implicit target in phing like there is  
     116      // in Ant 1.6, we will be called as soon as our xml is parsed. This isn't  
     117      // really what we want to have happen. Instead we will register ourself  
     118      // with the parse context to be called at the end of the current file's  
     119      // parse phase. 
     120      $cfg->delayTaskUntilParseEnd($this); 
     121 
     122    } else { 
     123      // Import xml file into current project scope 
     124      // Since this is delayed until after the importing file has been  
     125      // processed, the properties and targets of this new file may not take  
     126      // effect if they have alreday been defined in the outer scope. 
     127      $this->log("Importing configuration from {$file->getName()}", Project::MSG_VERBOSE); 
     128      ProjectConfigurator::configureProject($this->project, $file); 
     129      $this->log("Configuration imported.", Project::MSG_VERBOSE); 
     130    } 
     131  } //end main 
     132 
     133} //end ImportTask 
  • classes/phing/parser/ProjectConfigurator.php

    old new  
    2424include_once 'phing/BuildException.php'; 
    2525include_once 'phing/system/lang/FileNotFoundException.php'; 
    2626include_once 'phing/system/io/PhingFile.php'; 
     27include_once 'phing/parser/PhingXMLContext.php'; 
     28include_once 'phing/IntrospectionHelper.php'; 
    2729 
    2830/** 
    2931 * The datatype handler class. 
     
    4446     
    4547    public $buildFile; 
    4648    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 
    4861    /** 
     62     * Indicates whether the project tag attributes are to be ignored 
     63     * when processing a particular build file. 
     64     */ 
     65    private $ignoreProjectTag = false; 
     66 
     67    /** 
    4968     * Static call to ProjectConfigurator. Use this to configure a 
    5069     * project. Do not use the new operator. 
    5170     * 
     
    7190        $this->project = $project; 
    7291        $this->buildFile = new PhingFile($buildFile->getAbsolutePath()); 
    7392        $this->buildFileParent = new PhingFile($this->buildFile->getParent()); 
     93        $this->currentTargets = array(); 
     94        $this->parseEndTarget = new Target(); 
    7495    } 
    7596 
    7697    /** 
     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; 
     151    } 
     152 
     153    /** 
    77154     * Creates the ExpatParser, sets root handler and kick off parsing 
    78155     * process. 
    79156     * 
     
    82159     * @access private 
    83160     */ 
    84161    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 
    86182            $reader = new BufferedReader(new FileReader($this->buildFile)); 
    87183            $parser = new ExpatParser($reader); 
    88184            $parser->parserSetOption(XML_OPTION_CASE_FOLDING,0); 
     
    90186            $this->project->log("parsing buildfile ".$this->buildFile->getName(), Project::MSG_VERBOSE); 
    91187            $parser->parse(); 
    92188            $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(); 
    93196        } catch (Exception $exc) { 
    94197            throw new BuildException("Error reading project file", $exc); 
    95198        } 
    96199    } 
    97200 
    98201    /** 
     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); 
     209    } 
     210 
     211    /** 
    99212     * Configures an element and resolves eventually given properties. 
    100213     * 
    101214     * @param  object  the element to configure 
  • classes/phing/parser/PhingXMLContext.php

    old new  
     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/** 
     23 * Track the current state of the Xml parse operation. 
     24 * 
     25 * @author    Bryan Davis <bender@casadebender.com> 
     26 * @version   $Revision$ $Date$ 
     27 * @access    public 
     28 * @package   phing.parser 
     29 */ 
     30class PhingXMLContext { 
     31 
     32    /** 
     33     * Constructor 
     34     * @param $project the project to which this antxml context belongs to 
     35     */ 
     36    public function __construct ($project) { 
     37      $this->project = $project; 
     38    } 
     39 
     40    /** The project to configure. */ 
     41    private $project; 
     42 
     43    private $configurators = array(); 
     44 
     45    public function startConfigure ($cfg) { 
     46      $this->configurators[] = $cfg; 
     47    } 
     48 
     49    public function endConfigure () { 
     50      array_pop($this->configurators); 
     51    } 
     52 
     53    public function getConfigurator () { 
     54      $l = count($this->configurators); 
     55      if (0 == $l) { 
     56        return null; 
     57      } else { 
     58        return $this->configurators[$l - 1]; 
     59      } 
     60    } 
     61 
     62    /** Impoerted files */ 
     63    private $importStack = array(); 
     64 
     65    public function addImport ($file) { 
     66      $this->importStack[] = $file; 
     67    } 
     68 
     69    public function getImportStack () { 
     70      return $this->importStack; 
     71    } 
     72 
     73    /** 
     74     * find out the project to which this context belongs 
     75     * @return project 
     76     */ 
     77    public function getProject() { 
     78        return $this->project; 
     79    } 
     80 
     81} //end PhingXMLContext 
  • classes/phing/parser/ProjectHandler.php

    old new  
    8989                throw new ExpatParseException("Unexpected attribute '$key'"); 
    9090            } 
    9191        } 
    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()); 
    9498        } 
    95         $project->setDefaultTarget($def); 
    9699 
    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) { 
    98108            $project->setName($name); 
    99109            $project->addReference($name, $project); 
    100         } 
    101110 
    102         if ($id !== null) { 
     111          } 
     112 
     113          if ($id !== null) { 
    103114            $project->addReference($id, $project); 
    104        
    105          
    106         if ($desc !== null) { 
     115         
     116 
     117          if ($desc !== null) { 
    107118            $project->setDescription($desc); 
    108         }         
     119          }         
    109120 
    110         if ($project->getProperty("project.basedir") !== null) { 
     121          if ($project->getProperty("project.basedir") !== null) { 
    111122            $project->setBasedir($project->getProperty("project.basedir")); 
    112         } else { 
     123          } else { 
    113124            if ($baseDir === null) { 
    114                 $project->setBasedir($buildFileParent->getAbsolutePath()); 
     125              $project->setBasedir($buildFileParent->getAbsolutePath()); 
    115126            } else { 
    116                 // check whether the user has specified an absolute path 
    117                 $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             
    123134            } 
     135          } 
    124136        } 
    125137    } 
    126138 
     
    149161                        $tf->init($name, $attrs); 
    150162        } 
    151163    } 
     164 
     165    static function canonicalName ($name) { 
     166      return preg_replace('/\W/', '_', strtolower($name)); 
     167    } 
    152168} 
    153169 
  • classes/phing/parser/TargetHandler.php

    old new  
    108108        // shorthand 
    109109        $project = $this->configurator->project; 
    110110 
     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 
    111117        $this->target = new Target(); 
    112118        $this->target->setName($name); 
    113119        $this->target->setIf($ifCond); 
    114120        $this->target->setUnless($unlessCond); 
    115121        $this->target->setDescription($description); 
    116  
    117         $project->addTarget($name, $this->target); 
    118  
    119         if ($id !== null && $id !== "") { 
    120             $project->addReference($id, $this->target); 
    121         } 
    122122        // take care of dependencies 
    123123        if (strlen($depends) > 0) { 
    124124            $this->target->setDepends($depends); 
    125125        } 
    126126 
     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        } 
    127158    } 
    128159 
    129160    /** 
  • classes/phing/parser/Location.php

    old new  
    6969        } 
    7070        return (string) $buf; 
    7171    } 
     72 
     73    function __toString () { 
     74      return $this->toString(); 
     75    } 
    7276} 
  • classes/phing/Project.php

    old new  
    535538            $this->typedefs[$typeName] = $typeClass; 
    536539            $this->log("  +User datatype: $typeName ($typeClass)", Project::MSG_DEBUG); 
    537540        } else { 
    538             $this->log("Type $name ($class) already registerd, skipping", Project::MSG_VERBOSE); 
     541            $this->log("Type $typeName ($typeClass) already registerd, skipping", Project::MSG_VERBOSE); 
    539542        } 
    540543    } 
    541544 
     
    555558        $this->log("  +Target: $targetName", Project::MSG_DEBUG); 
    556559        $target->setProject($this); 
    557560        $this->targets[$targetName] = $target; 
     561 
     562        $ctx = $this->getReference("phing.parsing.context"); 
     563        $current = $ctx->getConfigurator()->getCurrentTargets(); 
     564        $current[$targetName] = $target; 
    558565    } 
    559566 
    560567    function getTargets() {