From 8511b3d7fd88e423acbb1fb25b2a55e74ac599d7 Mon Sep 17 00:00:00 2001 From: verbruggenalex Date: Wed, 14 Feb 2018 09:58:03 +0100 Subject: [PATCH] Relative symlinking (#695) --- classes/phing/tasks/ext/SymlinkTask.php | 51 ++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/classes/phing/tasks/ext/SymlinkTask.php b/classes/phing/tasks/ext/SymlinkTask.php index 04cb8e6079..4ce77488a3 100644 --- a/classes/phing/tasks/ext/SymlinkTask.php +++ b/classes/phing/tasks/ext/SymlinkTask.php @@ -206,6 +206,46 @@ public function isRelative() return $this->relative; } + /** + * Given an existing path, convert it to a path relative to a given starting path. + * + * @param string $endPath Absolute path of target + * @param string $startPath Absolute path where traversal begins + * + * @return string Path of target relative to starting path + */ + public function makePathRelative($endPath, $startPath) + { + // Normalize separators on Windows + if ('\\' === DIRECTORY_SEPARATOR) { + $endPath = str_replace('\\', '/', $endPath); + $startPath = str_replace('\\', '/', $startPath); + } + + // Split the paths into arrays + $startPathArr = explode('/', trim($startPath, '/')); + $endPathArr = explode('/', trim($endPath, '/')); + + // Find for which directory the common path stops + $index = 0; + while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { + ++$index; + } + + // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) + $depth = count($startPathArr) - $index; + + // Repeated "../" for each level need to reach the common path + $traverser = str_repeat('../', $depth); + + $endPathRemainder = implode('/', array_slice($endPathArr, $index)); + + // Construct $endPath from traversing to the common path, then to the remaining $endPath + $relativePath = $traverser.('' !== $endPathRemainder ? $endPathRemainder.'/' : ''); + + return '' === $relativePath ? './' : $relativePath; + } + /** * Generates an array of directories / files to be linked * If _filesets is empty, returns getTarget() @@ -235,11 +275,7 @@ protected function getMap() throw new BuildException('Link must be an existing directory when using fileset'); } - if ($this->isRelative()) { - $fromDir = $fs->getDir($this->getProject())->getPath(); - } else { - $fromDir = $fs->getDir($this->getProject())->getAbsolutePath(); - } + $fromDir = $fs->getDir($this->getProject())->getAbsolutePath(); if (!is_dir($fromDir)) { $this->log('Directory doesn\'t exist: ' . $fromDir, Project::MSG_WARN); @@ -300,6 +336,11 @@ protected function symlink($target, $link) { $fs = FileSystem::getFileSystem(); + if ($this->isRelative()) { + $link =(new PhingFile($link))->getAbsolutePath(); + $target = rtrim($this->makePathRelative($target, dirname($link)), '/'); + } + if (is_link($link) && @readlink($link) == $target) { $this->log('Link exists: ' . $link, Project::MSG_INFO);