Changeset 368

Show
Ignore:
Timestamp:
05/18/08 17:21:02 (8 months ago)
Author:
bender
Message:

#247 - New task: Import

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/2.4/classes/phing/Project.php

    r345 r368  
    536536            $this->log("  +User datatype: $typeName ($typeClass)", Project::MSG_DEBUG); 
    537537        } 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); 
    539539        } 
    540540    } 
     
    556556        $target->setProject($this); 
    557557        $this->targets[$targetName] = $target; 
     558 
     559        $ctx = $this->getReference("phing.parsing.context"); 
     560        $current = $ctx->getConfigurator()->getCurrentTargets(); 
     561        $current[$targetName] = $target; 
    558562    } 
    559563 
  • branches/2.4/classes/phing/parser/Location.php

    r123 r368  
    7070        return (string) $buf; 
    7171    } 
     72 
     73    function __toString () { 
     74      return $this->toString(); 
     75    } 
    7276} 
  • branches/2.4/classes/phing/parser/ProjectConfigurator.php

    r147 r368  
    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/** 
     
    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 
     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 
    4867    /** 
    4968     * Static call to ProjectConfigurator. Use this to configure a 
     
    7291        $this->buildFile = new PhingFile($buildFile->getAbsolutePath()); 
    7392        $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; 
    74151    } 
    75152 
     
    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); 
     
    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        } 
     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); 
    96209    } 
    97210 
  • branches/2.4/classes/phing/parser/ProjectHandler.php

    r132 r368  
    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    } 
     
    150162        } 
    151163    } 
     164 
     165    static function canonicalName ($name) { 
     166      return preg_replace('/\W/', '_', strtolower($name)); 
     167    } 
    152168} 
    153169 
  • branches/2.4/classes/phing/parser/TargetHandler.php

    r123 r368  
    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); 
     
    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) { 
     
    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 
  • branches/2.4/classes/phing/tasks/defaults.properties

    r353 r368  
    4141if=phing.tasks.system.IfTask 
    4242warn=phing.tasks.system.WarnTask 
     43import=phing.tasks.system.ImportTask 
    4344 
    4445; "Core" contributed tasks 
  • branches/2.4/docs/phing_guide/book/chapters/appendixes/AppendixB-CoreTasks.html

    r367 r368  
    901901</if> 
    902902</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.  
     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> 
    903987 
    904988<h2><a name="IncludePathTask"></a>IncludePathTask</h2> 
  • branches/2.4/test/run-tests.php

    r367 r368  
    7272 
    7373 
     74include_once 'phing/tasks/ImportTaskTest.php'; 
     75$tasksSuite->addTestSuite(new ReflectionClass('ImportTaskTest')); 
    7476  
    7577$suite = new PHPUnit2_Framework_TestSuite('Phing Tests');