123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- <?php
- /*
- * $Id: PhingTask.php 905 2010-10-05 16:28:03Z mrook $
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * This software consists of voluntary contributions made by many individuals
- * and is licensed under the LGPL. For more information please see
- * <http://phing.info>.
- */
- include_once 'phing/Task.php';
- include_once 'phing/util/FileUtils.php';
- include_once 'phing/types/Reference.php';
- include_once 'phing/tasks/system/PropertyTask.php';
- /**
- * Task that invokes phing on another build file.
- *
- * Use this task, for example, if you have nested buildfiles in your project. Unlike
- * AntTask, PhingTask can even support filesets:
- *
- * <pre>
- * <phing>
- * <fileset dir="${srcdir}">
- * <include name="** /build.xml" /> <!-- space added after ** is there because of PHP comment syntax -->
- * <exclude name="build.xml" />
- * </fileset>
- * </phing>
- * </pre>
- *
- * @author Hans Lellelid <hans@xmpl.org>
- * @version $Revision: 905 $
- * @package phing.tasks.system
- */
- class PhingTask extends Task {
- /** the basedir where is executed the build file */
- private $dir;
-
- /** build.xml (can be absolute) in this case dir will be ignored */
- private $phingFile;
-
- /** the target to call if any */
- protected $newTarget;
-
- /** should we inherit properties from the parent ? */
- private $inheritAll = true;
-
- /** should we inherit references from the parent ? */
- private $inheritRefs = false;
- /** the properties to pass to the new project */
- private $properties = array();
- /** the references to pass to the new project */
- private $references = array();
- /** The filesets that contain the files PhingTask is to be run on. */
- private $filesets = array();
- /** the temporary project created to run the build file */
- private $newProject;
- /** Fail the build process when the called build fails? */
- private $haltOnFailure = false;
- /**
- * If true, abort the build process if there is a problem with or in the target build file.
- * Defaults to false.
- *
- * @param boolean new value
- */
- public function setHaltOnFailure($hof) {
- $this->haltOnFailure = (boolean) $hof;
- }
- /**
- * Creates a Project instance for the project to call.
- * @return void
- */
- public function init() {
- $this->newProject = new Project();
- $tdf = $this->project->getTaskDefinitions();
- $this->newProject->addTaskDefinition("property", $tdf["property"]);
- }
- /**
- * Called in execute or createProperty if newProject is null.
- *
- * <p>This can happen if the same instance of this task is run
- * twice as newProject is set to null at the end of execute (to
- * save memory and help the GC).</p>
- *
- * <p>Sets all properties that have been defined as nested
- * property elements.</p>
- */
- private function reinit() {
- $this->init();
- $count = count($this->properties);
- for ($i = 0; $i < $count; $i++) {
- $p = $this->properties[$i];
- $newP = $this->newProject->createTask("property");
- $newP->setName($p->getName());
- if ($p->getValue() !== null) {
- $newP->setValue($p->getValue());
- }
- if ($p->getFile() !== null) {
- $newP->setFile($p->getFile());
- }
- if ($p->getPrefix() !== null) {
- $newP->setPrefix($p->getPrefix());
- }
- if ($p->getRefid() !== null) {
- $newP->setRefid($p->getRefid());
- }
- if ($p->getEnvironment() !== null) {
- $newP->setEnvironment($p->getEnvironment());
- }
- if ($p->getUserProperty() !== null) {
- $newP->setUserProperty($p->getUserProperty());
- }
- if ($p->getOverride() !== null) {
- $newP->setOverride($p->getOverride());
- }
- $this->properties[$i] = $newP;
- }
- }
- /**
- * Main entry point for the task.
- *
- * @return void
- */
- public function main() {
-
- // Call Phing on the file set with the attribute "phingfile"
- if ($this->phingFile !== null or $this->dir !== null) {
- $this->processFile();
- }
- // if no filesets are given stop here; else process filesets
- if (empty($this->filesets)) {
- return;
- }
-
- // preserve old settings
- $savedDir = $this->dir;
- $savedPhingFile = $this->phingFile;
- $savedTarget = $this->newTarget;
- // set no specific target for files in filesets
- // [HL] I'm commenting this out; I don't know why this should not be supported!
- // $this->newTarget = null;
-
- foreach($this->filesets as $fs) {
- $ds = $fs->getDirectoryScanner($this->project);
- $fromDir = $fs->getDir($this->project);
- $srcFiles = $ds->getIncludedFiles();
- foreach($srcFiles as $fname) {
- $f = new PhingFile($ds->getbasedir(), $fname);
- $f = $f->getAbsoluteFile();
- $this->phingFile = $f->getAbsolutePath();
- $this->dir = $f->getParentFile();
- $this->processFile(); // run Phing!
- }
- }
-
- // side effect free programming ;-)
- $this->dir = $savedDir;
- $this->phingFile = $savedPhingFile;
- $this->newTarget = $savedTarget;
-
- // [HL] change back to correct dir
- if ($this->dir !== null) {
- chdir($this->dir->getAbsolutePath());
- }
-
- }
-
- /**
- * Execute phing file.
- *
- * @return void
- */
- private function processFile() {
- $buildFailed = false;
- $savedDir = $this->dir;
- $savedPhingFile = $this->phingFile;
- $savedTarget = $this->newTarget;
-
- $savedBasedirAbsPath = null; // this is used to save the basedir *if* we change it
-
- try {
-
- if ($this->newProject === null) {
- $this->reinit();
- }
- $this->initializeProject();
-
- if ($this->dir !== null) {
-
- $dirAbsPath = $this->dir->getAbsolutePath();
-
- // BE CAREFUL! -- when the basedir is changed for a project,
- // all calls to getAbsolutePath() on a relative-path dir will
- // be made relative to the project's basedir! This means
- // that subsequent calls to $this->dir->getAbsolutePath() will be WRONG!
-
- // We need to save the current project's basedir first.
- $savedBasedirAbsPath = $this->getProject()->getBasedir()->getAbsolutePath();
-
- $this->newProject->setBasedir($this->dir);
-
- // Now we must reset $this->dir so that it continues to resolve to the same
- // path.
- $this->dir = new PhingFile($dirAbsPath);
-
- if ($savedDir !== null) { // has been set explicitly
- $this->newProject->setInheritedProperty("project.basedir", $this->dir->getAbsolutePath());
- }
-
- } else {
-
- // Since we're not changing the basedir here (for file resolution),
- // we don't need to worry about any side-effects in this scanrio.
- $this->dir = $this->getProject()->getBasedir();
- }
- $this->overrideProperties();
- if ($this->phingFile === null) {
- $this->phingFile = "build.xml";
- }
-
- $fu = new FileUtils();
- $file = $fu->resolveFile($this->dir, $this->phingFile);
- $this->phingFile = $file->getAbsolutePath();
-
- $this->log("Calling Buildfile '" . $this->phingFile . "' with target '" . $this->newTarget . "'");
-
- $this->newProject->setUserProperty("phing.file", $this->phingFile);
-
- ProjectConfigurator::configureProject($this->newProject, new PhingFile($this->phingFile));
- if ($this->newTarget === null) {
- $this->newTarget = $this->newProject->getDefaultTarget();
- }
- // Are we trying to call the target in which we are defined?
- if ($this->newProject->getBaseDir() == $this->project->getBaseDir() &&
- $this->newProject->getProperty("phing.file") == $this->project->getProperty("phing.file") &&
- $this->getOwningTarget() !== null &&
- $this->newTarget == $this->getOwningTarget()->getName()) {
- throw new BuildException("phing task calling its own parent target");
- }
- $this->addReferences();
- $this->newProject->executeTarget($this->newTarget);
-
- } catch (Exception $e) {
- $buildFailed = true;
- $this->log($e->getMessage(), Project::MSG_ERR);
- if (Phing::getMsgOutputLevel() <= Project::MSG_DEBUG) {
- $lines = explode("\n", $e->getTraceAsString());
- foreach($lines as $line) {
- $this->log($line, Project::MSG_DEBUG);
- }
- }
- // important!!! continue on to perform cleanup tasks.
- }
-
-
- // reset environment values to prevent side-effects.
-
- $this->newProject = null;
- $pkeys = array_keys($this->properties);
- foreach($pkeys as $k) {
- $this->properties[$k]->setProject(null);
- }
-
- $this->dir = $savedDir;
- $this->phingFile = $savedPhingFile;
- $this->newTarget = $savedTarget;
-
- // If the basedir for any project was changed, we need to set that back here.
- if ($savedBasedirAbsPath !== null) {
- chdir($savedBasedirAbsPath);
- }
- if ($this->haltOnFailure && $buildFailed) {
- throw new BuildException("Execution of the target buildfile failed. Aborting.");
- }
- }
- /**
- * Configure the Project, i.e. make intance, attach build listeners
- * (copy from father project), add Task and Datatype definitions,
- * copy properties and references from old project if these options
- * are set via the attributes of the XML tag.
- *
- * Developer note:
- * This function replaces the old methods "init", "_reinit" and
- * "_initializeProject".
- *
- * @access protected
- */
- private function initializeProject() {
-
- $this->newProject->setInputHandler($this->project->getInputHandler());
-
- foreach($this->project->getBuildListeners() as $listener) {
- $this->newProject->addBuildListener($listener);
- }
-
- /* Copy things from old project. Datatypes and Tasks are always
- * copied, properties and references only if specified so/not
- * specified otherwise in the XML definition.
- */
- // Add Datatype definitions
- foreach ($this->project->getDataTypeDefinitions() as $typeName => $typeClass) {
- $this->newProject->addDataTypeDefinition($typeName, $typeClass);
- }
-
- // Add Task definitions
- foreach ($this->project->getTaskDefinitions() as $taskName => $taskClass) {
- if ($taskClass == "propertytask") {
- // we have already added this taskdef in init()
- continue;
- }
- $this->newProject->addTaskDefinition($taskName, $taskClass);
- }
- // set user-defined properties
- $this->project->copyUserProperties($this->newProject);
- if (!$this->inheritAll) {
- // set System built-in properties separately,
- // b/c we won't inherit them.
- $this->newProject->setSystemProperties();
- } else {
- // set all properties from calling project
- $properties = $this->project->getProperties();
- foreach ($properties as $name => $value) {
- if ($name == "basedir" || $name == "phing.file" || $name == "phing.version") {
- // basedir and phing.file get special treatment in main()
- continue;
- }
- // don't re-set user properties, avoid the warning message
- if ($this->newProject->getProperty($name) === null){
- // no user property
- $this->newProject->setNewProperty($name, $value);
- }
- }
-
- }
-
- }
- /**
- * Override the properties in the new project with the one
- * explicitly defined as nested elements here.
- * @return void
- * @throws BuildException
- */
- private function overrideProperties() {
- foreach(array_keys($this->properties) as $i) {
- $p = $this->properties[$i];
- $p->setProject($this->newProject);
- $p->main();
- }
- $this->project->copyInheritedProperties($this->newProject);
- }
- /**
- * Add the references explicitly defined as nested elements to the
- * new project. Also copy over all references that don't override
- * existing references in the new project if inheritrefs has been
- * requested.
- *
- * @return void
- * @throws BuildException
- */
- private function addReferences() {
-
- // parent project references
- $projReferences = $this->project->getReferences();
-
- $newReferences = $this->newProject->getReferences();
-
- $subprojRefKeys = array();
-
- if (count($this->references) > 0) {
- for ($i=0, $count=count($this->references); $i < $count; $i++) {
- $ref = $this->references[$i];
- $refid = $ref->getRefId();
-
- if ($refid === null) {
- throw new BuildException("the refid attribute is required"
- . " for reference elements");
- }
- if (!isset($projReferences[$refid])) {
- $this->log("Parent project doesn't contain any reference '"
- . $refid . "'",
- Project::MSG_WARN);
- continue;
- }
-
- $subprojRefKeys[] = $refid;
- //thisReferences.remove(refid);
- $toRefid = $ref->getToRefid();
- if ($toRefid === null) {
- $toRefid = $refid;
- }
- $this->copyReference($refid, $toRefid);
- }
- }
- // Now add all references that are not defined in the
- // subproject, if inheritRefs is true
- if ($this->inheritRefs) {
-
- // get the keys that are were not used by the subproject
- $unusedRefKeys = array_diff(array_keys($projReferences), $subprojRefKeys);
-
- foreach($unusedRefKeys as $key) {
- if (isset($newReferences[$key])) {
- continue;
- }
- $this->copyReference($key, $key);
- }
- }
- }
- /**
- * Try to clone and reconfigure the object referenced by oldkey in
- * the parent project and add it to the new project with the key
- * newkey.
- *
- * <p>If we cannot clone it, copy the referenced object itself and
- * keep our fingers crossed.</p>
- *
- * @param string $oldKey
- * @param string $newKey
- * @return void
- */
- private function copyReference($oldKey, $newKey) {
- $orig = $this->project->getReference($oldKey);
- if ($orig === null) {
- $this->log("No object referenced by " . $oldKey . ". Can't copy to "
- .$newKey,
- PROJECT_SG_WARN);
- return;
- }
- $copy = clone $orig;
- if ($copy instanceof ProjectComponent) {
- $copy->setProject($this->newProject);
- } elseif (in_array('setProject', get_class_methods(get_class($copy)))) {
- $copy->setProject($this->newProject);
- } elseif ($copy instanceof Project) {
- // don't copy the old "Project" itself
- } else {
- $msg = "Error setting new project instance for "
- . "reference with id " . $oldKey;
- throw new BuildException($msg);
- }
-
- $this->newProject->addReference($newKey, $copy);
- }
- /**
- * If true, pass all properties to the new phing project.
- * Defaults to true.
- *
- * @access public
- */
- function setInheritAll($value) {
- $this->inheritAll = (boolean) $value;
- }
- /**
- * If true, pass all references to the new phing project.
- * Defaults to false.
- *
- * @access public
- */
- function setInheritRefs($value) {
- $this->inheritRefs = (boolean)$value;
- }
- /**
- * The directory to use as a base directory for the new phing project.
- * Defaults to the current project's basedir, unless inheritall
- * has been set to false, in which case it doesn't have a default
- * value. This will override the basedir setting of the called project.
- *
- * @access public
- */
- function setDir($d) {
- if ( is_string($d) )
- $this->dir = new PhingFile($d);
- else
- $this->dir = $d;
- }
- /**
- * The build file to use.
- * Defaults to "build.xml". This file is expected to be a filename relative
- * to the dir attribute given.
- *
- * @access public
- */
- function setPhingfile($s) {
- // it is a string and not a file to handle relative/absolute
- // otherwise a relative file will be resolved based on the current
- // basedir.
- $this->phingFile = $s;
- }
- /**
- * Alias function for setPhingfile
- *
- * @access public
- */
- function setBuildfile($s) {
- $this->setPhingFile($s);
- }
- /**
- * The target of the new Phing project to execute.
- * Defaults to the new project's default target.
- *
- * @access public
- */
- function setTarget($s) {
- $this->newTarget = $s;
- }
- /**
- * Support for filesets; This method returns a reference to an instance
- * of a FileSet object.
- *
- * @return FileSet
- */
- function createFileSet() {
- $num = array_push($this->filesets, new FileSet());
- return $this->filesets[$num-1];
- }
- /**
- * Property to pass to the new project.
- * The property is passed as a 'user property'
- *
- * @access public
- */
- function createProperty() {
- $p = new PropertyTask();
- $p->setFallback($this->newProject);
- $p->setUserProperty(true);
- $this->properties[] = $p;
- return $p;
- }
- /**
- * Reference element identifying a data type to carry
- * over to the new project.
- *
- * @access public
- */
- function createReference() {
- $num = array_push($this->references, new PhingReference());
- return $this->references[$num-1];
- }
- }
- /**
- * Helper class that implements the nested <reference>
- * element of <phing> and <phingcall>.
- *
- * @package phing.tasks.system
- */
- class PhingReference extends Reference {
- private $targetid = null;
- /**
- * Set the id that this reference to be stored under in the
- * new project.
- *
- * @param targetid the id under which this reference will be passed to
- * the new project */
- public function setToRefid($targetid) {
- $this->targetid = $targetid;
- }
- /**
- * Get the id under which this reference will be stored in the new
- * project
- *
- * @return the id of the reference in the new project.
- */
- public function getToRefid() {
- return $this->targetid;
- }
- }
|