home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / phing / tasks / system / PhingTask.php < prev    next >
Encoding:
PHP Script  |  2007-03-14  |  20.4 KB  |  624 lines

  1. <?php
  2.  
  3. /*
  4.  *  $Id: PhingTask.php 175 2007-03-14 13:52:03Z hans $  
  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.  
  23. include_once 'phing/Task.php';
  24. include_once 'phing/util/FileUtils.php';
  25. include_once 'phing/types/Reference.php';
  26. include_once 'phing/tasks/system/PropertyTask.php';
  27.  
  28. /**
  29.  * Task that invokes phing on another build file.
  30.  * 
  31.  * Use this task, for example, if you have nested buildfiles in your project. Unlike
  32.  * AntTask, PhingTask can even support filesets:
  33.  * 
  34.  * <pre>
  35.  *   <phing>
  36.  *    <fileset dir="${srcdir}">
  37.  *      <include name="** /build.xml" /> <!-- space added after ** is there because of PHP comment syntax -->
  38.  *      <exclude name="build.xml" />
  39.  *    </fileset>
  40.  *   </phing>
  41.  * </pre>
  42.  * 
  43.  * @author    Hans Lellelid <hans@xmpl.org>
  44.  * @version   $Revision: 1.20 $
  45.  * @package   phing.tasks.system
  46.  */
  47. class PhingTask extends Task {
  48.  
  49.     /** the basedir where is executed the build file */
  50.     private $dir;
  51.     
  52.     /** build.xml (can be absolute) in this case dir will be ignored */
  53.     private $phingFile;
  54.     
  55.     /** the target to call if any */
  56.     protected $newTarget;
  57.     
  58.     /** should we inherit properties from the parent ? */
  59.     private $inheritAll = true;
  60.     
  61.     /** should we inherit references from the parent ? */
  62.     private $inheritRefs = false;
  63.  
  64.     /** the properties to pass to the new project */
  65.     private $properties = array();
  66.  
  67.     /** the references to pass to the new project */
  68.     private $references = array();
  69.  
  70.     /** The filesets that contain the files PhingTask is to be run on. */
  71.     private $filesets = array();
  72.  
  73.     /** the temporary project created to run the build file */
  74.     private $newProject;
  75.  
  76.     /** Fail the build process when the called build fails? */
  77.     private $haltOnFailure = false;
  78.  
  79.     /**
  80.      *  If true, abort the build process if there is a problem with or in the target build file.
  81.      *  Defaults to false.
  82.      *
  83.      *  @param boolean new value
  84.      */
  85.     public function setHaltOnFailure($hof) {
  86.         $this->haltOnFailure = (boolean) $hof;
  87.     }
  88.  
  89.     /**
  90.      * Creates a Project instance for the project to call.
  91.      * @return void
  92.      */
  93.     public function init() {
  94.         $this->newProject = new Project();
  95.         $tdf = $this->project->getTaskDefinitions();
  96.         $this->newProject->addTaskDefinition("property", $tdf["property"]);
  97.     }
  98.  
  99.     /**
  100.      * Called in execute or createProperty if newProject is null.
  101.      *
  102.      * <p>This can happen if the same instance of this task is run
  103.      * twice as newProject is set to null at the end of execute (to
  104.      * save memory and help the GC).</p>
  105.      *
  106.      * <p>Sets all properties that have been defined as nested
  107.      * property elements.</p>
  108.      */
  109.     private function reinit() {
  110.         $this->init();
  111.         $count = count($this->properties);
  112.         for ($i = 0; $i < $count; $i++) {
  113.             $p = $this->properties[$i];
  114.             $newP = $this->newProject->createTask("property");
  115.             $newP->setName($p->getName());
  116.             if ($p->getValue() !== null) {
  117.                 $newP->setValue($p->getValue());
  118.             }
  119.             if ($p->getFile() !== null) {
  120.                 $newP->setFile($p->getFile());
  121.             }            
  122.             if ($p->getPrefix() !== null) {
  123.                 $newP->setPrefix($p->getPrefix());
  124.             }
  125.             if ($p->getRefid() !== null) {
  126.                 $newP->setRefid($p->getRefid());
  127.             }
  128.             if ($p->getEnvironment() !== null) {
  129.                 $newP->setEnvironment($p->getEnvironment());
  130.             }
  131.             if ($p->getUserProperty() !== null) {
  132.                 $newP->setUserProperty($p->getUserProperty());
  133.             }
  134.             if ($p->getOverride() !== null) {
  135.                 $newP->setOverride($p->getOverride());
  136.             }
  137.             $this->properties[$i] = $newP;
  138.         }
  139.     }
  140.  
  141.     /**
  142.      * Main entry point for the task.
  143.      *
  144.      * @return void
  145.      */
  146.     public function main() {
  147.     
  148.         // Call Phing on the file set with the attribute "phingfile"
  149.         if ($this->phingFile !== null or $this->dir !== null) {
  150.             $this->processFile();
  151.         }
  152.  
  153.         // if no filesets are given stop here; else process filesets
  154.         if (empty($this->filesets)) { 
  155.             return;
  156.         }
  157.         
  158.         // preserve old settings
  159.         $savedDir = $this->dir;
  160.         $savedPhingFile = $this->phingFile;
  161.         $savedTarget = $this->newTarget;
  162.         $buildFailed = false;
  163.  
  164.         // set no specific target for files in filesets
  165.         // [HL] I'm commenting this out; I don't know why this should not be supported!
  166.         // $this->newTarget = null;
  167.         
  168.         foreach($this->filesets as $fs) {
  169.  
  170.             $ds = $fs->getDirectoryScanner($this->project);
  171.  
  172.             $fromDir  = $fs->getDir($this->project);
  173.             $srcFiles = $ds->getIncludedFiles();
  174.  
  175.             foreach($srcFiles as $fname) {            
  176.                 $f = new PhingFile($ds->getbasedir(), $fname);
  177.                 $f = $f->getAbsoluteFile();
  178.                 $this->phingFile = $f->getAbsolutePath();
  179.                 $this->dir = $f->getParentFile();
  180.                 $this->processFile();    // run Phing!
  181.             }
  182.         }        
  183.         
  184.         // side effect free programming ;-)
  185.         $this->dir = $savedDir;        
  186.         $this->phingFile = $savedPhingFile;
  187.         $this->newTarget = $savedTarget;
  188.         
  189.         // [HL] change back to correct dir
  190.         if ($this->dir !== null) {
  191.             chdir($this->dir->getAbsolutePath());
  192.         }
  193.         
  194.     }
  195.     
  196.     /**
  197.      * Execute phing file.
  198.      * 
  199.      * @return void
  200.      */
  201.     private function processFile()  {
  202.             
  203.         $savedDir = $this->dir;
  204.         $savedPhingFile = $this->phingFile;
  205.         $savedTarget = $this->newTarget;
  206.         
  207.         $savedBasedirAbsPath = null; // this is used to save the basedir *if* we change it
  208.         
  209.         try {
  210.         
  211.             if ($this->newProject === null) {
  212.                 $this->reinit();
  213.             }
  214.  
  215.             $this->initializeProject();
  216.             
  217.             if ($this->dir !== null) {
  218.                 
  219.                 $dirAbsPath = $this->dir->getAbsolutePath();
  220.                 
  221.                 // BE CAREFUL! -- when the basedir is changed for a project,
  222.                 // all calls to getAbsolutePath() on a relative-path dir will
  223.                 // be made relative to the project's basedir!  This means
  224.                 // that subsequent calls to $this->dir->getAbsolutePath() will be WRONG!
  225.                 
  226.                 // We need to save the current project's basedir first.
  227.                 $savedBasedirAbsPath = $this->getProject()->getBasedir()->getAbsolutePath();
  228.                  
  229.                 $this->newProject->setBasedir($this->dir);
  230.                 
  231.                 // Now we must reset $this->dir so that it continues to resolve to the same
  232.                 // path.
  233.                 $this->dir = new PhingFile($dirAbsPath);
  234.                 
  235.                 if ($savedDir !== null) { // has been set explicitly
  236.                     $this->newProject->setInheritedProperty("project.basedir", $this->dir->getAbsolutePath());
  237.                 }
  238.                 
  239.             } else {
  240.                 
  241.                 // Since we're not changing the basedir here (for file resolution),
  242.                 // we don't need to worry about any side-effects in this scanrio.
  243.                 $this->dir = $this->getProject()->getBasedir();   
  244.             }
  245.  
  246.             $this->overrideProperties();
  247.             if ($this->phingFile === null) {
  248.                 $this->phingFile = "build.xml";
  249.             }
  250.             
  251.             $fu = new FileUtils();
  252.             $file = $fu->resolveFile($this->dir, $this->phingFile);
  253.             $this->phingFile = $file->getAbsolutePath();
  254.             
  255.             $this->log("Calling Buildfile '" . $this->phingFile . "' with target '" . $this->newTarget . "'");
  256.                         
  257.             $this->newProject->setUserProperty("phing.file", $this->phingFile);
  258.                        
  259.             ProjectConfigurator::configureProject($this->newProject, new PhingFile($this->phingFile));
  260.  
  261.             if ($this->newTarget === null) {
  262.                 $this->newTarget = $this->newProject->getDefaultTarget();
  263.             }
  264.  
  265.             // Are we trying to call the target in which we are defined?
  266.             if ($this->newProject->getBaseDir() == $this->project->getBaseDir() &&
  267.                 $this->newProject->getProperty("phing.file") == $this->project->getProperty("phing.file") &&
  268.                 $this->getOwningTarget() !== null &&
  269.                 $this->newTarget == $this->getOwningTarget()->getName()) {
  270.  
  271.                 throw new BuildException("phing task calling its own parent target");
  272.             }
  273.  
  274.             $this->addReferences();
  275.             $this->newProject->executeTarget($this->newTarget);
  276.             
  277.         } catch (Exception $e) {
  278.             $buildFailed = true;
  279.             $this->log($e->getMessage(), Project::MSG_ERR);
  280.             if (Phing::getMsgOutputLevel() <= Project::MSG_DEBUG) { 
  281.                 $lines = explode("\n", $e->getTraceAsString());
  282.                 foreach($lines as $line) {
  283.                     $this->log($line, Project::MSG_DEBUG);
  284.                 }
  285.             }
  286.             // important!!! continue on to perform cleanup tasks.    
  287.         }
  288.         
  289.         
  290.         // reset environment values to prevent side-effects.
  291.         
  292.         $this->newProject = null;
  293.         $pkeys = array_keys($this->properties);
  294.         foreach($pkeys as $k) {
  295.             $this->properties[$k]->setProject(null);
  296.         }
  297.         
  298.         $this->dir = $savedDir;        
  299.         $this->phingFile = $savedPhingFile;
  300.         $this->newTarget = $savedTarget;
  301.         
  302.         // If the basedir for any project was changed, we need to set that back here.
  303.         if ($savedBasedirAbsPath !== null) {
  304.             chdir($savedBasedirAbsPath);
  305.         }
  306.  
  307.         if ($this->haltOnFailure && $buildFailed) {
  308.             throw new BuildException("Execution of the target buildfile failed. Aborting.");
  309.         }
  310.     }
  311.  
  312.     /**
  313.      * Configure the Project, i.e. make intance, attach build listeners
  314.      * (copy from father project), add Task and Datatype definitions,
  315.      * copy properties and references from old project if these options
  316.      * are set via the attributes of the XML tag.
  317.      *
  318.      * Developer note:
  319.      * This function replaces the old methods "init", "_reinit" and 
  320.      * "_initializeProject".
  321.      *
  322.      * @access      protected
  323.      */
  324.     private function initializeProject() {
  325.         
  326.         $this->newProject->setInputHandler($this->project->getInputHandler());
  327.         
  328.         foreach($this->project->getBuildListeners() as $listener) {
  329.             $this->newProject->addBuildListener($listener);
  330.         }
  331.         
  332.         /* Copy things from old project. Datatypes and Tasks are always
  333.          * copied, properties and references only if specified so/not
  334.          * specified otherwise in the XML definition.
  335.          */
  336.         // Add Datatype definitions
  337.         foreach ($this->project->getDataTypeDefinitions() as $typeName => $typeClass) {
  338.             $this->newProject->addDataTypeDefinition($typeName, $typeClass);
  339.         }
  340.         
  341.         // Add Task definitions
  342.         foreach ($this->project->getTaskDefinitions() as $taskName => $taskClass) {
  343.             if ($taskClass == "propertytask") {
  344.                 // we have already added this taskdef in init()
  345.                 continue;
  346.             }
  347.             $this->newProject->addTaskDefinition($taskName, $taskClass);
  348.         }
  349.  
  350.         // set user-defined properties
  351.         $this->project->copyUserProperties($this->newProject);
  352.  
  353.         if (!$this->inheritAll) {
  354.            // set System built-in properties separately,
  355.            // b/c we won't inherit them.
  356.            $this->newProject->setSystemProperties();
  357.  
  358.         } else {
  359.             // set all properties from calling project
  360.             $properties = $this->project->getProperties();
  361.             foreach ($properties as $name => $value) {                
  362.                 if ($name == "basedir" || $name == "phing.file" || $name == "phing.version") {
  363.                     // basedir and phing.file get special treatment in main()
  364.                     continue;
  365.                 }
  366.                    // don't re-set user properties, avoid the warning message
  367.                 if ($this->newProject->getProperty($name) === null){
  368.                     // no user property
  369.                     $this->newProject->setNewProperty($name, $value);
  370.                 }
  371.             }
  372.             
  373.         }
  374.     
  375.     }
  376.  
  377.     /**
  378.      * Override the properties in the new project with the one
  379.      * explicitly defined as nested elements here.
  380.      * @return void
  381.      * @throws BuildException 
  382.      */
  383.     private function overrideProperties() {     
  384.         foreach(array_keys($this->properties) as $i) {
  385.             $p = $this->properties[$i];
  386.             $p->setProject($this->newProject);
  387.             $p->main();
  388.         }
  389.         $this->project->copyInheritedProperties($this->newProject);
  390.     }
  391.  
  392.     /**
  393.      * Add the references explicitly defined as nested elements to the
  394.      * new project.  Also copy over all references that don't override
  395.      * existing references in the new project if inheritrefs has been
  396.      * requested.
  397.      * 
  398.      * @return void
  399.      * @throws BuildException 
  400.      */
  401.     private function addReferences() {
  402.     
  403.         // parent project references
  404.         $projReferences = $this->project->getReferences();
  405.         
  406.         $newReferences = $this->newProject->getReferences();
  407.         
  408.         $subprojRefKeys = array();
  409.         
  410.         if (count($this->references) > 0) {
  411.             for ($i=0, $count=count($this->references); $i < $count; $i++) {
  412.                 $ref = $this->references[$i];            
  413.                 $refid = $ref->getRefId();
  414.                 
  415.                 if ($refid === null) {
  416.                     throw new BuildException("the refid attribute is required"
  417.                                              . " for reference elements");
  418.                 }
  419.                 if (!isset($projReferences[$refid])) {
  420.                     $this->log("Parent project doesn't contain any reference '"
  421.                         . $refid . "'",
  422.                         Project::MSG_WARN);
  423.                     continue;
  424.                 }
  425.                 
  426.                 $subprojRefKeys[] = $refid;
  427.                 //thisReferences.remove(refid);
  428.                 $toRefid = $ref->getToRefid();
  429.                 if ($toRefid === null) {
  430.                     $toRefid = $refid;
  431.                 }
  432.                 $this->copyReference($refid, $toRefid);
  433.             }
  434.         }
  435.  
  436.         // Now add all references that are not defined in the
  437.         // subproject, if inheritRefs is true
  438.         if ($this->inheritRefs) {
  439.         
  440.             // get the keys that are were not used by the subproject
  441.             $unusedRefKeys = array_diff(array_keys($projReferences), $subprojRefKeys);
  442.             
  443.             foreach($unusedRefKeys as $key) {
  444.                 if (isset($newReferences[$key])) {
  445.                     continue;
  446.                 }
  447.                 $this->copyReference($key, $key);
  448.             }
  449.         }
  450.     }
  451.  
  452.     /**
  453.      * Try to clone and reconfigure the object referenced by oldkey in
  454.      * the parent project and add it to the new project with the key
  455.      * newkey.
  456.      *
  457.      * <p>If we cannot clone it, copy the referenced object itself and
  458.      * keep our fingers crossed.</p>
  459.      *
  460.      * @param string $oldKey
  461.      * @param string $newKey
  462.      * @return void
  463.      */
  464.     private function copyReference($oldKey, $newKey) {
  465.         $orig = $this->project->getReference($oldKey);
  466.         if ($orig === null) {
  467.             $this->log("No object referenced by " . $oldKey . ". Can't copy to " 
  468.                 .$newKey, 
  469.                 PROJECT_SG_WARN);
  470.             return;
  471.         }
  472.  
  473.         $copy = clone $orig;
  474.  
  475.         if ($copy instanceof ProjectComponent) {
  476.             $copy->setProject($this->newProject);
  477.         } elseif (in_array('setProject', get_class_methods(get_class($copy)))) {
  478.             $copy->setProject($this->newProject);
  479.         } elseif ($copy instanceof Project) {
  480.             // don't copy the old "Project" itself
  481.         } else {
  482.             $msg = "Error setting new project instance for "
  483.                 . "reference with id " . $oldKey;
  484.             throw new BuildException($msg);
  485.         }
  486.         
  487.         $this->newProject->addReference($newKey, $copy);
  488.     }
  489.  
  490.     /**
  491.      * If true, pass all properties to the new phing project.
  492.      * Defaults to true.
  493.      *
  494.      * @access      public
  495.      */
  496.     function setInheritAll($value) {
  497.         $this->inheritAll = (boolean) $value;
  498.     }
  499.  
  500.     /**
  501.      * If true, pass all references to the new phing project.
  502.      * Defaults to false.
  503.      *
  504.      * @access      public
  505.      */
  506.     function setInheritRefs($value) {
  507.         $this->inheritRefs = (boolean)$value;
  508.     }
  509.  
  510.     /**
  511.      * The directory to use as a base directory for the new phing project.
  512.      * Defaults to the current project's basedir, unless inheritall
  513.      * has been set to false, in which case it doesn't have a default
  514.      * value. This will override the basedir setting of the called project.
  515.      *
  516.      * @access      public
  517.      */
  518.     function setDir($d) {
  519.         if ( is_string($d) )
  520.             $this->dir = new PhingFile($d);
  521.         else
  522.             $this->dir = $d;
  523.     }
  524.  
  525.     /**
  526.      * The build file to use.
  527.      * Defaults to "build.xml". This file is expected to be a filename relative
  528.      * to the dir attribute given.
  529.      *
  530.      * @access      public
  531.      */
  532.     function setPhingfile($s) {
  533.         // it is a string and not a file to handle relative/absolute
  534.         // otherwise a relative file will be resolved based on the current
  535.         // basedir.
  536.         $this->phingFile = $s;
  537.     }
  538.  
  539.    /**
  540.     * Alias function for setPhingfile
  541.     *
  542.     * @access       public
  543.     */
  544.     function setBuildfile($s) {
  545.         $this->setPhingFile($s);
  546.     }
  547.  
  548.     /**
  549.      * The target of the new Phing project to execute.
  550.      * Defaults to the new project's default target.
  551.      *
  552.      * @access      public
  553.      */
  554.     function setTarget($s) {
  555.         $this->newTarget = $s;
  556.     }
  557.  
  558.     /**
  559.      * Support for filesets; This method returns a reference to an instance
  560.      * of a FileSet object.
  561.      *
  562.      * @return FileSet
  563.      */
  564.     function createFileSet() {
  565.         $num = array_push($this->filesets, new FileSet());
  566.         return $this->filesets[$num-1];
  567.     }
  568.  
  569.     /**
  570.      * Property to pass to the new project.
  571.      * The property is passed as a 'user property'
  572.      *
  573.      * @access      public
  574.      */
  575.     function createProperty() {
  576.         $p = new PropertyTask();
  577.         $p->setFallback($this->newProject);
  578.         $p->setUserProperty(true);
  579.         $this->properties[] = $p;
  580.         return $p;
  581.     }
  582.  
  583.     /**
  584.      * Reference element identifying a data type to carry
  585.      * over to the new project.
  586.      *
  587.      * @access      public
  588.      */
  589.     function createReference() {
  590.         $num = array_push($this->references, new PhingReference());
  591.         return $this->references[$num-1];
  592.     }
  593.  
  594. }
  595.  
  596. /**
  597.  * Helper class that implements the nested <reference>
  598.  * element of <phing> and <phingcall>.
  599.  */
  600. class PhingReference extends Reference {
  601.  
  602.     private $targetid = null;
  603.  
  604.     /**
  605.      * Set the id that this reference to be stored under in the
  606.      * new project.
  607.      *
  608.      * @param targetid the id under which this reference will be passed to
  609.      *        the new project */
  610.     public function setToRefid($targetid) {
  611.         $this->targetid = $targetid;
  612.     }
  613.  
  614.     /**
  615.      * Get the id under which this reference will be stored in the new
  616.      * project
  617.      *
  618.      * @return the id of the reference in the new project.
  619.      */
  620.     public function getToRefid() {
  621.         return $this->targetid;
  622.     }
  623. }
  624.