Skip to content

Commit

Permalink
Refs #620 - always make imported targets available as importname.targ…
Browse files Browse the repository at this point in the history
…etname.
  • Loading branch information
mpdude committed Jun 1, 2011
1 parent eaf81bc commit dd97b79
Show file tree
Hide file tree
Showing 10 changed files with 419 additions and 443 deletions.
2 changes: 1 addition & 1 deletion classes/phing/Phing.php
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,7 @@ function printTargets($project) {

// subtargets are targets w/o descriptions
if ($targetDescription === null) {
$subNames[] = $targetName;
$subNames[$targetName] = $targetName;
} else {
// topNames and topDescriptions are handled later
// here we store in hash map (for sorting purposes)
Expand Down
117 changes: 47 additions & 70 deletions classes/phing/Project.php
Original file line number Diff line number Diff line change
Expand Up @@ -580,10 +580,6 @@ function addOrReplaceTarget($targetName, &$target) {
$this->log(" +Target: $targetName", Project::MSG_DEBUG);
$target->setProject($this);
$this->targets[$targetName] = $target;

$ctx = $this->getReference("phing.parsing.context");
$current = $ctx->getConfigurator()->getCurrentTargets();
$current[$targetName] = $target;
}

function getTargets() {
Expand Down Expand Up @@ -723,19 +719,18 @@ function executeTarget($targetName) {

// invoke topological sort of the target tree and run all targets
// until targetName occurs.
$sortedTargets = $this->_topoSort($targetName, $this->targets);
foreach ($this->_topoSort($targetName) as $t) {

$curIndex = (int) 0;
$curTarget = null;
do {
try {
$curTarget = $sortedTargets[$curIndex++];
$curTarget->performTasks();
$t->performTasks();
} catch (BuildException $exc) {
$this->log("Execution of target \"".$curTarget->getName()."\" failed for the following reason: ".$exc->getMessage(), Project::MSG_ERR);
$this->log("Execution of target \"".$t->getName()."\" failed for the following reason: ".$exc->getMessage(), Project::MSG_ERR);
throw $exc;
}
} while ($curTarget->getName() !== $targetName);

if ($t === $this->targets[$targetName])
return;
}
}


Expand All @@ -747,6 +742,12 @@ function resolveFile($fileName, $rootDir = null) {
}
}

protected function _targetSequenceToString($seq) {
$r = "";
foreach ($seq as $t) $r .= $t->toString() . " ";
return $r;
}

/**
* Topologically sort a set of Targets.
* @param $root is the (String) name of the root Target. The sort is
Expand All @@ -756,7 +757,7 @@ function resolveFile($fileName, $rootDir = null) {
* @return An array of Strings with the names of the targets in
* sorted order.
*/
function _topoSort($root, &$targets) {
function _topoSort($root) {

$root = (string) $root;
$ret = array();
Expand All @@ -771,35 +772,20 @@ function _topoSort($root, &$targets) {
// dependency tree, not just on the Targets that depend on the
// build Target.

$this->_tsort($root, $targets, $state, $visiting, $ret);

$retHuman = "";
for ($i=0, $_i=count($ret); $i < $_i; $i++) {
$retHuman .= $ret[$i]->toString()." ";
}
$this->log("Build sequence for target '$root' is: $retHuman", Project::MSG_VERBOSE);
$this->_tsort($root, $state, $visiting, $ret);

$keys = array_keys($targets);
while($keys) {
$curTargetName = (string) array_shift($keys);
if (!isset($state[$curTargetName])) {
$st = null;
} else {
$st = (string) $state[$curTargetName];
}
$this->log("Build sequence for target '$root' is: " . $this->_targetSequenceToString($ret), Project::MSG_VERBOSE);

if ($st === null) {
$this->_tsort($curTargetName, $targets, $state, $visiting, $ret);
} elseif ($st === "VISITING") {
throw new Exception("Unexpected node in visiting state: $curTargetName");
foreach ($this->targets as $t) {
$name = $t->getName(); // "canonical" name
if (!isset($state[$name])) {
$this->_tsort($name, $state, $visiting, $ret);
} else if ($state[$name] == 'VISITING') {
throw new Exception("Unexpected node in visiting state: $name");
}
}

$retHuman = "";
for ($i=0,$_i=count($ret); $i < $_i; $i++) {
$retHuman .= $ret[$i]->toString()." ";
}
$this->log("Complete build sequence is: $retHuman", Project::MSG_VERBOSE);
$this->log("Complete build sequence is: " . $this->_targetSequenceToString($ret), Project::MSG_VERBOSE);

return $ret;
}
Expand All @@ -821,51 +807,42 @@ function _topoSort($root, &$targets) {
// "ret" now contains the sorted sequence of Targets upto the current
// Target.

function _tsort($root, &$targets, &$state, &$visiting, &$ret) {
$state[$root] = "VISITING";
$visiting[] = $root;
function _tsort($root, &$state, &$visiting, &$ret) {

if (!isset($targets[$root]) || !($targets[$root] instanceof Target)) {
$target = null;
} else {
$target = $targets[$root];
}

// make sure we exist
if ($target === null) {
// Make sure target named $root exists.
if (!isset($this->targets[$root]) || !($this->targets[$root] instanceof Target)) {
$sb = "Target '$root' does not exist in this project.";
array_pop($visiting);
if (!empty($visiting)) {
$parent = (string) $visiting[count($visiting)-1];
$parent = end($visiting);
$sb .= "It is used from target '$parent'.";
}
throw new BuildException($sb);
}

$deps = $target->getDependencies();
// Fetch $root's "canonical" name
$target = $this->targets[$root];
$name = $target->getName();

// Put $root on DFS stack
$state[$name] = 'VISITING';
$visiting[] = $name;

while($deps) {
$cur = (string) array_shift($deps);
if (!isset($state[$cur])) {
$m = null;
} else {
$m = (string) $state[$cur];
}
if ($m === null) {
// not been visited
$this->_tsort($cur, $targets, $state, $visiting, $ret);
} elseif ($m == "VISITING") {
// currently visiting this node, so have a cycle
throw $this->_makeCircularException($cur, $visiting);
}
}
// Recursively visit $root's dependencies
foreach ($target->getDependencies() as $dep) {

$p = (string) array_pop($visiting);
if ($root !== $p) {
throw new Exception("Unexpected internal error: expected to pop $root but got $p");
// Map names to "canonical" form.
$depname = $this->targets[$dep]->getName();

if (!isset($state[$depname]))
$this->_tsort($depname, $state, $visiting, $ret);
else if ($state[$depname] == 'VISITING')
throw $this->_makeCircularException($depname, $visiting);
}

$state[$root] = "VISITED";
// Finishing/leaving $root.
if ($name !== array_pop($visiting))
throw new Exception("Unexpected internal error: expected to pop $name but got $p");
$state[$name] = "VISITED";
$ret[] = $target;
}

Expand Down
7 changes: 0 additions & 7 deletions classes/phing/parser/ProjectConfigurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ class ProjectConfigurator {
public $buildFile;
public $buildFileParent;

/** Targets in current file */
private $currentTargets;

/** Synthetic target that will be called at the end to the parse phase */
private $parseEndTarget;

Expand Down Expand Up @@ -142,10 +139,6 @@ public function setIgnoreProjectTag($flag) {
$this->ignoreProjectTag = $flag;
}

public function &getCurrentTargets () {
return $this->currentTargets;
}

public function isParsing () {
return $this->isParsing;
}
Expand Down
36 changes: 21 additions & 15 deletions classes/phing/parser/ProjectHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,29 @@ function init($tag, $attrs) {
throw new ExpatParseException("Unexpected attribute '$key'");
}
}

// these things get done no matter what
$resolvedBasedir = null;
if ($baseDir === null) {
$resolvedBasedir = $buildFileParent->getAbsolutePath();
} else {
// check whether the user has specified an absolute path
$f = new PhingFile($baseDir);
if ($f->isAbsolute()) {
$resolvedBasedir = $baseDir;
} else {
$resolvedBasedir = $project->resolveFile($baseDir, $buildFileParent);
}
}

if (null != $name) {
$canonicalName = self::canonicalName($name);
$this->configurator->setCurrentProjectName($canonicalName);
$project->setUserProperty("phing.file.{$canonicalName}",
(string) $this->configurator->getBuildFile());
$project->setUserProperty("phing.file.{$canonicalName}", (string) $this->configurator->getBuildFile());
$project->setUserProperty("project.basedir.{$canonicalName}", $resolvedBasedir);
}

// this only happens for toplevel build files
if (!$this->configurator->isIgnoringProjectTag()) {
if ($def === null) {
throw new ExpatParseException(
Expand All @@ -125,21 +140,12 @@ function init($tag, $attrs) {
$project->setPhingVersion($ver);
}

if ($project->getProperty("project.basedir") !== null) {
$project->setBasedir($project->getProperty("project.basedir"));
if (($bd = $project->getProperty("project.basedir")) !== null) {
$project->setBasedir($bd);
} else {
if ($baseDir === null) {
$project->setBasedir($buildFileParent->getAbsolutePath());
} else {
// check whether the user has specified an absolute path
$f = new PhingFile($baseDir);
if ($f->isAbsolute()) {
$project->setBasedir($baseDir);
} else {
$project->setBaseDir($project->resolveFile($baseDir, $buildFileParent));
}
}
$project->setBasedir($resolvedBasedir);
}

}
}

Expand Down
57 changes: 27 additions & 30 deletions classes/phing/parser/TargetHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,10 @@ function init($tag, $attrs) {

// shorthand
$project = $this->configurator->project;

// check to see if this target is a dup within the same file
if (isset($this->configurator->getCurrentTargets[$name])) {
throw new BuildException("Duplicate target: $targetName",
$this->parser->getLocation());
}
$projectTargets = $project->getTargets();
$currentProjectName = $this->configurator->getCurrentProjectName();
$importedFile = $this->configurator->isIgnoringProjectTag();
$targetAdded = false;

$this->target = new Target();
$this->target->setName($name);
Expand All @@ -128,37 +126,36 @@ function init($tag, $attrs) {
$this->target->setDepends($depends);
}

$usedTarget = false;
// check to see if target with same name is already defined
$projectTargets = $project->getTargets();
if (isset($projectTargets[$name])) {
$project->log("Already defined in main or a previous import, " .
"ignore {$name}", Project::MSG_VERBOSE);
if (!$importedFile)
throw new BuildException("Duplicate target: $name", $this->parser->getLocation());
else
$project->log("Target $name already defined, either in main or a previous import. Not importing it under this name.", Project::MSG_VERBOSE);
} else {
$project->addTarget($name, $this->target);
if ($id !== null && $id !== "") {
$project->addReference($id, $this->target);
}
$usedTarget = true;
$targetAdded = true;
}

if ($this->configurator->isIgnoringProjectTag() &&
$this->configurator->getCurrentProjectName() != null &&
strlen($this->configurator->getCurrentProjectName()) != 0) {
// In an impored file (and not completely
// ignoring the project tag)
$newName = $this->configurator->getCurrentProjectName() . "." . $name;
if ($usedTarget) {
// clone needs to make target->children a shared reference
$newTarget = clone $this->target;
} else {
$newTarget = $this->target;
// If we're in an imported file and the project name is set,
// we can namespace the target. If we're imported but have no
// project name and the target name is taken, silently ignore the new (imported) target.
if ($importedFile && $currentProjectName) {
$namespacedName = "$currentProjectName.$name";

if (!isset($projectTargets[$namespacedName])) {

if (!$targetAdded)
$this->target->setName($namespacedName);

$project->log("Adding $name as $namespacedName.", Project::MSG_DEBUG);
$project->addTarget($namespacedName, $this->target);
$targetAdded = true;
}
$newTarget->setName($newName);
$ct = $this->configurator->getCurrentTargets();
$ct[$newName] = $newTarget;
$project->addTarget($newName, $newTarget);
}

if ($targetAdded && $id)
$project->addReference($id, $this->target);

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ <h2>
<td>Full path to current buildfile.</td>
</tr>
<tr>
<td>phing.file.<em>projectname</em></td>
<td>Full path to an imported buildfile containing the project named <em>projectname</em>.</td>
</tr>

<tr>
<td>phing.home</td>
<td>Phing installation directory, not set in <em>PEAR</em>
installations.</td>
Expand Down Expand Up @@ -128,6 +133,10 @@ <h2>
</tr>

<tr>
<td>project.basedir.<em>projectname</em></td>
<td>For an imported project named <em>projectname</em>, the directory qualified by the imported file's basedir attribute.</td>
</tr>
<tr>
<td>user.home</td>
<td>Value of the environment variable <em>HOME</em>.</td>
</tr>
Expand Down
Loading

0 comments on commit dd97b79

Please sign in to comment.