From 89e095b4b55c6bca1c3d36d97a937f01c1268e9f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 14 Apr 2012 15:45:25 +0200 Subject: [PATCH] Handle --dev installs/updates --- src/Composer/Factory.php | 15 +- src/Composer/Installer.php | 152 +++++++++++------- .../Installer/InstallationManager.php | 30 ++-- src/Composer/Installer/InstallerInstaller.php | 25 +-- src/Composer/Installer/InstallerInterface.php | 14 +- src/Composer/Installer/LibraryInstaller.php | 34 ++-- .../Installer/MetapackageInstaller.php | 36 ++--- src/Composer/Package/Locker.php | 44 +++-- src/Composer/Repository/RepositoryManager.php | 31 ++++ .../installer-v1/Installer/Custom.php | 9 +- .../installer-v2/Installer/Custom2.php | 9 +- .../installer-v3/Installer/Custom2.php | 9 +- .../Installer/InstallationManagerTest.php | 36 +++-- .../Test/Installer/InstallerInstallerTest.php | 18 +-- .../Test/Installer/LibraryInstallerTest.php | 36 ++--- .../Installer/MetapackageInstallerTest.php | 12 +- tests/Composer/Test/Package/LockerTest.php | 5 +- 17 files changed, 299 insertions(+), 216 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 1db5c11db..9f32ec27c 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -152,6 +152,7 @@ class Factory protected function addLocalRepository(RepositoryManager $rm, $vendorDir) { $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/.composer/installed.json'))); + $rm->setLocalDevRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/.composer/installed_dev.json'))); } protected function addPackagistRepository(array $localConfig) @@ -202,18 +203,20 @@ class Factory protected function createInstallationManager(Repository\RepositoryManager $rm, Downloader\DownloadManager $dm, $vendorDir, $binDir, IOInterface $io) { $im = new Installer\InstallationManager($vendorDir); - $im->addInstaller(new Installer\LibraryInstaller($vendorDir, $binDir, $dm, $rm->getLocalRepository(), $io, null)); - $im->addInstaller(new Installer\InstallerInstaller($vendorDir, $binDir, $dm, $rm->getLocalRepository(), $io, $im)); - $im->addInstaller(new Installer\MetapackageInstaller($rm->getLocalRepository(), $io)); + $im->addInstaller(new Installer\LibraryInstaller($vendorDir, $binDir, $dm, $io, null)); + $im->addInstaller(new Installer\InstallerInstaller($vendorDir, $binDir, $dm, $io, $im, $rm->getLocalRepositories())); + $im->addInstaller(new Installer\MetapackageInstaller($io)); return $im; } protected function purgePackages(Repository\RepositoryManager $rm, Installer\InstallationManager $im) { - foreach ($rm->getLocalRepository()->getPackages() as $package) { - if (!$im->isPackageInstalled($package)) { - $rm->getLocalRepository()->removePackage($package); + foreach ($rm->getLocalRepositories() as $repo) { + foreach ($repo->getPackages() as $package) { + if (!$im->isPackageInstalled($repo, $package)) { + $repo->removePackage($package); + } } } } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index e459c94ca..dca27a6eb 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -122,33 +122,14 @@ class Installer $this->downloadManager->setPreferSource(true); } - // create local repo, this contains all packages that are installed in the local project - $localRepo = $this->repositoryManager->getLocalRepository(); // create installed repo, this contains all local packages + platform packages (php & extensions) - $installedRepo = new CompositeRepository(array($localRepo, new PlatformRepository())); + $repos = array_merge($this->repositoryManager->getLocalRepositories(), array(new PlatformRepository())); + $installedRepo = new CompositeRepository($repos); if ($this->additionalInstalledRepository) { $installedRepo->addRepository($this->additionalInstalledRepository); } - // prepare aliased packages - if (!$this->update && $this->locker->isLocked()) { - $aliases = $this->locker->getAliases(); - } else { - $aliases = $this->package->getAliases(); - } - foreach ($aliases as $alias) { - foreach ($this->repositoryManager->findPackages($alias['package'], $alias['version']) as $package) { - $package->setAlias($alias['alias_normalized']); - $package->setPrettyAlias($alias['alias']); - $package->getRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); - } - foreach ($this->repositoryManager->getLocalRepository()->findPackages($alias['package'], $alias['version']) as $package) { - $package->setAlias($alias['alias_normalized']); - $package->setPrettyAlias($alias['alias']); - $this->repositoryManager->getLocalRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); - $this->repositoryManager->getLocalRepository()->removePackage($package); - } - } + $aliases = $this->aliasPackages(); // creating repository pool $pool = new Pool; @@ -157,34 +138,74 @@ class Installer $pool->addRepository($repository); } - // dispatch pre event if (!$this->dryRun) { + // dispatch pre event $eventName = $this->update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD; $this->eventDispatcher->dispatchCommandEvent($eventName); } + $suggestedPackages = $this->doInstall($this->repositoryManager->getLocalRepository(), $installedRepo, $pool, $aliases); + if ($this->devMode) { + $devSuggested = $this->doInstall($this->repositoryManager->getLocalDevRepository(), $installedRepo, $pool, $aliases, true); + $suggestedPackages = array_merge($suggestedPackages, $devSuggested); + } + + // dump suggestions + foreach ($suggestedPackages as $suggestion) { + $this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')'); + } + + if (!$this->dryRun) { + // write lock + if ($this->update || !$this->locker->isLocked()) { + $updatedLock = $this->locker->setLockData( + $this->repositoryManager->getLocalRepository()->getPackages(), + $this->repositoryManager->getLocalDevRepository()->getPackages(), + $aliases + ); + if ($updatedLock) { + $this->io->write('Writing lock file'); + } + } + + // write autoloader + $this->io->write('Generating autoload files'); + $generator = new AutoloadGenerator; + $localRepos = new CompositeRepository($this->repositoryManager->getLocalRepositories()); + $generator->dump($localRepos, $this->package, $this->installationManager, $this->installationManager->getVendorPath().'/.composer'); + + // dispatch post event + $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; + $this->eventDispatcher->dispatchCommandEvent($eventName); + } + + return true; + } + + protected function doInstall($localRepo, $installedRepo, $pool, $aliases, $devMode = false) + { // creating requirements request $installFromLock = false; $request = new Request($pool); if ($this->update) { - $this->io->write('Updating dependencies'); + $this->io->write('Updating '.($devMode ? 'dev ': '').'dependencies'); $request->updateAll(); - $links = $this->collectLinks(); + $links = $devMode ? $this->package->getDevRequires() : $this->package->getRequires(); foreach ($links as $link) { $request->install($link->getTarget(), $link->getConstraint()); } } elseif ($this->locker->isLocked()) { $installFromLock = true; - $this->io->write('Installing from lock file'); + $this->io->write('Installing '.($devMode ? 'dev ': '').'dependencies from lock file'); - if (!$this->locker->isFresh()) { + if (!$this->locker->isFresh() && !$devMode) { $this->io->write('Your lock file is out of sync with your composer.json, run "composer.phar update" to update dependencies'); } - foreach ($this->locker->getLockedPackages() as $package) { + foreach ($this->locker->getLockedPackages($devMode) as $package) { $version = $package->getVersion(); foreach ($aliases as $alias) { if ($alias['package'] === $package->getName() && $alias['version'] === $package->getVersion()) { @@ -196,15 +217,25 @@ class Installer $request->install($package->getName(), $constraint); } } else { - $this->io->write('Installing dependencies'); + $this->io->write('Installing '.($devMode ? 'dev ': '').'dependencies'); - $links = $this->collectLinks(); + $links = $devMode ? $this->package->getDevRequires() : $this->package->getRequires(); foreach ($links as $link) { $request->install($link->getTarget(), $link->getConstraint()); } } + // fix the version all installed packages that are not in the current local repo to prevent rogue updates + foreach ($installedRepo->getPackages() as $package) { + if ($package->getRepository() === $localRepo || $package->getRepository() instanceof PlatformRepository) { + continue; + } + + $constraint = new VersionConstraint('=', $package->getVersion()); + $request->install($package->getName(), $constraint); + } + // prepare solver $policy = new DefaultPolicy(); $solver = new Solver($policy, $pool, $installedRepo); @@ -261,16 +292,16 @@ class Installer } // anti-alias local repository to allow updates to work fine - foreach ($this->repositoryManager->getLocalRepository()->getPackages() as $package) { + foreach ($localRepo->getPackages() as $package) { if ($package instanceof AliasPackage) { - $this->repositoryManager->getLocalRepository()->addPackage(clone $package->getAliasOf()); - $this->repositoryManager->getLocalRepository()->removePackage($package); + $package->getRepository()->addPackage(clone $package->getAliasOf()); + $package->getRepository()->removePackage($package); } } // execute operations if (!$operations) { - $this->io->write('Nothing to install or update'); + $this->io->write('Nothing to install or update'); } $suggestedPackages = array(); @@ -311,7 +342,7 @@ class Installer } } } - $this->installationManager->execute($operation); + $this->installationManager->execute($localRepo, $operation); $this->eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation); @@ -319,37 +350,34 @@ class Installer } } - // dump suggestions - foreach ($suggestedPackages as $suggestion) { - $this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')'); - } - - if (!$this->dryRun) { - // write lock - if ($this->update || !$this->locker->isLocked()) { - if ($this->locker->setLockData($localRepo->getPackages(), $aliases)) { - $this->io->write('Writing lock file'); - } - } - - // write autoloader - $this->io->write('Generating autoload files'); - $generator = new AutoloadGenerator; - $generator->dump($localRepo, $this->package, $this->installationManager, $this->installationManager->getVendorPath().'/.composer'); - - // dispatch post event - $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; - $this->eventDispatcher->dispatchCommandEvent($eventName); - } - - return true; + return $suggestedPackages; } - private function collectLinks() + private function aliasPackages() { - $links = $this->package->getRequires(); + if (!$this->update && $this->locker->isLocked()) { + $aliases = $this->locker->getAliases(); + } else { + $aliases = $this->package->getAliases(); + } - return $links; + foreach ($aliases as $alias) { + foreach ($this->repositoryManager->findPackages($alias['package'], $alias['version']) as $package) { + $package->setAlias($alias['alias_normalized']); + $package->setPrettyAlias($alias['alias']); + $package->getRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); + } + foreach ($this->repositoryManager->getLocalRepositories() as $repo) { + foreach ($repo->findPackages($alias['package'], $alias['version']) as $package) { + $package->setAlias($alias['alias_normalized']); + $package->setPrettyAlias($alias['alias']); + $package->getRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias'])); + $package->getRepository()->removePackage($package); + } + } + } + + return $aliases; } /** diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 6f397b358..c015bd9f9 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -14,6 +14,7 @@ namespace Composer\Installer; use Composer\Package\PackageInterface; use Composer\Package\AliasPackage; +use Composer\Repository\RepositoryInterface; use Composer\Repository\NotifiableRepositoryInterface; use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\InstallOperation; @@ -95,32 +96,35 @@ class InstallationManager /** * Checks whether provided package is installed in one of the registered installers. * + * @param RepositoryInterface $repo repository in which to check * @param PackageInterface $package package instance * * @return Boolean */ - public function isPackageInstalled(PackageInterface $package) + public function isPackageInstalled(RepositoryInterface $repo, PackageInterface $package) { - return $this->getInstaller($package->getType())->isInstalled($package); + return $this->getInstaller($package->getType())->isInstalled($repo, $package); } /** * Executes solver operation. * + * @param RepositoryInterface $repo repository in which to check * @param OperationInterface $operation operation instance */ - public function execute(OperationInterface $operation) + public function execute(RepositoryInterface $repo, OperationInterface $operation) { $method = $operation->getJobType(); - $this->$method($operation); + $this->$method($repo, $operation); } /** * Executes install operation. * + * @param RepositoryInterface $repo repository in which to check * @param InstallOperation $operation operation instance */ - public function install(InstallOperation $operation) + public function install(RepositoryInterface $repo, InstallOperation $operation) { $package = $operation->getPackage(); if ($package instanceof AliasPackage) { @@ -128,16 +132,17 @@ class InstallationManager $package->setInstalledAsAlias(true); } $installer = $this->getInstaller($package->getType()); - $installer->install($package); + $installer->install($repo, $package); $this->notifyInstall($package); } /** * Executes update operation. * + * @param RepositoryInterface $repo repository in which to check * @param InstallOperation $operation operation instance */ - public function update(UpdateOperation $operation) + public function update(RepositoryInterface $repo, UpdateOperation $operation) { $initial = $operation->getInitialPackage(); if ($initial instanceof AliasPackage) { @@ -154,27 +159,28 @@ class InstallationManager if ($initialType === $targetType) { $installer = $this->getInstaller($initialType); - $installer->update($initial, $target); + $installer->update($repo, $initial, $target); $this->notifyInstall($target); } else { - $this->getInstaller($initialType)->uninstall($initial); - $this->getInstaller($targetType)->install($target); + $this->getInstaller($initialType)->uninstall($repo, $initial); + $this->getInstaller($targetType)->install($repo, $target); } } /** * Uninstalls package. * + * @param RepositoryInterface $repo repository in which to check * @param UninstallOperation $operation operation instance */ - public function uninstall(UninstallOperation $operation) + public function uninstall(RepositoryInterface $repo, UninstallOperation $operation) { $package = $operation->getPackage(); if ($package instanceof AliasPackage) { $package = $package->getAliasOf(); } $installer = $this->getInstaller($package->getType()); - $installer->uninstall($package); + $installer->uninstall($repo, $package); } /** diff --git a/src/Composer/Installer/InstallerInstaller.php b/src/Composer/Installer/InstallerInstaller.php index a43d0f15a..9519d384b 100644 --- a/src/Composer/Installer/InstallerInstaller.php +++ b/src/Composer/Installer/InstallerInstaller.php @@ -33,17 +33,20 @@ class InstallerInstaller extends LibraryInstaller * @param string $vendorDir relative path for packages home * @param string $binDir relative path for binaries * @param DownloadManager $dm download manager - * @param WritableRepositoryInterface $repository repository controller * @param IOInterface $io io instance + * @param InstallationManager $im installation manager + * @param array $localRepositories array of WritableRepositoryInterface */ - public function __construct($vendorDir, $binDir, DownloadManager $dm, WritableRepositoryInterface $repository, IOInterface $io, InstallationManager $im) + public function __construct($vendorDir, $binDir, DownloadManager $dm, IOInterface $io, InstallationManager $im, array $localRepositories) { - parent::__construct($vendorDir, $binDir, $dm, $repository, $io, 'composer-installer'); + parent::__construct($vendorDir, $binDir, $dm, $io, 'composer-installer'); $this->installationManager = $im; - foreach ($repository->getPackages() as $package) { - if ('composer-installer' === $package->getType()) { - $this->registerInstaller($package); + foreach ($localRepositories as $repo) { + foreach ($repo->getPackages() as $package) { + if ('composer-installer' === $package->getType()) { + $this->registerInstaller($package); + } } } } @@ -51,28 +54,28 @@ class InstallerInstaller extends LibraryInstaller /** * {@inheritDoc} */ - public function install(PackageInterface $package) + public function install(WritableRepositoryInterface $repo, PackageInterface $package) { $extra = $package->getExtra(); if (empty($extra['class'])) { throw new \UnexpectedValueException('Error while installing '.$package->getPrettyName().', composer-installer packages should have a class defined in their extra key to be usable.'); } - parent::install($package); + parent::install($repo, $package); $this->registerInstaller($package); } /** * {@inheritDoc} */ - public function update(PackageInterface $initial, PackageInterface $target) + public function update(WritableRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) { $extra = $target->getExtra(); if (empty($extra['class'])) { throw new \UnexpectedValueException('Error while installing '.$target->getPrettyName().', composer-installer packages should have a class defined in their extra key to be usable.'); } - parent::update($initial, $target); + parent::update($repo, $initial, $target); $this->registerInstaller($target); } @@ -97,7 +100,7 @@ class InstallerInstaller extends LibraryInstaller } $extra = $package->getExtra(); - $installer = new $class($this->vendorDir, $this->binDir, $this->downloadManager, $this->repository, $this->io); + $installer = new $class($this->vendorDir, $this->binDir, $this->downloadManager, $this->io); $this->installationManager->addInstaller($installer); } } diff --git a/src/Composer/Installer/InstallerInterface.php b/src/Composer/Installer/InstallerInterface.php index 36c023b3b..3716ec6df 100644 --- a/src/Composer/Installer/InstallerInterface.php +++ b/src/Composer/Installer/InstallerInterface.php @@ -14,11 +14,13 @@ namespace Composer\Installer; use Composer\DependencyResolver\Operation\OperationInterface; use Composer\Package\PackageInterface; +use Composer\Repository\WritableRepositoryInterface; /** * Interface for the package installation manager. * * @author Konstantin Kudryashov + * @author Jordi Boggiano */ interface InstallerInterface { @@ -33,35 +35,39 @@ interface InstallerInterface /** * Checks that provided package is installed. * + * @param WritableRepositoryInterface $repo repository in which to check * @param PackageInterface $package package instance * * @return Boolean */ - function isInstalled(PackageInterface $package); + function isInstalled(WritableRepositoryInterface $repo, PackageInterface $package); /** * Installs specific package. * + * @param WritableRepositoryInterface $repo repository in which to check * @param PackageInterface $package package instance */ - function install(PackageInterface $package); + function install(WritableRepositoryInterface $repo, PackageInterface $package); /** * Updates specific package. * + * @param WritableRepositoryInterface $repo repository in which to check * @param PackageInterface $initial already installed package version * @param PackageInterface $target updated version * * @throws InvalidArgumentException if $from package is not installed */ - function update(PackageInterface $initial, PackageInterface $target); + function update(WritableRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target); /** * Uninstalls specific package. * + * @param WritableRepositoryInterface $repo repository in which to check * @param PackageInterface $package package instance */ - function uninstall(PackageInterface $package); + function uninstall(WritableRepositoryInterface $repo, PackageInterface $package); /** * Returns the installation path of a package diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index bef93cf55..baff5fdeb 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -30,7 +30,6 @@ class LibraryInstaller implements InstallerInterface protected $vendorDir; protected $binDir; protected $downloadManager; - protected $repository; protected $io; private $type; private $filesystem; @@ -41,14 +40,12 @@ class LibraryInstaller implements InstallerInterface * @param string $vendorDir relative path for packages home * @param string $binDir relative path for binaries * @param DownloadManager $dm download manager - * @param WritableRepositoryInterface $repository repository controller * @param IOInterface $io io instance * @param string $type package type that this installer handles */ - public function __construct($vendorDir, $binDir, DownloadManager $dm, WritableRepositoryInterface $repository, IOInterface $io, $type = 'library') + public function __construct($vendorDir, $binDir, DownloadManager $dm, IOInterface $io, $type = 'library') { $this->downloadManager = $dm; - $this->repository = $repository; $this->io = $io; $this->type = $type; @@ -68,37 +65,37 @@ class LibraryInstaller implements InstallerInterface /** * {@inheritDoc} */ - public function isInstalled(PackageInterface $package) + public function isInstalled(WritableRepositoryInterface $repo, PackageInterface $package) { - return $this->repository->hasPackage($package) && is_readable($this->getInstallPath($package)); + return $repo->hasPackage($package) && is_readable($this->getInstallPath($package)); } /** * {@inheritDoc} */ - public function install(PackageInterface $package) + public function install(WritableRepositoryInterface $repo, PackageInterface $package) { $this->initializeVendorDir(); $downloadPath = $this->getInstallPath($package); // remove the binaries if it appears the package files are missing - if (!is_readable($downloadPath) && $this->repository->hasPackage($package)) { + if (!is_readable($downloadPath) && $repo->hasPackage($package)) { $this->removeBinaries($package); } $this->downloadManager->download($package, $downloadPath); $this->installBinaries($package); - if (!$this->repository->hasPackage($package)) { - $this->repository->addPackage(clone $package); + if (!$repo->hasPackage($package)) { + $repo->addPackage(clone $package); } } /** * {@inheritDoc} */ - public function update(PackageInterface $initial, PackageInterface $target) + public function update(WritableRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) { - if (!$this->repository->hasPackage($initial)) { + if (!$repo->hasPackage($initial)) { throw new \InvalidArgumentException('Package is not installed: '.$initial); } @@ -108,18 +105,18 @@ class LibraryInstaller implements InstallerInterface $this->removeBinaries($initial); $this->downloadManager->update($initial, $target, $downloadPath); $this->installBinaries($target); - $this->repository->removePackage($initial); - if (!$this->repository->hasPackage($target)) { - $this->repository->addPackage(clone $target); + $repo->removePackage($initial); + if (!$repo->hasPackage($target)) { + $repo->addPackage(clone $target); } } /** * {@inheritDoc} */ - public function uninstall(PackageInterface $package) + public function uninstall(WritableRepositoryInterface $repo, PackageInterface $package) { - if (!$this->repository->hasPackage($package)) { + if (!$repo->hasPackage($package)) { // TODO throw exception again here, when update is fixed and we don't have to remove+install (see #125) return; throw new \InvalidArgumentException('Package is not installed: '.$package); @@ -129,7 +126,7 @@ class LibraryInstaller implements InstallerInterface $this->downloadManager->remove($package, $downloadPath); $this->removeBinaries($package); - $this->repository->removePackage($package); + $repo->removePackage($package); } /** @@ -138,6 +135,7 @@ class LibraryInstaller implements InstallerInterface public function getInstallPath(PackageInterface $package) { $targetDir = $package->getTargetDir(); + return ($this->vendorDir ? $this->vendorDir.'/' : '') . $package->getPrettyName() . ($targetDir ? '/'.$targetDir : ''); } diff --git a/src/Composer/Installer/MetapackageInstaller.php b/src/Composer/Installer/MetapackageInstaller.php index ceee0f1a3..9dcdbd3d0 100644 --- a/src/Composer/Installer/MetapackageInstaller.php +++ b/src/Composer/Installer/MetapackageInstaller.php @@ -12,7 +12,6 @@ namespace Composer\Installer; -use Composer\IO\IOInterface; use Composer\Repository\WritableRepositoryInterface; use Composer\Package\PackageInterface; @@ -23,19 +22,6 @@ use Composer\Package\PackageInterface; */ class MetapackageInstaller implements InstallerInterface { - protected $repository; - protected $io; - - /** - * @param WritableRepositoryInterface $repository repository controller - * @param IOInterface $io io instance - */ - public function __construct(WritableRepositoryInterface $repository, IOInterface $io) - { - $this->repository = $repository; - $this->io = $io; - } - /** * {@inheritDoc} */ @@ -47,44 +33,44 @@ class MetapackageInstaller implements InstallerInterface /** * {@inheritDoc} */ - public function isInstalled(PackageInterface $package) + public function isInstalled(WritableRepositoryInterface $repo, PackageInterface $package) { - return $this->repository->hasPackage($package); + return $repo->hasPackage($package); } /** * {@inheritDoc} */ - public function install(PackageInterface $package) + public function install(WritableRepositoryInterface $repo, PackageInterface $package) { - $this->repository->addPackage(clone $package); + $repo->addPackage(clone $package); } /** * {@inheritDoc} */ - public function update(PackageInterface $initial, PackageInterface $target) + public function update(WritableRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) { - if (!$this->repository->hasPackage($initial)) { + if (!$repo->hasPackage($initial)) { throw new \InvalidArgumentException('Package is not installed: '.$initial); } - $this->repository->removePackage($initial); - $this->repository->addPackage(clone $target); + $repo->removePackage($initial); + $repo->addPackage(clone $target); } /** * {@inheritDoc} */ - public function uninstall(PackageInterface $package) + public function uninstall(WritableRepositoryInterface $repo, PackageInterface $package) { - if (!$this->repository->hasPackage($package)) { + if (!$repo->hasPackage($package)) { // TODO throw exception again here, when update is fixed and we don't have to remove+install (see #125) return; throw new \InvalidArgumentException('Package is not installed: '.$package); } - $this->repository->removePackage($package); + $repo->removePackage($package); } /** diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index cc5112835..5f11e419d 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -69,13 +69,17 @@ class Locker * * @return array */ - public function getLockedPackages() + public function getLockedPackages($dev = false) { $lockList = $this->getLockData(); $packages = array(); - foreach ($lockList['packages'] as $info) { + + $lockedPackages = $dev ? $lockList['packages-dev'] : $lockList['packages']; + $repo = $dev ? $this->repositoryManager->getLocalDevRepository() : $this->repositoryManager->getLocalRepository(); + + foreach ($lockedPackages as $info) { $resolvedVersion = !empty($info['alias']) ? $info['alias'] : $info['version']; - $package = $this->repositoryManager->getLocalRepository()->findPackage($info['package'], $resolvedVersion); + $package = $repo->findPackage($info['package'], $resolvedVersion); if (!$package) { $package = $this->repositoryManager->findPackage($info['package'], $info['version']); @@ -120,18 +124,37 @@ class Locker * Locks provided data into lockfile. * * @param array $packages array of packages + * @param array $packages array of dev packages * @param array $aliases array of aliases * * @return Boolean */ - public function setLockData(array $packages, array $aliases) + public function setLockData(array $packages, array $devPackages, array $aliases) { $lock = array( 'hash' => $this->hash, 'packages' => array(), + 'packages-dev' => array(), 'aliases' => $aliases, ); + $lock['packages'] = $this->lockPackages($packages); + $lock['packages-dev'] = $this->lockPackages($devPackages); + + if (!$this->isLocked() || $lock !== $this->getLockData()) { + $this->lockFile->write($lock); + $this->lockDataCache = null; + + return true; + } + + return false; + } + + private function lockPackages(array $packages) + { + $locked = array(); + foreach ($packages as $package) { $name = $package->getPrettyName(); $version = $package->getPrettyVersion(); @@ -152,20 +175,13 @@ class Locker $spec['alias'] = $package->getAlias(); } - $lock['packages'][] = $spec; + $locked[] = $spec; } - usort($lock['packages'], function ($a, $b) { + usort($locked, function ($a, $b) { return strcmp($a['package'], $b['package']); }); - if (!$this->isLocked() || $lock !== $this->getLockData()) { - $this->lockFile->write($lock); - $this->lockDataCache = null; - - return true; - } - - return false; + return $locked; } } diff --git a/src/Composer/Repository/RepositoryManager.php b/src/Composer/Repository/RepositoryManager.php index 8dd684082..febce81a3 100644 --- a/src/Composer/Repository/RepositoryManager.php +++ b/src/Composer/Repository/RepositoryManager.php @@ -25,6 +25,7 @@ use Composer\Config; class RepositoryManager { private $localRepository; + private $localDevRepository; private $repositories = array(); private $repositoryClasses = array(); private $io; @@ -140,4 +141,34 @@ class RepositoryManager { return $this->localRepository; } + + /** + * Sets localDev repository for the project. + * + * @param RepositoryInterface $repository repository instance + */ + public function setLocalDevRepository(RepositoryInterface $repository) + { + $this->localDevRepository = $repository; + } + + /** + * Returns localDev repository for the project. + * + * @return RepositoryInterface + */ + public function getLocalDevRepository() + { + return $this->localDevRepository; + } + + /** + * Returns all local repositories for the project. + * + * @return array[RepositoryInterface] + */ + public function getLocalRepositories() + { + return array($this->localRepository, $this->localDevRepository); + } } diff --git a/tests/Composer/Test/Installer/Fixtures/installer-v1/Installer/Custom.php b/tests/Composer/Test/Installer/Fixtures/installer-v1/Installer/Custom.php index 4bb58ded8..cfda3c3d3 100644 --- a/tests/Composer/Test/Installer/Fixtures/installer-v1/Installer/Custom.php +++ b/tests/Composer/Test/Installer/Fixtures/installer-v1/Installer/Custom.php @@ -4,15 +4,16 @@ namespace Installer; use Composer\Installer\InstallerInterface; use Composer\Package\PackageInterface; +use Composer\Repository\WritableRepositoryInterface; class Custom implements InstallerInterface { public $version = 'installer-v1'; public function supports($packageType) {} - public function isInstalled(PackageInterface $package) {} - public function install(PackageInterface $package) {} - public function update(PackageInterface $initial, PackageInterface $target) {} - public function uninstall(PackageInterface $package) {} + public function isInstalled(WritableRepositoryInterface $repo, PackageInterface $package) {} + public function install(WritableRepositoryInterface $repo, PackageInterface $package) {} + public function update(WritableRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) {} + public function uninstall(WritableRepositoryInterface $repo, PackageInterface $package) {} public function getInstallPath(PackageInterface $package) {} } diff --git a/tests/Composer/Test/Installer/Fixtures/installer-v2/Installer/Custom2.php b/tests/Composer/Test/Installer/Fixtures/installer-v2/Installer/Custom2.php index edd264428..52f46371b 100644 --- a/tests/Composer/Test/Installer/Fixtures/installer-v2/Installer/Custom2.php +++ b/tests/Composer/Test/Installer/Fixtures/installer-v2/Installer/Custom2.php @@ -4,15 +4,16 @@ namespace Installer; use Composer\Installer\InstallerInterface; use Composer\Package\PackageInterface; +use Composer\Repository\WritableRepositoryInterface; class Custom2 implements InstallerInterface { public $version = 'installer-v2'; public function supports($packageType) {} - public function isInstalled(PackageInterface $package) {} - public function install(PackageInterface $package) {} - public function update(PackageInterface $initial, PackageInterface $target) {} - public function uninstall(PackageInterface $package) {} + public function isInstalled(WritableRepositoryInterface $repo, PackageInterface $package) {} + public function install(WritableRepositoryInterface $repo, PackageInterface $package) {} + public function update(WritableRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) {} + public function uninstall(WritableRepositoryInterface $repo, PackageInterface $package) {} public function getInstallPath(PackageInterface $package) {} } diff --git a/tests/Composer/Test/Installer/Fixtures/installer-v3/Installer/Custom2.php b/tests/Composer/Test/Installer/Fixtures/installer-v3/Installer/Custom2.php index db211bed5..ca8ad7897 100644 --- a/tests/Composer/Test/Installer/Fixtures/installer-v3/Installer/Custom2.php +++ b/tests/Composer/Test/Installer/Fixtures/installer-v3/Installer/Custom2.php @@ -4,15 +4,16 @@ namespace Installer; use Composer\Installer\InstallerInterface; use Composer\Package\PackageInterface; +use Composer\Repository\WritableRepositoryInterface; class Custom2 implements InstallerInterface { public $version = 'installer-v3'; public function supports($packageType) {} - public function isInstalled(PackageInterface $package) {} - public function install(PackageInterface $package) {} - public function update(PackageInterface $initial, PackageInterface $target) {} - public function uninstall(PackageInterface $package) {} + public function isInstalled(WritableRepositoryInterface $repo, PackageInterface $package) {} + public function install(WritableRepositoryInterface $repo, PackageInterface $package) {} + public function update(WritableRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) {} + public function uninstall(WritableRepositoryInterface $repo, PackageInterface $package) {} public function getInstallPath(PackageInterface $package) {} } diff --git a/tests/Composer/Test/Installer/InstallationManagerTest.php b/tests/Composer/Test/Installer/InstallationManagerTest.php index 941157a29..f14c08c84 100644 --- a/tests/Composer/Test/Installer/InstallationManagerTest.php +++ b/tests/Composer/Test/Installer/InstallationManagerTest.php @@ -19,6 +19,11 @@ use Composer\DependencyResolver\Operation\UninstallOperation; class InstallationManagerTest extends \PHPUnit_Framework_TestCase { + public function setUp() + { + $this->repository = $this->getMock('Composer\Repository\WritableRepositoryInterface'); + } + public function testVendorDirOutsideTheWorkingDir() { $manager = new InstallationManager(realpath(getcwd().'/../')); @@ -67,22 +72,23 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase $this->createPackageMock(), $this->createPackageMock() ); + $manager ->expects($this->once()) ->method('install') - ->with($installOperation); + ->with($this->repository, $installOperation); $manager ->expects($this->once()) ->method('uninstall') - ->with($removeOperation); + ->with($this->repository, $removeOperation); $manager ->expects($this->once()) ->method('update') - ->with($updateOperation); + ->with($this->repository, $updateOperation); - $manager->execute($installOperation); - $manager->execute($removeOperation); - $manager->execute($updateOperation); + $manager->execute($this->repository, $installOperation); + $manager->execute($this->repository, $removeOperation); + $manager->execute($this->repository, $updateOperation); } public function testInstall() @@ -108,9 +114,9 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase $installer ->expects($this->once()) ->method('install') - ->with($package); + ->with($this->repository, $package); - $manager->install($operation); + $manager->install($this->repository, $operation); } public function testUpdateWithEqualTypes() @@ -141,9 +147,9 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase $installer ->expects($this->once()) ->method('update') - ->with($initial, $target); + ->with($this->repository, $initial, $target); - $manager->update($operation); + $manager->update($this->repository, $operation); } public function testUpdateWithNotEqualTypes() @@ -183,14 +189,14 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase $libInstaller ->expects($this->once()) ->method('uninstall') - ->with($initial); + ->with($this->repository, $initial); $bundleInstaller ->expects($this->once()) ->method('install') - ->with($target); + ->with($this->repository, $target); - $manager->update($operation); + $manager->update($this->repository, $operation); } public function testUninstall() @@ -210,7 +216,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase $installer ->expects($this->once()) ->method('uninstall') - ->with($package); + ->with($this->repository, $package); $installer ->expects($this->once()) @@ -218,7 +224,7 @@ class InstallationManagerTest extends \PHPUnit_Framework_TestCase ->with('library') ->will($this->returnValue(true)); - $manager->uninstall($operation); + $manager->uninstall($this->repository, $operation); } public function testGetVendorPathAbsolute() diff --git a/tests/Composer/Test/Installer/InstallerInstallerTest.php b/tests/Composer/Test/Installer/InstallerInstallerTest.php index eab2d948a..2c107c32c 100644 --- a/tests/Composer/Test/Installer/InstallerInstallerTest.php +++ b/tests/Composer/Test/Installer/InstallerInstallerTest.php @@ -34,11 +34,9 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $this->repository = $this->getMockBuilder('Composer\Repository\WritableRepositoryInterface') - ->getMock(); + $this->repository = $this->getMock('Composer\Repository\WritableRepositoryInterface'); - $this->io = $this->getMockBuilder('Composer\IO\IOInterface') - ->getMock(); + $this->io = $this->getMock('Composer\IO\IOInterface'); } public function testInstallNewInstaller() @@ -47,7 +45,7 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase ->expects($this->once()) ->method('getPackages') ->will($this->returnValue(array())); - $installer = new InstallerInstallerMock(__DIR__.'/Fixtures/', __DIR__.'/Fixtures/bin', $this->dm, $this->repository, $this->io, $this->im); + $installer = new InstallerInstallerMock(__DIR__.'/Fixtures/', __DIR__.'/Fixtures/bin', $this->dm, $this->io, $this->im, array($this->repository)); $test = $this; $this->im @@ -57,7 +55,7 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase $test->assertEquals('installer-v1', $installer->version); })); - $installer->install($this->packages[0]); + $installer->install($this->repository, $this->packages[0]); } public function testUpgradeWithNewClassName() @@ -70,7 +68,7 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase ->expects($this->exactly(2)) ->method('hasPackage') ->will($this->onConsecutiveCalls(true, false)); - $installer = new InstallerInstallerMock(__DIR__.'/Fixtures/', __DIR__.'/Fixtures/bin', $this->dm, $this->repository, $this->io, $this->im); + $installer = new InstallerInstallerMock(__DIR__.'/Fixtures/', __DIR__.'/Fixtures/bin', $this->dm, $this->io, $this->im, array($this->repository)); $test = $this; $this->im @@ -80,7 +78,7 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase $test->assertEquals('installer-v2', $installer->version); })); - $installer->update($this->packages[0], $this->packages[1]); + $installer->update($this->repository, $this->packages[0], $this->packages[1]); } public function testUpgradeWithSameClassName() @@ -93,7 +91,7 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase ->expects($this->exactly(2)) ->method('hasPackage') ->will($this->onConsecutiveCalls(true, false)); - $installer = new InstallerInstallerMock(__DIR__.'/Fixtures/', __DIR__.'/Fixtures/bin', $this->dm, $this->repository, $this->io, $this->im); + $installer = new InstallerInstallerMock(__DIR__.'/Fixtures/', __DIR__.'/Fixtures/bin', $this->dm, $this->io, $this->im, array($this->repository)); $test = $this; $this->im @@ -103,7 +101,7 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase $test->assertEquals('installer-v3', $installer->version); })); - $installer->update($this->packages[1], $this->packages[2]); + $installer->update($this->repository, $this->packages[1], $this->packages[2]); } } diff --git a/tests/Composer/Test/Installer/LibraryInstallerTest.php b/tests/Composer/Test/Installer/LibraryInstallerTest.php index 100ebb60e..7983cf2a2 100644 --- a/tests/Composer/Test/Installer/LibraryInstallerTest.php +++ b/tests/Composer/Test/Installer/LibraryInstallerTest.php @@ -40,11 +40,9 @@ class LibraryInstallerTest extends TestCase ->disableOriginalConstructor() ->getMock(); - $this->repository = $this->getMockBuilder('Composer\Repository\WritableRepositoryInterface') - ->getMock(); + $this->repository = $this->getMock('Composer\Repository\WritableRepositoryInterface'); - $this->io = $this->getMockBuilder('Composer\IO\IOInterface') - ->getMock(); + $this->io = $this->getMock('Composer\IO\IOInterface'); } protected function tearDown() @@ -57,7 +55,7 @@ class LibraryInstallerTest extends TestCase { $this->fs->removeDirectory($this->vendorDir); - new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io); + new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->io); $this->assertFileNotExists($this->vendorDir); } @@ -65,13 +63,13 @@ class LibraryInstallerTest extends TestCase { $this->fs->removeDirectory($this->binDir); - new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io); + new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->io); $this->assertFileNotExists($this->binDir); } public function testIsInstalled() { - $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io); + $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->io); $package = $this->createPackageMock(); $this->repository @@ -80,8 +78,8 @@ class LibraryInstallerTest extends TestCase ->with($package) ->will($this->onConsecutiveCalls(true, false)); - $this->assertTrue($library->isInstalled($package)); - $this->assertFalse($library->isInstalled($package)); + $this->assertTrue($library->isInstalled($this->repository, $package)); + $this->assertFalse($library->isInstalled($this->repository, $package)); } /** @@ -90,7 +88,7 @@ class LibraryInstallerTest extends TestCase */ public function testInstall() { - $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io); + $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->io); $package = $this->createPackageMock(); $package @@ -108,7 +106,7 @@ class LibraryInstallerTest extends TestCase ->method('addPackage') ->with($package); - $library->install($package); + $library->install($this->repository, $package); $this->assertFileExists($this->vendorDir, 'Vendor dir should be created'); $this->assertFileExists($this->binDir, 'Bin dir should be created'); } @@ -119,7 +117,7 @@ class LibraryInstallerTest extends TestCase */ public function testUpdate() { - $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io); + $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->io); $initial = $this->createPackageMock(); $target = $this->createPackageMock(); @@ -148,18 +146,18 @@ class LibraryInstallerTest extends TestCase ->method('addPackage') ->with($target); - $library->update($initial, $target); + $library->update($this->repository, $initial, $target); $this->assertFileExists($this->vendorDir, 'Vendor dir should be created'); $this->assertFileExists($this->binDir, 'Bin dir should be created'); $this->setExpectedException('InvalidArgumentException'); - $library->update($initial, $target); + $library->update($this->repository, $initial, $target); } public function testUninstall() { - $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io); + $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->io); $package = $this->createPackageMock(); $package @@ -183,17 +181,17 @@ class LibraryInstallerTest extends TestCase ->method('removePackage') ->with($package); - $library->uninstall($package); + $library->uninstall($this->repository, $package); // TODO re-enable once #125 is fixed and we throw exceptions again // $this->setExpectedException('InvalidArgumentException'); - $library->uninstall($package); + $library->uninstall($this->repository, $package); } public function testGetInstallPath() { - $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io); + $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->io); $package = $this->createPackageMock(); $package @@ -206,7 +204,7 @@ class LibraryInstallerTest extends TestCase public function testGetInstallPathWithTargetDir() { - $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->repository, $this->io); + $library = new LibraryInstaller($this->vendorDir, $this->binDir, $this->dm, $this->io); $package = $this->createPackageMock(); $package diff --git a/tests/Composer/Test/Installer/MetapackageInstallerTest.php b/tests/Composer/Test/Installer/MetapackageInstallerTest.php index fd71eb1b6..45d1ff605 100644 --- a/tests/Composer/Test/Installer/MetapackageInstallerTest.php +++ b/tests/Composer/Test/Installer/MetapackageInstallerTest.php @@ -26,7 +26,7 @@ class MetapackageInstallerTest extends \PHPUnit_Framework_TestCase $this->io = $this->getMock('Composer\IO\IOInterface'); - $this->installer = new MetapackageInstaller($this->repository, $this->io); + $this->installer = new MetapackageInstaller(); } public function testInstall() @@ -38,7 +38,7 @@ class MetapackageInstallerTest extends \PHPUnit_Framework_TestCase ->method('addPackage') ->with($package); - $this->installer->install($package); + $this->installer->install($this->repository, $package); } public function testUpdate() @@ -62,11 +62,11 @@ class MetapackageInstallerTest extends \PHPUnit_Framework_TestCase ->method('addPackage') ->with($target); - $this->installer->update($initial, $target); + $this->installer->update($this->repository, $initial, $target); $this->setExpectedException('InvalidArgumentException'); - $this->installer->update($initial, $target); + $this->installer->update($this->repository, $initial, $target); } public function testUninstall() @@ -84,12 +84,12 @@ class MetapackageInstallerTest extends \PHPUnit_Framework_TestCase ->method('removePackage') ->with($package); - $this->installer->uninstall($package); + $this->installer->uninstall($this->repository, $package); // TODO re-enable once #125 is fixed and we throw exceptions again // $this->setExpectedException('InvalidArgumentException'); - $this->installer->uninstall($package); + $this->installer->uninstall($this->repository, $package); } private function createPackageMock() diff --git a/tests/Composer/Test/Package/LockerTest.php b/tests/Composer/Test/Package/LockerTest.php index 9c3f3cbe0..a4cbb6e1d 100644 --- a/tests/Composer/Test/Package/LockerTest.php +++ b/tests/Composer/Test/Package/LockerTest.php @@ -151,10 +151,11 @@ class LockerTest extends \PHPUnit_Framework_TestCase array('package' => 'pkg1', 'version' => '1.0.0-beta'), array('package' => 'pkg2', 'version' => '0.1.10') ), + 'packages-dev' => array(), 'aliases' => array(), )); - $locker->setLockData(array($package1, $package2), array()); + $locker->setLockData(array($package1, $package2), array(), array()); } public function testLockBadPackages() @@ -172,7 +173,7 @@ class LockerTest extends \PHPUnit_Framework_TestCase $this->setExpectedException('LogicException'); - $locker->setLockData(array($package1), array()); + $locker->setLockData(array($package1), array(), array()); } public function testIsFresh()