From 96a2ab25d0dee0dc92e76bb925841196f2154961 Mon Sep 17 00:00:00 2001 From: siad007 Date: Sun, 13 Aug 2017 22:00:41 +0200 Subject: [PATCH] Added DependSet task. --- classes/phing/tasks/defaults.properties | 1 + classes/phing/tasks/system/DependSet.php | 244 ++++++++++++++++++ .../en/source/appendixes/coretasks.xml | 58 +++++ etc/phing-grammar.rng | 30 +++ .../phing/tasks/system/DependSetTest.php | 54 ++++ test/etc/tasks/system/dependset.xml | 43 +++ 6 files changed, 430 insertions(+) create mode 100644 classes/phing/tasks/system/DependSet.php create mode 100644 test/classes/phing/tasks/system/DependSetTest.php create mode 100644 test/etc/tasks/system/dependset.xml diff --git a/classes/phing/tasks/defaults.properties b/classes/phing/tasks/defaults.properties index b26f94f422..01baf1c4de 100644 --- a/classes/phing/tasks/defaults.properties +++ b/classes/phing/tasks/defaults.properties @@ -55,6 +55,7 @@ switch=phing.tasks.system.SwitchTask basename=phing.tasks.system.Basename diagnostics=phing.tasks.system.DiagnosticsTask pathconvert=phing.tasks.system.PathConvert +dependset=phing.tasks.system.DependSet ; "Core" contributed tasks ; -- i.e. no taskdef needed. diff --git a/classes/phing/tasks/system/DependSet.php b/classes/phing/tasks/system/DependSet.php new file mode 100644 index 0000000000..773c1bbc54 --- /dev/null +++ b/classes/phing/tasks/system/DependSet.php @@ -0,0 +1,244 @@ +. + */ + +require_once 'phing/tasks/system/MatchingTask.php'; + +/** + * Examines and removes out of date target files. If any of the target files + * are out of date with respect to any of the source files, all target + * files are removed. This is useful where dependencies cannot be + * computed (for example, dynamically interpreted parameters or files + * that need to stay in synch but are not directly linked) or where + * the phing task in question could compute them but does not. + * + * nested arguments: + * + * At least one instance of either a fileset or filelist for both source and + * target are required. + *

+ * This task will examine each of the source files against each of the target + * files. If any target files are out of date with respect to any of the source + * files, all targets are removed. If any files named in a (src or target) + * filelist do not exist, all targets are removed. + * Hint: If missing files should be ignored, specify them as include patterns + * in filesets, rather than using filelists. + *

+ * This task attempts to optimize speed of dependency checking. It will stop + * after the first out of date file is found and remove all targets, rather + * than exhaustively checking every source vs target combination unnecessarily. + *

+ * + * @package phing.tasks.system + * @author Siad Ardroumli + */ +class DependSet extends MatchingTask +{ + /** @var FileSet[] $sourceFileSets */ + private $sourceFileSets = []; + + /** @var FileList[] $sourceFileLists */ + private $sourceFileLists = []; + + /** @var FileSet[] $targetFileSets */ + private $targetFileSets = []; + + /** @var FileList[] $targetFileLists */ + private $targetFileLists = []; + + /** + * Add a set of source files. + * @param FileSet $fs the FileSet to add. + */ + public function addSrcfileset(FileSet $fs) + { + $this->sourceFileSets[] = $fs; + } + + /** + * Add a list of source files. + * @param FileList $fl the FileList to add. + */ + public function addSrcfilelist(FileList $fl) + { + $this->sourceFileLists[] = $fl; + } + + /** + * Add a set of target files. + * @param FileSet $fs the FileSet to add. + */ + public function addTargetfileset(FileSet $fs) + { + $this->targetFileSets[] = $fs; + } + + /** + * Add a list of target files. + * @param FileList $fl the FileList to add. + */ + public function addTargetfilelist(FileList $fl) + { + $this->targetFileLists[] = $fl; + } + + /** + * Executes the task. + * @throws BuildException if errors occur. + */ + public function main() + { + if ((count($this->sourceFileSets) === 0) && (count($this->sourceFileLists) === 0)) { + throw new BuildException('At least one or ' + . ' element must be set'); + } + if ((count($this->targetFileSets) === 0) && (count($this->targetFileLists) === 0)) { + throw new BuildException('At least one or' + . ' element must be set'); + } + $now = (new DateTime())->getTimestamp(); + /* + We have to munge the time to allow for the filesystem time + granularity. + */ + // $now += FILE_UTILS . getFileTimestampGranularity(); + + // Grab all the target files specified via filesets: + $allTargets = []; + $oldestTargetTime = 0; + $oldestTarget = null; + foreach ($this->targetFileSets as $targetFS) { + if (!$targetFS->getDir($this->getProject())->exists()) { + // this is the same as if it was empty, no target files found + continue; + } + $targetDS = $targetFS->getDirectoryScanner($this->getProject()); + $targetFiles = $targetDS->getIncludedFiles(); + + foreach ($targetFiles as $targetFile) { + + $dest = new PhingFile($targetFS->getDir($this->getProject()), $targetFile); + $allTargets[] = $dest; + + if ($dest->lastModified() > $now) { + $this->log('Warning: ' . $targetFile . ' modified in the future.', + Project::MSG_WARN); + } + if ($oldestTarget === null + || $dest->lastModified() < $oldestTargetTime) { + $oldestTargetTime = $dest->lastModified(); + $oldestTarget = $dest; + } + } + } + // Grab all the target files specified via filelists: + $upToDate = true; + foreach ($this->targetFileLists as $targetFL) { + $targetFiles = $targetFL->getFiles($this->getProject()); + + foreach ($targetFiles as $targetFile) { + $dest = new PhingFile($targetFL->getDir($this->getProject()), $targetFile); + if (!$dest->exists()) { + $this->log($targetFile . ' does not exist.', Project::MSG_VERBOSE); + $upToDate = false; + continue; + } + + $allTargets[] = $dest; + if ($dest->lastModified() > $now) { + $this->log('Warning: ' . $targetFile . ' modified in the future.', + Project::MSG_WARN); + } + if ($oldestTarget === null + || $dest->lastModified() < $oldestTargetTime) { + $oldestTargetTime = $dest->lastModified(); + $oldestTarget = $dest; + } + } + } + if ($oldestTarget !== null) { + $this->log($oldestTarget . ' is oldest target file', Project::MSG_VERBOSE); + } else { + // no target files, then we cannot remove any target files and + // skip the following tests right away + $upToDate = false; + } + // Check targets vs source files specified via filelists: + if ($upToDate) { + foreach ($this->sourceFileLists as $sourceFL) { + $sourceFiles = $sourceFL->getFiles($this->getProject()); + + foreach ($sourceFiles as $sourceFile) { + $src = new PhingFile($sourceFL->getDir($this->getProject()), $sourceFile); + + if ($src->lastModified() > $now) { + $this->log('Warning: ' . $sourceFile + . ' modified in the future.', Project::MSG_WARN); + } + if (!$src->exists()) { + $this->log($sourceFile . ' does not exist.', + Project::MSG_VERBOSE); + $upToDate = false; + break 2; + } + if ($src->lastModified() > $oldestTargetTime) { + $upToDate = false; + $this->log($oldestTarget . ' is out of date with respect to ' + . $sourceFile, Project::MSG_VERBOSE); + break 2; + } + } + } + } + // Check targets vs source files specified via filesets: + if ($upToDate) { + foreach ($this->sourceFileSets as $sourceFS) { + $sourceDS = $sourceFS->getDirectoryScanner($this->getProject()); + $sourceFiles = $sourceDS->getIncludedFiles(); + + foreach ($sourceFiles as $sourceFile) { + $src = new PhingFile($sourceFS->getDir($this->getProject()), $sourceFile); + + if ($src->lastModified() > $now) { + $this->log('Warning: ' . $sourceFile + . ' modified in the future.', Project::MSG_WARN); + } + if ($src->lastModified() > $oldestTargetTime) { + $upToDate = false; + $this->log($oldestTarget . ' is out of date with respect to ' + . $sourceFile, Project::MSG_VERBOSE); + break 2; + } + } + } + } + if (!$upToDate) { + $this->log('Deleting all target files. ', Project::MSG_VERBOSE); + foreach ($allTargets as $fileToRemove) { + $this->log('Deleting file ' . $fileToRemove->getAbsolutePath(), + Project::MSG_VERBOSE); + $fileToRemove->delete(); + } + } + } +} diff --git a/docs/docbook5/en/source/appendixes/coretasks.xml b/docs/docbook5/en/source/appendixes/coretasks.xml index 7f8ac9e492..531c2aae56 100644 --- a/docs/docbook5/en/source/appendixes/coretasks.xml +++ b/docs/docbook5/en/source/appendixes/coretasks.xml @@ -1366,6 +1366,64 @@ verbose="true" failonerror="false" /> + + DependSet + The dependset task compares a set of sources with a set of target files. + If any of the sources has been modified more recently than any of the target files, + all of the target files are removed. + + Examples + <dependset> + <srcfilelist + dir = "${dtd.dir}" + files = "paper.dtd,common.dtd"/> + <srcfilelist + dir = "${xsl.dir}" + files = "common.xsl"/> + <srcfilelist + dir = "${basedir}" + files = "build.xml"/> + <targetfileset + dir = "${output.dir}" + includes = "**/*.html"/> +</dependset> + In this example derived HTML files in the ${output.dir} directory will be removed if any are + out-of-date with respect to: + + + the DTD of their source XML files + + + a common DTD (imported by the main DTD) + + + a subordinate XSLT stylesheet (imported by the main stylesheet), or + + + the buildfile + + + If any of the sources in the above example does not exist, all target files will also be removed. + To ignore missing sources instead, use filesets instead of filelists for the sources. + + + Supported Nested Tags + + + srcfileset + + + srcfilelist + + + targetfileset + + + targetfilelist + + + + Diagnostics Runs phing's -diagnostics code inside phing itself. diff --git a/etc/phing-grammar.rng b/etc/phing-grammar.rng index c825305aff..2f4b3341ce 100644 --- a/etc/phing-grammar.rng +++ b/etc/phing-grammar.rng @@ -223,6 +223,7 @@ + @@ -346,6 +347,22 @@ + + + + + + + + + + + + + + + + @@ -1014,6 +1031,19 @@ + + + + + + + + + + + + + diff --git a/test/classes/phing/tasks/system/DependSetTest.php b/test/classes/phing/tasks/system/DependSetTest.php new file mode 100644 index 0000000000..1759524fd5 --- /dev/null +++ b/test/classes/phing/tasks/system/DependSetTest.php @@ -0,0 +1,54 @@ + + * @package phing.tasks.system + */ +class DependSetTest extends BuildFileTest +{ + public function setUp() + { + $this->configureProject( + PHING_TEST_BASE . '/etc/tasks/system/dependset.xml' + ); + } + + public function tearDown() + { + $this->executeTarget('cleanup'); + } + + public function test1() + { + $this->expectBuildException(__FUNCTION__, "At least one or element must be set"); + } + + public function test2() + { + $this->expectBuildException(__FUNCTION__, + "At least one or element must be set"); + } + + public function test3() + { + $this->expectBuildException(__FUNCTION__, "At least one or element must be set"); + } + + public function test4() + { + $this->executeTarget(__FUNCTION__); + } + + public function test5() + { + $this->executeTarget(__FUNCTION__); + $f = new PhingFile($this->getProjectDir(), 'older.tmp'); + if ($f->exists()) { + $this->fail('dependset failed to remove out of date file ' . $f->toString()); + } + } +} diff --git a/test/etc/tasks/system/dependset.xml b/test/etc/tasks/system/dependset.xml new file mode 100644 index 0000000000..e33f6333b4 --- /dev/null +++ b/test/etc/tasks/system/dependset.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +