소스 검색

Merge pull request #5100 from helhum/master

Extract binary installation and removal to own class
Jordi Boggiano 9 년 전
부모
커밋
6527bb4166
3개의 변경된 파일222개의 추가작업 그리고 177개의 파일을 삭제
  1. 205 0
      src/Composer/Installer/LibraryBinariesHandler.php
  2. 13 173
      src/Composer/Installer/LibraryInstaller.php
  3. 4 4
      tests/Composer/Test/Installer/LibraryInstallerTest.php

+ 205 - 0
src/Composer/Installer/LibraryBinariesHandler.php

@@ -0,0 +1,205 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Installer;
+
+use Composer\IO\IOInterface;
+use Composer\Package\PackageInterface;
+use Composer\Util\Filesystem;
+use Composer\Util\Platform;
+use Composer\Util\ProcessExecutor;
+use Composer\Util\Silencer;
+
+/**
+ * Package installation manager.
+ *
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ * @author Helmut Hummel <info@helhum.io>
+ */
+class LibraryBinariesHandler
+{
+    protected $binDir;
+    protected $binCompat;
+    protected $io;
+    protected $filesystem;
+
+    /**
+     * LibraryBinaryHandler constructor.
+     *
+     * @param string $binDir
+     * @param string $binCompat
+     * @param IOInterface $io
+     * @param Filesystem $filesystem
+     */
+    public function __construct($binDir, $binCompat, IOInterface $io, Filesystem $filesystem = null)
+    {
+        $this->binDir = $binDir;
+        $this->binCompat = $binCompat;
+        $this->io = $io;
+        $this->filesystem = $filesystem ?: new Filesystem();
+    }
+
+    public function installBinaries(PackageInterface $package, $installPath)
+    {
+        $binaries = $package->getBinaries();
+        if (!$binaries) {
+            return;
+        }
+        foreach ($binaries as $bin) {
+            $binPath = $installPath.'/'.$bin;
+            if (!file_exists($binPath)) {
+                $this->io->writeError('    <warning>Skipped installation of bin '.$bin.' for package '.$package->getName().': file not found in package</warning>');
+                continue;
+            }
+
+            // in case a custom installer returned a relative path for the
+            // $package, we can now safely turn it into a absolute path (as we
+            // already checked the binary's existence). The following helpers
+            // will require absolute paths to work properly.
+            $binPath = realpath($binPath);
+
+            $this->initializeBinDir();
+            $link = $this->binDir.'/'.basename($bin);
+            if (file_exists($link)) {
+                if (is_link($link)) {
+                    // likely leftover from a previous install, make sure
+                    // that the target is still executable in case this
+                    // is a fresh install of the vendor.
+                    Silencer::call('chmod', $link, 0777 & ~umask());
+                }
+                $this->io->writeError('    Skipped installation of bin '.$bin.' for package '.$package->getName().': name conflicts with an existing file');
+                continue;
+            }
+
+            if ($this->binCompat === "auto") {
+                if (Platform::isWindows()) {
+                    $this->installFullBinaries($binPath, $link, $bin, $package);
+                } else {
+                    $this->installSymlinkBinaries($binPath, $link);
+                }
+            } elseif ($this->binCompat === "full") {
+                $this->installFullBinaries($binPath, $link, $bin, $package);
+            }
+            Silencer::call('chmod', $link, 0777 & ~umask());
+        }
+    }
+
+    protected function installFullBinaries($binPath, $link, $bin, PackageInterface $package)
+    {
+        // add unixy support for cygwin and similar environments
+        if ('.bat' !== substr($binPath, -4)) {
+            $this->installUnixyProxyBinaries($binPath, $link);
+            @chmod($link, 0777 & ~umask());
+            $link .= '.bat';
+            if (file_exists($link)) {
+                $this->io->writeError('    Skipped installation of bin '.$bin.'.bat proxy for package '.$package->getName().': a .bat proxy was already installed');
+            }
+        }
+        if (!file_exists($link)) {
+            file_put_contents($link, $this->generateWindowsProxyCode($binPath, $link));
+        }
+    }
+
+    protected function installSymlinkBinaries($binPath, $link)
+    {
+        if (!$this->filesystem->relativeSymlink($binPath, $link)) {
+            $this->installUnixyProxyBinaries($binPath, $link);
+        }
+    }
+
+    protected function installUnixyProxyBinaries($binPath, $link)
+    {
+        file_put_contents($link, $this->generateUnixyProxyCode($binPath, $link));
+    }
+
+    public function removeBinaries(PackageInterface $package)
+    {
+        $binaries = $package->getBinaries();
+        if (!$binaries) {
+            return;
+        }
+        foreach ($binaries as $bin) {
+            $link = $this->binDir.'/'.basename($bin);
+            if (is_link($link) || file_exists($link)) {
+                $this->filesystem->unlink($link);
+            }
+            if (file_exists($link.'.bat')) {
+                $this->filesystem->unlink($link.'.bat');
+            }
+        }
+
+        // attempt removing the bin dir in case it is left empty
+        if ((is_dir($this->binDir)) && ($this->filesystem->isDirEmpty($this->binDir))) {
+            Silencer::call('rmdir', $this->binDir);
+        }
+    }
+
+    protected function initializeBinDir()
+    {
+        $this->filesystem->ensureDirectoryExists($this->binDir);
+        $this->binDir = realpath($this->binDir);
+    }
+
+    protected function generateWindowsProxyCode($bin, $link)
+    {
+        $binPath = $this->filesystem->findShortestPath($link, $bin);
+        if ('.bat' === substr($bin, -4) || '.exe' === substr($bin, -4)) {
+            $caller = 'call';
+        } else {
+            $handle = fopen($bin, 'r');
+            $line = fgets($handle);
+            fclose($handle);
+            if (preg_match('{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m', $line, $match)) {
+                $caller = trim($match[1]);
+            } else {
+                $caller = 'php';
+            }
+        }
+
+        return "@ECHO OFF\r\n".
+            "setlocal DISABLEDELAYEDEXPANSION\r\n".
+            "SET BIN_TARGET=%~dp0/".trim(ProcessExecutor::escape($binPath), '"')."\r\n".
+            "{$caller} \"%BIN_TARGET%\" %*\r\n";
+    }
+
+    protected function generateUnixyProxyCode($bin, $link)
+    {
+        $binPath = $this->filesystem->findShortestPath($link, $bin);
+
+        $binDir = ProcessExecutor::escape(dirname($binPath));
+        $binFile = basename($binPath);
+
+        $proxyCode = <<<PROXY
+#!/usr/bin/env sh
+
+dir=$(d=\${0%[/\\\\]*}; cd "\$d"; cd $binDir && pwd)
+
+# See if we are running in Cygwin by checking for cygpath program
+if command -v 'cygpath' >/dev/null 2>&1; then
+	# Cygwin paths start with /cygdrive/ which will break windows PHP,
+	# so we need to translate the dir path to windows format. However
+	# we could be using cygwin PHP which does not require this, so we
+	# test if the path to PHP starts with /cygdrive/ rather than /usr/bin
+	if [[ $(which php) == /cygdrive/* ]]; then
+		dir=$(cygpath -m "\$dir");
+	fi
+fi
+
+dir=$(echo \$dir | sed 's/ /\ /g')
+"\${dir}/$binFile" "$@"
+
+PROXY;
+
+        return $proxyCode;
+    }
+}

+ 13 - 173
src/Composer/Installer/LibraryInstaller.php

@@ -17,8 +17,6 @@ use Composer\IO\IOInterface;
 use Composer\Repository\InstalledRepositoryInterface;
 use Composer\Package\PackageInterface;
 use Composer\Util\Filesystem;
-use Composer\Util\Platform;
-use Composer\Util\ProcessExecutor;
 use Composer\Util\Silencer;
 
 /**
@@ -37,16 +35,18 @@ class LibraryInstaller implements InstallerInterface
     protected $type;
     protected $filesystem;
     protected $binCompat;
+    protected $libraryBinariesHandler;
 
     /**
      * Initializes library installer.
      *
-     * @param IOInterface $io
-     * @param Composer    $composer
-     * @param string      $type
-     * @param Filesystem  $filesystem
+     * @param IOInterface          $io
+     * @param Composer             $composer
+     * @param string               $type
+     * @param Filesystem           $filesystem
+     * @param LibraryBinariesHandler $libraryBinariesHandler
      */
-    public function __construct(IOInterface $io, Composer $composer, $type = 'library', Filesystem $filesystem = null)
+    public function __construct(IOInterface $io, Composer $composer, $type = 'library', Filesystem $filesystem = null, LibraryBinariesHandler $libraryBinariesHandler = null)
     {
         $this->composer = $composer;
         $this->downloadManager = $composer->getDownloadManager();
@@ -55,8 +55,7 @@ class LibraryInstaller implements InstallerInterface
 
         $this->filesystem = $filesystem ?: new Filesystem();
         $this->vendorDir = rtrim($composer->getConfig()->get('vendor-dir'), '/');
-        $this->binDir = rtrim($composer->getConfig()->get('bin-dir'), '/');
-        $this->binCompat = $composer->getConfig()->get('bin-compat');
+        $this->libraryBinariesHandler = $libraryBinariesHandler ?: new LibraryBinariesHandler(rtrim($composer->getConfig()->get('bin-dir'), '/'), $composer->getConfig()->get('bin-compat'), $this->io, $this->filesystem);
     }
 
     /**
@@ -85,11 +84,11 @@ class LibraryInstaller implements InstallerInterface
 
         // remove the binaries if it appears the package files are missing
         if (!is_readable($downloadPath) && $repo->hasPackage($package)) {
-            $this->removeBinaries($package);
+            $this->libraryBinariesHandler->removeBinaries($package);
         }
 
         $this->installCode($package);
-        $this->installBinaries($package);
+        $this->libraryBinariesHandler->installBinaries($package, $this->getInstallPath($package));
         if (!$repo->hasPackage($package)) {
             $repo->addPackage(clone $package);
         }
@@ -106,9 +105,9 @@ class LibraryInstaller implements InstallerInterface
 
         $this->initializeVendorDir();
 
-        $this->removeBinaries($initial);
+        $this->libraryBinariesHandler->removeBinaries($initial);
         $this->updateCode($initial, $target);
-        $this->installBinaries($target);
+        $this->libraryBinariesHandler->installBinaries($target, $this->getInstallPath($target));
         $repo->removePackage($initial);
         if (!$repo->hasPackage($target)) {
             $repo->addPackage(clone $target);
@@ -125,7 +124,7 @@ class LibraryInstaller implements InstallerInterface
         }
 
         $this->removeCode($package);
-        $this->removeBinaries($package);
+        $this->libraryBinariesHandler->removeBinaries($package);
         $repo->removePackage($package);
 
         $downloadPath = $this->getPackageBasePath($package);
@@ -204,168 +203,9 @@ class LibraryInstaller implements InstallerInterface
         $this->downloadManager->remove($package, $downloadPath);
     }
 
-    protected function getBinaries(PackageInterface $package)
-    {
-        return $package->getBinaries();
-    }
-
-    protected function installBinaries(PackageInterface $package)
-    {
-        $binaries = $this->getBinaries($package);
-        if (!$binaries) {
-            return;
-        }
-        foreach ($binaries as $bin) {
-            $binPath = $this->getInstallPath($package).'/'.$bin;
-            if (!file_exists($binPath)) {
-                $this->io->writeError('    <warning>Skipped installation of bin '.$bin.' for package '.$package->getName().': file not found in package</warning>');
-                continue;
-            }
-
-            // in case a custom installer returned a relative path for the
-            // $package, we can now safely turn it into a absolute path (as we
-            // already checked the binary's existence). The following helpers
-            // will require absolute paths to work properly.
-            $binPath = realpath($binPath);
-
-            $this->initializeBinDir();
-            $link = $this->binDir.'/'.basename($bin);
-            if (file_exists($link)) {
-                if (is_link($link)) {
-                    // likely leftover from a previous install, make sure
-                    // that the target is still executable in case this
-                    // is a fresh install of the vendor.
-                    Silencer::call('chmod', $link, 0777 & ~umask());
-                }
-                $this->io->writeError('    Skipped installation of bin '.$bin.' for package '.$package->getName().': name conflicts with an existing file');
-                continue;
-            }
-
-            if ($this->binCompat === "auto") {
-                if (Platform::isWindows()) {
-                    $this->installFullBinaries($binPath, $link, $bin, $package);
-                } else {
-                    $this->installSymlinkBinaries($binPath, $link);
-                }
-            } elseif ($this->binCompat === "full") {
-                $this->installFullBinaries($binPath, $link, $bin, $package);
-            }
-            Silencer::call('chmod', $link, 0777 & ~umask());
-        }
-    }
-
-    protected function installFullBinaries($binPath, $link, $bin, PackageInterface $package)
-    {
-        // add unixy support for cygwin and similar environments
-        if ('.bat' !== substr($binPath, -4)) {
-            $this->installUnixyProxyBinaries($binPath, $link);
-            @chmod($link, 0777 & ~umask());
-            $link .= '.bat';
-            if (file_exists($link)) {
-                $this->io->writeError('    Skipped installation of bin '.$bin.'.bat proxy for package '.$package->getName().': a .bat proxy was already installed');
-            }
-        }
-        if (!file_exists($link)) {
-            file_put_contents($link, $this->generateWindowsProxyCode($binPath, $link));
-        }
-    }
-
-    protected function installSymlinkBinaries($binPath, $link)
-    {
-        if (!$this->filesystem->relativeSymlink($binPath, $link)) {
-            $this->installUnixyProxyBinaries($binPath, $link);
-        }
-    }
-
-    protected function installUnixyProxyBinaries($binPath, $link)
-    {
-        file_put_contents($link, $this->generateUnixyProxyCode($binPath, $link));
-    }
-
-    protected function removeBinaries(PackageInterface $package)
-    {
-        $binaries = $this->getBinaries($package);
-        if (!$binaries) {
-            return;
-        }
-        foreach ($binaries as $bin) {
-            $link = $this->binDir.'/'.basename($bin);
-            if (is_link($link) || file_exists($link)) {
-                $this->filesystem->unlink($link);
-            }
-            if (file_exists($link.'.bat')) {
-                $this->filesystem->unlink($link.'.bat');
-            }
-        }
-
-        // attempt removing the bin dir in case it is left empty
-        if ((is_dir($this->binDir)) && ($this->filesystem->isDirEmpty($this->binDir))) {
-            Silencer::call('rmdir', $this->binDir);
-        }
-    }
-
     protected function initializeVendorDir()
     {
         $this->filesystem->ensureDirectoryExists($this->vendorDir);
         $this->vendorDir = realpath($this->vendorDir);
     }
-
-    protected function initializeBinDir()
-    {
-        $this->filesystem->ensureDirectoryExists($this->binDir);
-        $this->binDir = realpath($this->binDir);
-    }
-
-    protected function generateWindowsProxyCode($bin, $link)
-    {
-        $binPath = $this->filesystem->findShortestPath($link, $bin);
-        if ('.bat' === substr($bin, -4) || '.exe' === substr($bin, -4)) {
-            $caller = 'call';
-        } else {
-            $handle = fopen($bin, 'r');
-            $line = fgets($handle);
-            fclose($handle);
-            if (preg_match('{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m', $line, $match)) {
-                $caller = trim($match[1]);
-            } else {
-                $caller = 'php';
-            }
-        }
-
-        return "@ECHO OFF\r\n".
-            "setlocal DISABLEDELAYEDEXPANSION\r\n".
-            "SET BIN_TARGET=%~dp0/".trim(ProcessExecutor::escape($binPath), '"')."\r\n".
-            "{$caller} \"%BIN_TARGET%\" %*\r\n";
-    }
-
-    protected function generateUnixyProxyCode($bin, $link)
-    {
-        $binPath = $this->filesystem->findShortestPath($link, $bin);
-
-        $binDir = ProcessExecutor::escape(dirname($binPath));
-        $binFile = basename($binPath);
-
-        $proxyCode = <<<PROXY
-#!/usr/bin/env sh
-
-dir=$(d=\${0%[/\\\\]*}; cd "\$d"; cd $binDir && pwd)
-
-# See if we are running in Cygwin by checking for cygpath program
-if command -v 'cygpath' >/dev/null 2>&1; then
-	# Cygwin paths start with /cygdrive/ which will break windows PHP,
-	# so we need to translate the dir path to windows format. However
-	# we could be using cygwin PHP which does not require this, so we
-	# test if the path to PHP starts with /cygdrive/ rather than /usr/bin
-	if [[ $(which php) == /cygdrive/* ]]; then
-		dir=$(cygpath -m "\$dir");
-	fi
-fi
-
-dir=$(echo \$dir | sed 's/ /\ /g')
-"\${dir}/$binFile" "$@"
-
-PROXY;
-
-        return $proxyCode;
-    }
 }

+ 4 - 4
tests/Composer/Test/Installer/LibraryInstallerTest.php

@@ -143,22 +143,22 @@ class LibraryInstallerTest extends TestCase
         $target  = $this->createPackageMock();
 
         $initial
-            ->expects($this->once())
+            ->expects($this->any())
             ->method('getPrettyName')
             ->will($this->returnValue('package1'));
 
         $initial
-            ->expects($this->once())
+            ->expects($this->any())
             ->method('getTargetDir')
             ->will($this->returnValue('oldtarget'));
 
         $target
-            ->expects($this->once())
+            ->expects($this->any())
             ->method('getPrettyName')
             ->will($this->returnValue('package1'));
 
         $target
-            ->expects($this->once())
+            ->expects($this->any())
             ->method('getTargetDir')
             ->will($this->returnValue('newtarget'));