Ticket #221: phing.symlinks[1].patch

File phing.symlinks[1].patch, 8.5 kB (added by Bruce Weirdan <weirdan@gmail.com>, 6 months ago)

patch that adds ability to copy symlinks 'as is' both for directories and files

  • classes/phing/system/io/File.php

    old new  
    405405        return @is_file($this->path); 
    406406    } 
    407407 
     408        public function isLink() { 
     409                clearstatcache(); 
     410                $fs = FileSystem::getFileSystem(); 
     411                if ($fs->checkAccess($this) !== true) { 
     412                        throw new IOException("No read access to " . $this->path); 
     413                } 
     414                return @is_link($this->path); 
     415        } 
     416 
     417        public function getLinkTarget()  
     418        { 
     419                return @readlink($this->path); 
     420        } 
     421 
    408422    /** 
    409423     * Tests whether the path represented by this object is a hidden file. 
    410424     *   
     
    608622     * @param File $destFile  The new abstract pathname for the named file 
    609623     * @return true if and only if the renaming succeeded; false otherwise 
    610624     */ 
    611     public function copyTo(File $destFile) { 
     625    public function copyTo(File $destFile, $resolveSymlinks = true) { 
    612626        $fs = FileSystem::getFileSystem(); 
    613627 
    614628        if ($fs->checkAccess($this) !== true) { 
     
    618632        if ($fs->checkAccess($destFile, true) !== true) { 
    619633            throw new IOException("File::copyTo() No write access to ".$destFile->getPath()); 
    620634        } 
    621         return $fs->copy($this, $destFile); 
     635        return $fs->copy($this, $destFile, $resolveSymlinks); 
    622636    } 
    623637 
    624638    /** 
  • classes/phing/system/io/UnixFileSystem.php

    old new  
    271271     * @param File $f 
    272272     * @return boolean 
    273273     */ 
    274     function canDelete(File $f)  
     274       function canDelete(File $f)  
    275275        {  
    276276                @clearstatcache();  
    277277                $dir = dirname($f->getAbsolutePath());  
    278278                return (bool) @is_writable($dir);  
    279279        } 
     280 
     281 
     282        /** 
     283         * Copy a file.  
     284         * 
     285         * This method overrides generic copy method to add support for copying  
     286         * symlinks as is (without resolving them to ordinary files). 
     287         *  
     288         * @param File $src Source path and name file to copy 
     289         * @param File $dest Destination path and name of new file 
     290         * @param bool $resolveSymlinks  whether to resolve symlink to their content 
     291         * 
     292         * @return void 
     293         * @throws Exception if file cannot be copied or symlink cannot be created 
     294         */ 
     295        function copy(File $src, File $dest, $resolveSymlinks = true) { 
     296                if ($resolveSymlinks || !$src->isLink()) { // copy as usual (content) 
     297                        return parent::copy($src, $dest); 
     298                } else { // do not resolve symlinks, copy as is 
     299                        $destPath = $dest->getAbsolutePath(); 
     300                        $linkTarget = $src->getLinkTarget(); 
     301                        if (false === @symlink($linkTarget, $destPath)) { 
     302                                $msg = "FileSystem::copy() FAILED. Cannot create symlink from $destPath to $linkTarget."; 
     303                                throw new Exception($msg); 
     304                        } 
     305 
     306                        // chmod has no effects on symlinks, so we do not have to chmod created symlink 
     307                } 
     308        } 
    280309     
    281310} 
  • classes/phing/tasks/system/CopyTask.php

    old new  
    4949    protected $preserveLMT   = true;   // sync timestamps (from xml attribute) 
    5050    protected $includeEmpty  = true;   // include empty dirs? (from XML) 
    5151    protected $flatten       = false;  // apply the FlattenMapper right way (from XML) 
     52        protected $resolveSymlinks = true; // resolve symlinks (copy them as their content) ? 
    5253    protected $mapperElement = null; 
    5354 
    5455    protected $fileCopyMap   = array(); // asoc array containing mapped file names 
     
    118119        $this->includeEmpty = (boolean) $bool; 
    119120    } 
    120121 
     122        /** 
     123         * Set the resolve symlinks flag. Obviously meaningless on filesystems 
     124         * without symlink support. 
     125         *  
     126         * @param boolean  Flag if symlinks should be copied as their content 
     127         * @return void 
     128         * @access public 
     129         */ 
     130        function setResolveSymlinks($bool) { 
     131                $this->resolveSymlinks = (boolean) $bool; 
     132        } 
    121133 
     134 
    122135    /** 
    123136     * Set the file. We have to manually take care of the 
    124137     * type that is coming due to limited type support in php 
     
    160173        $this->destDir = $dir; 
    161174    } 
    162175 
     176 
    163177    /** 
    164178     * Nested creator, creates a FileSet for this task 
    165179     * 
     
    230244        foreach($this->filesets as $fs) { 
    231245            $ds = $fs->getDirectoryScanner($project); 
    232246            $fromDir  = $fs->getDir($project); 
     247 
     248                        if (!$this->resolveSymlinks) { 
     249                                $ds->setTreatDirSymlinksAsFiles(true); 
     250                        } 
     251 
    233252            $srcFiles = $ds->getIncludedFiles(); 
    234253            $srcDirs  = $ds->getIncludedDirectories(); 
    235              
     254 
    236255            if (!$this->flatten && $this->mapperElement === null) 
    237256            { 
    238257                                $this->completeDirMap[$fromDir->getAbsolutePath()] = $this->destDir->getAbsolutePath(); 
     
    376395                                        $toSlot->setValue($toFile->getPath()); 
    377396                                        $toBasenameSlot->setValue($toFile->getName()); 
    378397                                         
    379                     FileUtils::copyFile($fromFile, $toFile, $this->overwrite, $this->preserveLMT, $this->filterChains, $this->getProject()); 
     398                    FileUtils::copyFile($fromFile, $toFile, $this->overwrite, $this->preserveLMT, $this->filterChains, $this->getProject(), $this->resolveSymlinks); 
    380399                         
    381400                    $count++; 
    382401                } catch (IOException $ioe) { 
  • classes/phing/util/DirectoryScanner.php

    old new  
    188188    /** if there are no deselected files */ 
    189189    protected $everythingIncluded = true;         
    190190 
     191        protected $treatDirSymlinksAsFiles = false; 
     192 
    191193    /** 
    192194     * Does the path match the start of this pattern up to the first "**". 
    193195     * This is a static mehtod and should always be called static 
     
    419421        return $list; 
    420422    } 
    421423 
     424        public function setTreatDirSymlinksAsFiles($bool = true)  
     425        { 
     426                $this->treatDirSymlinksAsFiles = (bool) $bool; 
     427        } 
     428 
    422429    /** 
    423430     * Scans the passed dir for files and directories. Found files and 
    424431     * directories are placed in their respective collections, based on the 
     
    450457            $file = $_rootdir . DIRECTORY_SEPARATOR . $newfiles[$i]; 
    451458            $name = $_vpath . $newfiles[$i]; 
    452459 
    453             if (@is_dir($file)) { 
     460            if (@is_dir($file) && !(@is_link($file) && $this->treatDirSymlinksAsFiles)) { 
    454461                if ($this->isIncluded($name)) { 
    455462                    if (!$this->isExcluded($name)) { 
    456463                        if ($this->isSelected($name, $file)) { 
  • classes/phing/util/FileUtils.php

    old new  
    7070     * @param boolean $preserveLastModified 
    7171     * @param array $filterChains  
    7272     * @param Project $project 
     73         * @param boolean $resolveSymlinks 
    7374     * @return void 
    7475     */ 
    75     public static function copyFile(File $sourceFile, File $destFile, $overwrite = false, $preserveLastModified = true, &$filterChains = null, Project $project) { 
     76    public static function copyFile(File $sourceFile, File $destFile, $overwrite = false, $preserveLastModified = true, &$filterChains = null, Project $project, $resolveSymlinks = true) { 
    7677        
    7778        if ($overwrite || !$destFile->exists() || $destFile->lastModified() < $sourceFile->lastModified()) { 
    7879            if ($destFile->exists() && $destFile->isFile()) { 
     
    101102                    $out->close(); 
    102103            } else { 
    103104                // simple copy (no filtering) 
    104                 $sourceFile->copyTo($destFile); 
     105                $sourceFile->copyTo($destFile, $resolveSymlinks); 
    105106            } 
    106107 
    107             if ($preserveLastModified) { 
    108                 $destFile->setLastModified($sourceFile->lastModified()); 
    109             } 
     108                        // changing LMT on symlinks fails for some reason so we attempt it  
     109                        // only if we are sure the created file is not a symlink (either it 
     110                        // was resolved to file content due to the resolveSymlinks flag or  
     111                        // the source file wasn't symlink in the first place) 
     112                         
     113                        if ($resolveSymlinks || !$sourceFile->isLink()) { 
     114                                if ($preserveLastModified) { 
     115                                        $destFile->setLastModified($sourceFile->lastModified()); 
     116                                } 
     117                        } 
    110118 
    111119        } 
    112120    }