From 4207fc3b197dc6df0bdc304d5d6ca696deeece23 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 3 Mar 2013 00:41:12 +0100 Subject: [PATCH 1/6] Refactor require-dev handling to use one single repository and a one pass solving, fixes #719, fixes #1185, fixes #1330, fixes #789, fixes #640 --- src/Composer/Factory.php | 11 +- src/Composer/Installer.php | 183 ++++++++++-------- src/Composer/Package/Locker.php | 26 +-- src/Composer/Repository/RepositoryManager.php | 25 +-- tests/Composer/Test/CacheTest.php | 4 +- .../Test/Fixtures/installer/update-all.test | 5 +- tests/Composer/Test/InstallerTest.php | 18 +- 7 files changed, 123 insertions(+), 149 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 64ea0724f..5014b3724 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -291,7 +291,6 @@ 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'))); } /** @@ -345,12 +344,10 @@ class Factory */ protected function purgePackages(Repository\RepositoryManager $rm, Installer\InstallationManager $im) { - foreach ($rm->getLocalRepositories() as $repo) { - /* @var $repo Repository\WritableRepositoryInterface */ - foreach ($repo->getPackages() as $package) { - if (!$im->isPackageInstalled($repo, $package)) { - $repo->removePackage($package); - } + $repo = $rm->getLocalRepository(); + 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 ac7838537..e6a4138a7 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -160,13 +160,12 @@ class Installer $installedRootPackage->setRequires(array()); $installedRootPackage->setDevRequires(array()); + $localRepo = $this->repositoryManager->getLocalRepository(); $platformRepo = new PlatformRepository(); - $repos = array_merge( - $this->repositoryManager->getLocalRepositories(), - array( - new InstalledArrayRepository(array($installedRootPackage)), - $platformRepo, - ) + $repos = array( + $localRepo, + new InstalledArrayRepository(array($installedRootPackage)), + $platformRepo, ); $installedRepo = new CompositeRepository($repos); if ($this->additionalInstalledRepository) { @@ -184,14 +183,9 @@ class Installer try { $this->suggestedPackages = array(); - if (!$this->doInstall($this->repositoryManager->getLocalRepository(), $installedRepo, $aliases)) { + if (!$this->doInstall($localRepo, $installedRepo, $platformRepo, $aliases, $this->devMode)) { return false; } - if ($this->devMode) { - if (!$this->doInstall($this->repositoryManager->getLocalDevRepository(), $installedRepo, $aliases, true)) { - return false; - } - } } catch (\Exception $e) { $this->installationManager->notifyInstalls(); @@ -214,9 +208,34 @@ class Installer if (!$this->dryRun) { // write lock if ($this->update || !$this->locker->isLocked()) { + $devPackages = $this->devMode ? array() : null; + $localRepo->reload(); + + // split dev and non-dev requirements by checking what would be removed if we update without the dev requirements + if ($this->devMode && $this->package->getDevRequires()) { + $policy = new DefaultPolicy(); + $pool = $this->createPool(); + $pool->addRepository($installedRepo, $aliases); + + // creating requirements request + $request = $this->createRequest($pool, $this->package, $platformRepo); + $request->updateAll(); + foreach ($this->package->getRequires() as $link) { + $request->install($link->getTarget(), $link->getConstraint()); + } + + $solver = new Solver($policy, $pool, $installedRepo); + $ops = $solver->solve($request); + foreach ($ops as $op) { + if ($op->getJobType() === 'uninstall') { + $devPackages[] = $op->getPackage(); + } + } + } + $updatedLock = $this->locker->setLockData( - $this->repositoryManager->getLocalRepository()->getPackages(), - $this->devMode ? $this->repositoryManager->getLocalDevRepository()->getPackages() : null, + array_diff($localRepo->getPackages(), (array) $devPackages), + $devPackages, $aliases, $this->package->getMinimumStability(), $this->package->getStabilityFlags() @@ -228,8 +247,7 @@ class Installer // write autoloader $this->io->write('Generating autoload files'); - $localRepos = new CompositeRepository($this->repositoryManager->getLocalRepositories()); - $this->autoloadGenerator->dump($this->config, $localRepos, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader); + $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader); if ($this->runScripts) { // dispatch post event @@ -241,27 +259,22 @@ class Installer return true; } - protected function doInstall($localRepo, $installedRepo, $aliases, $devMode = false) + protected function doInstall($localRepo, $installedRepo, $platformRepo, $aliases, $withDevReqs) { - $minimumStability = $this->package->getMinimumStability(); - $stabilityFlags = $this->package->getStabilityFlags(); - // init vars $lockedRepository = null; $repositories = null; // initialize locker to create aliased packages $installFromLock = false; - if (!$this->update && $this->locker->isLocked($devMode)) { + if (!$this->update && $this->locker->isLocked()) { $installFromLock = true; - $lockedRepository = $this->locker->getLockedRepository($devMode); - $minimumStability = $this->locker->getMinimumStability(); - $stabilityFlags = $this->locker->getStabilityFlags(); + $lockedRepository = $this->locker->getLockedRepository($withDevReqs); } $this->whitelistUpdateDependencies( $localRepo, - $devMode, + $withDevReqs, $this->package->getRequires(), $this->package->getDevRequires() ); @@ -270,13 +283,13 @@ class Installer // creating repository pool $policy = new DefaultPolicy(); - $pool = new Pool($minimumStability, $stabilityFlags); + $pool = $this->createPool(); $pool->addRepository($installedRepo, $aliases); if ($installFromLock) { $pool->addRepository($lockedRepository, $aliases); } - if (!$installFromLock || !$this->locker->isCompleteFormat($devMode)) { + if (!$installFromLock || !$this->locker->isCompleteFormat()) { $repositories = $this->repositoryManager->getRepositories(); foreach ($repositories as $repository) { $pool->addRepository($repository, $aliases); @@ -284,30 +297,30 @@ class Installer } // creating requirements request - $request = new Request($pool); - - $constraint = new VersionConstraint('=', $this->package->getVersion()); - $constraint->setPrettyString($this->package->getPrettyVersion()); - $request->install($this->package->getName(), $constraint); + $request = $this->createRequest($pool, $this->package, $platformRepo); if ($this->update) { - $this->io->write('Updating '.($devMode ? 'dev ': '').'dependencies'); + $this->io->write('Updating dependencies'.($withDevReqs?' (including require-dev)':'').''); $request->updateAll(); - $links = $devMode ? $this->package->getDevRequires() : $this->package->getRequires(); + if ($withDevReqs) { + $links = array_merge($this->package->getRequires(), $this->package->getDevRequires()); + } else { + $links = $this->package->getRequires(); + } foreach ($links as $link) { $request->install($link->getTarget(), $link->getConstraint()); } } elseif ($installFromLock) { - $this->io->write('Installing '.($devMode ? 'dev ': '').'dependencies from lock file'); + $this->io->write('Installing dependencies'.($withDevReqs?' (including require-dev)':'').' from lock file'); - if (!$this->locker->isCompleteFormat($devMode)) { + if (!$this->locker->isCompleteFormat($withDevReqs)) { $this->io->write('Warning: Your lock file is in a deprecated format. It will most likely take a *long* time for composer to install dependencies, and may cause dependency solving issues.'); } - if (!$this->locker->isFresh() && !$devMode) { + if (!$this->locker->isFresh()) { $this->io->write('Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.'); } @@ -321,40 +334,24 @@ class Installer $request->install($package->getName(), $constraint); } } else { - $this->io->write('Installing '.($devMode ? 'dev ': '').'dependencies'); + $this->io->write('Installing dependencies'.($withDevReqs?' (including require-dev)':'').''); - $links = $devMode ? $this->package->getDevRequires() : $this->package->getRequires(); + if ($withDevReqs) { + $links = array_merge($this->package->getRequires(), $this->package->getDevRequires()); + } else { + $links = $this->package->getRequires(); + } foreach ($links as $link) { $request->install($link->getTarget(), $link->getConstraint()); } } - // fix the version of all installed packages (+ platform) that are not - // in the current local repo to prevent rogue updates (e.g. non-dev - // updating when in dev) - foreach ($installedRepo->getPackages() as $package) { - if ($package->getRepository() === $localRepo) { - continue; - } - - $constraint = new VersionConstraint('=', $package->getVersion()); - $constraint->setPrettyString($package->getPrettyVersion()); - - if (!($package->getRepository() instanceof PlatformRepository) - || !($provided = $this->package->getProvides()) - || !isset($provided[$package->getName()]) - || !$provided[$package->getName()]->getConstraint()->matches($constraint) - ) { - $request->install($package->getName(), $constraint); - } - } - // if the updateWhitelist is enabled, packages not in it are also fixed // to the version specified in the lock, or their currently installed version if ($this->update && $this->updateWhitelist) { - if ($this->locker->isLocked($devMode)) { - $currentPackages = $this->locker->getLockedRepository($devMode)->getPackages(); + if ($this->locker->isLocked()) { + $currentPackages = $this->locker->getLockedRepository($withDevReqs)->getPackages(); } else { $currentPackages = $installedRepo->getPackages(); } @@ -397,19 +394,6 @@ class Installer return false; } - if ($devMode) { - // remove bogus operations that the solver creates for stuff that was force-updated in the non-dev pass - // TODO this should not be necessary ideally, but it seems to work around the problem quite well - foreach ($operations as $index => $op) { - if ('update' === $op->getJobType() && $op->getInitialPackage()->getUniqueName() === $op->getTargetPackage()->getUniqueName() - && $op->getInitialPackage()->getSourceReference() === $op->getTargetPackage()->getSourceReference() - && $op->getInitialPackage()->getDistReference() === $op->getTargetPackage()->getDistReference() - ) { - unset($operations[$index]); - } - } - } - // force dev packages to be updated if we update or install from a (potentially new) lock $operations = $this->processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, 'force-updates', $operations); @@ -472,6 +456,45 @@ class Installer return true; } + private function createPool() + { + $minimumStability = $this->package->getMinimumStability(); + $stabilityFlags = $this->package->getStabilityFlags(); + + if (!$this->update && $this->locker->isLocked()) { + $minimumStability = $this->locker->getMinimumStability(); + $stabilityFlags = $this->locker->getStabilityFlags(); + } + + return new Pool($minimumStability, $stabilityFlags); + } + + private function createRequest(Pool $pool, RootPackageInterface $rootPackage, PlatformRepository $platformRepo) + { + $request = new Request($pool); + + $constraint = new VersionConstraint('=', $rootPackage->getVersion()); + $constraint->setPrettyString($rootPackage->getPrettyVersion()); + $request->install($rootPackage->getName(), $constraint); + + // fix the version of all installed packages (+ platform) that are not + // in the current local repo to prevent rogue updates (e.g. non-dev + // updating when in dev) + foreach ($platformRepo->getPackages() as $package) { + $constraint = new VersionConstraint('=', $package->getVersion()); + $constraint->setPrettyString($package->getPrettyVersion()); + + if (!($provided = $rootPackage->getProvides()) + || !isset($provided[$package->getName()]) + || !$provided[$package->getName()]->getConstraint()->matches($constraint) + ) { + $request->install($package->getName(), $constraint); + } + } + + return $request; + } + private function processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, $task, array $operations = null) { if ($task === 'force-updates' && null === $operations) { @@ -732,18 +755,6 @@ class Installer $rm->setLocalRepository( new InstalledArrayRepository($packages) ); - - $packages = array_map(function ($p) { - return clone $p; - }, $rm->getLocalDevRepository()->getPackages()); - foreach ($packages as $key => $package) { - if ($package instanceof AliasPackage) { - unset($packages[$key]); - } - } - $rm->setLocalDevRepository( - new InstalledArrayRepository($packages) - ); } /** diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index e867860fc..424983787 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -58,19 +58,15 @@ class Locker /** * Checks whether locker were been locked (lockfile found). * - * @param bool $dev true to check if dev packages are locked * @return bool */ - public function isLocked($dev = false) + public function isLocked() { if (!$this->lockFile->exists()) { return false; } $data = $this->getLockData(); - if ($dev) { - return isset($data['packages-dev']); - } return isset($data['packages']); } @@ -90,13 +86,12 @@ class Locker /** * Checks whether the lock file is in the new complete format or not * - * @param bool $dev true to check in dev mode * @return bool */ - public function isCompleteFormat($dev) + public function isCompleteFormat() { $lockData = $this->getLockData(); - $lockedPackages = $dev ? $lockData['packages-dev'] : $lockData['packages']; + $lockedPackages = $lockData['packages']; if (empty($lockedPackages) || isset($lockedPackages[0]['name'])) { return true; @@ -108,15 +103,22 @@ class Locker /** * Searches and returns an array of locked packages, retrieved from registered repositories. * - * @param bool $dev true to retrieve the locked dev packages + * @param bool $withDevReqs true to retrieve the locked dev packages * @return \Composer\Repository\RepositoryInterface */ - public function getLockedRepository($dev = false) + public function getLockedRepository($withDevReqs = false) { $lockData = $this->getLockData(); $packages = new ArrayRepository(); - $lockedPackages = $dev ? $lockData['packages-dev'] : $lockData['packages']; + $lockedPackages = $lockData['packages']; + if ($withDevReqs) { + if (isset($lockData['packages-dev'])) { + $lockedPackages = array_merge($lockedPackages, $lockData['packages-dev']); + } else { + throw new \RuntimeException('The lock file does not contain require-dev information, run install without --dev or run update to install those packages.'); + } + } if (empty($lockedPackages)) { return $packages; @@ -131,7 +133,7 @@ class Locker } // legacy lock file support - $repo = $dev ? $this->repositoryManager->getLocalDevRepository() : $this->repositoryManager->getLocalRepository(); + $repo = $this->repositoryManager->getLocalRepository(); foreach ($lockedPackages as $info) { $resolvedVersion = !empty($info['alias-version']) ? $info['alias-version'] : $info['version']; diff --git a/src/Composer/Repository/RepositoryManager.php b/src/Composer/Repository/RepositoryManager.php index ea0dca137..417cc6163 100644 --- a/src/Composer/Repository/RepositoryManager.php +++ b/src/Composer/Repository/RepositoryManager.php @@ -25,7 +25,6 @@ use Composer\Config; class RepositoryManager { private $localRepository; - private $localDevRepository; private $repositories = array(); private $repositoryClasses = array(); private $io; @@ -143,26 +142,6 @@ 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. * @@ -170,6 +149,8 @@ class RepositoryManager */ public function getLocalRepositories() { - return array($this->localRepository, $this->localDevRepository); + trigger_error('This method is deprecated, use getLocalRepository instead since the getLocalDevRepository is now gone', E_USER_DEPRECATED); + + return array($this->localRepository); } } diff --git a/tests/Composer/Test/CacheTest.php b/tests/Composer/Test/CacheTest.php index a07f35e2f..ba2ea77ad 100644 --- a/tests/Composer/Test/CacheTest.php +++ b/tests/Composer/Test/CacheTest.php @@ -29,7 +29,7 @@ class CacheTest extends TestCase file_put_contents("{$this->root}/cached.file{$i}.zip", $zeros); $this->files[] = new \SplFileInfo("{$this->root}/cached.file{$i}.zip"); } - $this->finder = $this->getMock('Symfony\Component\Finder\Finder'); + $this->finder = $this->getMockBuilder('Symfony\Component\Finder\Finder')->disableOriginalConstructor()->getMock(); $io = $this->getMock('Composer\IO\IOInterface'); $this->cache = $this->getMock( @@ -65,7 +65,7 @@ class CacheTest extends TestCase public function testRemoveFilesWhenCacheIsTooLarge() { - $emptyFinder = $this->getMock('Symfony\Component\Finder\Finder'); + $emptyFinder = $this->getMockBuilder('Symfony\Component\Finder\Finder')->disableOriginalConstructor()->getMock(); $emptyFinder ->expects($this->once()) ->method('getIterator') diff --git a/tests/Composer/Test/Fixtures/installer/update-all.test b/tests/Composer/Test/Fixtures/installer/update-all.test index 35a2e9337..ad5e1d3be 100644 --- a/tests/Composer/Test/Fixtures/installer/update-all.test +++ b/tests/Composer/Test/Fixtures/installer/update-all.test @@ -30,10 +30,7 @@ Updates updateable packages --INSTALLED-- [ { "name": "a/a", "version": "1.0.0" }, - { "name": "a/c", "version": "1.0.0" } -] ---INSTALLED-DEV-- -[ + { "name": "a/c", "version": "1.0.0" }, { "name": "a/b", "version": "1.0.0" } ] --RUN-- diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 951e81397..1e01ae345 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -41,7 +41,6 @@ class InstallerTest extends TestCase $repositoryManager = new RepositoryManager($io, $config); $repositoryManager->setLocalRepository(new WritableRepositoryMock()); - $repositoryManager->setLocalDevRepository(new WritableRepositoryMock()); if (!is_array($repositories)) { $repositories = array($repositories); @@ -124,7 +123,7 @@ class InstallerTest extends TestCase /** * @dataProvider getIntegrationTests */ - public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $installedDev, $run, $expectLock, $expectOutput, $expect) + public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectOutput, $expect) { if ($condition) { eval('$res = '.$condition.';'); @@ -151,17 +150,8 @@ class InstallerTest extends TestCase ->method('exists') ->will($this->returnValue(true)); - $devJsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock(); - $devJsonMock->expects($this->any()) - ->method('read') - ->will($this->returnValue($installedDev)); - $devJsonMock->expects($this->any()) - ->method('exists') - ->will($this->returnValue(true)); - $repositoryManager = $composer->getRepositoryManager(); $repositoryManager->setLocalRepository(new InstalledFilesystemRepositoryMock($jsonMock)); - $repositoryManager->setLocalDevRepository(new InstalledFilesystemRepositoryMock($devJsonMock)); $lockJsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock(); $lockJsonMock->expects($this->any()) @@ -253,7 +243,6 @@ class InstallerTest extends TestCase --COMPOSER--\s*(?P'.$content.')\s* (?:--LOCK--\s*(?P'.$content.'))?\s* (?:--INSTALLED--\s*(?P'.$content.'))?\s* - (?:--INSTALLED-DEV--\s*(?P'.$content.'))?\s* --RUN--\s*(?P.*?)\s* (?:--EXPECT-LOCK--\s*(?P'.$content.'))?\s* (?:--EXPECT-OUTPUT--\s*(?P'.$content.'))?\s* @@ -279,9 +268,6 @@ class InstallerTest extends TestCase if (!empty($match['installed'])) { $installed = JsonFile::parseJson($match['installed']); } - if (!empty($match['installedDev'])) { - $installedDev = JsonFile::parseJson($match['installedDev']); - } $run = $match['run']; if (!empty($match['expectLock'])) { $expectLock = JsonFile::parseJson($match['expectLock']); @@ -295,7 +281,7 @@ class InstallerTest extends TestCase die(sprintf('Test "%s" is not valid, did not match the expected format.', str_replace($fixturesDir.'/', '', $file))); } - $tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $installedDev, $run, $expectLock, $expectOutput, $expect); + $tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectOutput, $expect); } return $tests; From caf26ac37c09c6b99ba14db39bb45b7b87cab0e5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 3 Mar 2013 00:42:22 +0100 Subject: [PATCH 2/6] Enable dev mode by default in update command, add a --no-dev flag, fixes #1005 --- src/Composer/Command/InstallCommand.php | 3 ++- src/Composer/Command/UpdateCommand.php | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 0d8ffce2d..c578a308c 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -33,7 +33,8 @@ class InstallCommand extends Command new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), - new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of dev-require packages.'), + new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages.'), + new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages (enabled by default, only present for sanity).'), new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'Disables all custom installers.'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index a6aa3a476..f1ac08f28 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -33,7 +33,8 @@ class UpdateCommand extends Command new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), - new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of dev-require packages.'), + new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for sanity).'), + new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'), new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'Disables all custom installers.'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), @@ -68,7 +69,7 @@ EOT ->setVerbose($input->getOption('verbose')) ->setPreferSource($input->getOption('prefer-source')) ->setPreferDist($input->getOption('prefer-dist')) - ->setDevMode($input->getOption('dev')) + ->setDevMode(!$input->getOption('no-dev')) ->setRunScripts(!$input->getOption('no-scripts')) ->setOptimizeAutoloader($input->getOption('optimize-autoloader')) ->setUpdate(true) From 542d10d8fd1da464d6ea61b36f42093b8cc05911 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 3 Mar 2013 01:54:14 +0100 Subject: [PATCH 3/6] Remove all occurrences of getLocalDevRepository and getLocalRepositories calls --- src/Composer/Command/DependsCommand.php | 28 ++++++++----------- src/Composer/Command/DumpAutoloadCommand.php | 4 +-- src/Composer/Command/ShowCommand.php | 14 ++-------- src/Composer/Installer/InstallerInstaller.php | 9 +++--- src/Composer/Script/EventDispatcher.php | 5 +--- 5 files changed, 21 insertions(+), 39 deletions(-) diff --git a/src/Composer/Command/DependsCommand.php b/src/Composer/Command/DependsCommand.php index fe42387b1..6dc1a7e44 100644 --- a/src/Composer/Command/DependsCommand.php +++ b/src/Composer/Command/DependsCommand.php @@ -50,13 +50,11 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { - $repos = $this->getComposer()->getRepositoryManager()->getLocalRepositories(); + $repo = $this->getComposer()->getRepositoryManager()->getLocalRepository(); $needle = $input->getArgument('package'); $pool = new Pool(); - foreach ($repos as $repo) { - $pool->addRepository($repo); - } + $pool->addRepository($repo); $packages = $pool->whatProvides($needle); if (empty($packages)) { @@ -75,22 +73,20 @@ EOT }, $input->getOption('link-type')); $messages = array(); - foreach ($repos as $repo) { - $repo->filterPackages(function ($package) use ($needle, $types, $linkTypes, &$messages) { - static $outputPackages = array(); + $repo->filterPackages(function ($package) use ($needle, $types, $linkTypes, &$messages) { + static $outputPackages = array(); - foreach ($types as $type) { - foreach ($package->{'get'.$linkTypes[$type][0]}() as $link) { - if ($link->getTarget() === $needle) { - if (!isset($outputPackages[$package->getName()])) { - $messages[] = ''.$package->getPrettyName() . ' ' . $linkTypes[$type][1] . ' ' . $needle .' (' . $link->getPrettyConstraint() . ')'; - $outputPackages[$package->getName()] = true; - } + foreach ($types as $type) { + foreach ($package->{'get'.$linkTypes[$type][0]}() as $link) { + if ($link->getTarget() === $needle) { + if (!isset($outputPackages[$package->getName()])) { + $messages[] = ''.$package->getPrettyName() . ' ' . $linkTypes[$type][1] . ' ' . $needle .' (' . $link->getPrettyConstraint() . ')'; + $outputPackages[$package->getName()] = true; } } } - }); - } + } + }); if ($messages) { sort($messages); diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php index aa01aecbb..e3687899e 100755 --- a/src/Composer/Command/DumpAutoloadCommand.php +++ b/src/Composer/Command/DumpAutoloadCommand.php @@ -45,10 +45,10 @@ EOT $composer = $this->getComposer(); $installationManager = $composer->getInstallationManager(); - $localRepos = new CompositeRepository($composer->getRepositoryManager()->getLocalRepositories()); + $localRepo = $composer->getRepositoryManager()->getLocalRepository(); $package = $composer->getPackage(); $config = $composer->getConfig(); - $composer->getAutoloadGenerator()->dump($config, $localRepos, $package, $installationManager, 'composer', $input->getOption('optimize')); + $composer->getAutoloadGenerator()->dump($config, $localRepo, $package, $installationManager, 'composer', $input->getOption('optimize')); } } diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index d5ec48dfe..c05517398 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -45,7 +45,6 @@ class ShowCommand extends Command new InputOption('platform', 'p', InputOption::VALUE_NONE, 'List platform packages only'), new InputOption('available', 'a', InputOption::VALUE_NONE, 'List available packages only'), new InputOption('self', 's', InputOption::VALUE_NONE, 'Show the root package information'), - new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables display of dev-require packages.'), new InputOption('name-only', 'N', InputOption::VALUE_NONE, 'List package names only'), )) ->setHelp(<<getRepositoryManager(); - $repos = new CompositeRepository(array($manager->getLocalRepository())); - if ($dev) { - $repos->addRepository($manager->getLocalDevRepository()); - } - - return $repos; - }; if ($input->getOption('self')) { $package = $this->getComposer(false)->getPackage(); @@ -79,7 +69,7 @@ EOT } elseif ($input->getOption('platform')) { $repos = $installedRepo = $platformRepo; } elseif ($input->getOption('installed')) { - $repos = $installedRepo = $getRepositories($this->getComposer(), $input->getOption('dev')); + $repos = $installedRepo = $this->getComposer()->getRepositoryManager()->getLocalRepository(); } elseif ($input->getOption('available')) { $installedRepo = $platformRepo; if ($composer = $this->getComposer(false)) { @@ -90,7 +80,7 @@ EOT $output->writeln('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos))); } } elseif ($composer = $this->getComposer(false)) { - $localRepo = $getRepositories($composer, $input->getOption('dev')); + $localRepo = $composer = $this->getComposer()->getRepositoryManager()->getLocalRepository(); $installedRepo = new CompositeRepository(array($localRepo, $platformRepo)); $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories())); } else { diff --git a/src/Composer/Installer/InstallerInstaller.php b/src/Composer/Installer/InstallerInstaller.php index dd2b63cec..5a41d4e96 100644 --- a/src/Composer/Installer/InstallerInstaller.php +++ b/src/Composer/Installer/InstallerInstaller.php @@ -41,11 +41,10 @@ class InstallerInstaller extends LibraryInstaller parent::__construct($io, $composer, 'composer-installer'); $this->installationManager = $composer->getInstallationManager(); - foreach ($composer->getRepositoryManager()->getLocalRepositories() as $repo) { - foreach ($repo->getPackages() as $package) { - if ('composer-installer' === $package->getType()) { - $this->registerInstaller($package); - } + $repo = $composer->getRepositoryManager()->getLocalRepository(); + foreach ($repo->getPackages() as $package) { + if ('composer-installer' === $package->getType()) { + $this->registerInstaller($package); } } } diff --git a/src/Composer/Script/EventDispatcher.php b/src/Composer/Script/EventDispatcher.php index c24ea21ae..88b08e2d8 100644 --- a/src/Composer/Script/EventDispatcher.php +++ b/src/Composer/Script/EventDispatcher.php @@ -155,10 +155,7 @@ class EventDispatcher } $generator = $this->composer->getAutoloadGenerator(); - $packages = array_merge( - $this->composer->getRepositoryManager()->getLocalRepository()->getPackages(), - $this->composer->getRepositoryManager()->getLocalDevRepository()->getPackages() - ); + $packages = $this->composer->getRepositoryManager()->getLocalRepository()->getPackages(); $packageMap = $generator->buildPackageMap($this->composer->getInstallationManager(), $package, $packages); $map = $generator->parseAutoloads($packageMap, $package); $this->loader = $generator->createLoader($map); From 73adf29602f9e7292f59b0b352749cf20164abd4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 3 Mar 2013 01:55:10 +0100 Subject: [PATCH 4/6] Purge old dev packages before installing/updating new ones to make sure people do not have issues updating --- src/Composer/Installer.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index e6a4138a7..dad53d023 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -15,6 +15,7 @@ namespace Composer; use Composer\Autoload\AutoloadGenerator; use Composer\DependencyResolver\DefaultPolicy; use Composer\DependencyResolver\Operation\UpdateOperation; +use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Solver; @@ -24,6 +25,7 @@ use Composer\Installer\InstallationManager; use Composer\Config; use Composer\Installer\NoopInstaller; use Composer\IO\IOInterface; +use Composer\Json\JsonFile; use Composer\Package\AliasPackage; use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; @@ -32,6 +34,7 @@ use Composer\Package\PackageInterface; use Composer\Package\RootPackageInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\InstalledArrayRepository; +use Composer\Repository\InstalledFilesystemRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryInterface; use Composer\Repository\RepositoryManager; @@ -148,6 +151,20 @@ class Installer $this->mockLocalRepositories($this->repositoryManager); } + // TODO remove this BC feature at some point + // purge old require-dev packages to avoid conflicts with the new way of handling dev requirements + $devRepo = new InstalledFilesystemRepository(new JsonFile($this->config->get('vendor-dir').'/composer/installed_dev.json')); + if ($devRepo->getPackages()) { + $this->io->write('BC Notice: Removing old dev packages to migrate to the new require-dev handling.'); + foreach ($devRepo->getPackages() as $package) { + if ($this->installationManager->isPackageInstalled($devRepo, $package)) { + $this->installationManager->uninstall($devRepo, new UninstallOperation($package)); + } + } + } + unset($devRepo, $package); + // end BC + if ($this->preferSource) { $this->downloadManager->setPreferSource(true); } From c32470c7dfe4bed334ddb7125d82491524272004 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 3 Mar 2013 17:18:50 +0100 Subject: [PATCH 5/6] Update docs, fix tests --- doc/03-cli.md | 6 ++++-- doc/04-schema.md | 12 +----------- .../Test/Installer/InstallerInstallerTest.php | 4 ++-- tests/Composer/Test/InstallerTest.php | 13 +++++++++++++ .../Composer/Test/Mock/InstallationManagerMock.php | 1 + 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 043ce1801..2d5ad54a3 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -76,6 +76,8 @@ resolution. * **--dev:** By default composer will only install required packages. By passing this option you can also make it install packages referenced by `require-dev`. +* **--no-dev:** Skip installing packages listed in `require-dev` (this is + the default for `install`). * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--no-custom-installers:** Disables custom installers. * **--no-progress:** Removes the progress display that can mess with some @@ -107,7 +109,8 @@ You can also use wildcards to update a bunch of packages at once: * **--prefer-source:** Install packages from `source` when available. * **--prefer-dist:** Install packages from `dist` when available. * **--dry-run:** Simulate the command without actually doing anything. -* **--dev:** Install packages listed in `require-dev`. +* **--dev:** Install packages listed in `require-dev` (this is the default for `update`). +* **--no-dev:** Skip installing packages listed in `require-dev`. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--no-custom-installers:** Disables custom installers. * **--no-progress:** Removes the progress display that can mess with some @@ -190,7 +193,6 @@ specific version. * **--installed (-i):** List the packages that are installed. * **--platform (-p):** List only platform packages (php & extensions). * **--self (-s):** List the root package info. -* **--dev:** Include dev-required packages when combined with **--installed** or **--platform**. ## depends diff --git a/doc/04-schema.md b/doc/04-schema.md index ed585b76d..74c7510de 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -296,17 +296,7 @@ unless those requirements can be met. Lists packages required for developing this package, or running tests, etc. The dev requirements of the root package only will be installed -if `install` or `update` is ran with `--dev`. - -Packages listed here and their dependencies can not overrule the resolution -found with the packages listed in require. This is even true if a different -version of a package would be installable and solve the conflict. The reason -is that `install --dev` produces the exact same state as just `install`, apart -from the additional dev packages. - -If you run into such a conflict, you can specify the conflicting package in -the require section and require the right version number to resolve the -conflict. +if `install` is run with `--dev` or if `update` is run without `--no-dev`. #### conflict diff --git a/tests/Composer/Test/Installer/InstallerInstallerTest.php b/tests/Composer/Test/Installer/InstallerInstallerTest.php index a53ad467c..c61182389 100644 --- a/tests/Composer/Test/Installer/InstallerInstallerTest.php +++ b/tests/Composer/Test/Installer/InstallerInstallerTest.php @@ -51,8 +51,8 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); $rm->expects($this->any()) - ->method('getLocalRepositories') - ->will($this->returnValue(array($this->repository))); + ->method('getLocalRepository') + ->will($this->returnValue($this->repository)); $this->io = $this->getMock('Composer\IO\IOInterface'); diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 1e01ae345..69b292b7a 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -29,6 +29,19 @@ use Symfony\Component\Console\Output\StreamOutput; class InstallerTest extends TestCase { + protected $prevCwd; + + public function setUp() + { + $this->prevCwd = getcwd(); + chdir(__DIR__); + } + + public function tearDown() + { + chdir($this->prevCwd); + } + /** * @dataProvider provideInstaller */ diff --git a/tests/Composer/Test/Mock/InstallationManagerMock.php b/tests/Composer/Test/Mock/InstallationManagerMock.php index b643df728..0ecca1e2a 100644 --- a/tests/Composer/Test/Mock/InstallationManagerMock.php +++ b/tests/Composer/Test/Mock/InstallationManagerMock.php @@ -13,6 +13,7 @@ namespace Composer\Test\Mock; use Composer\Installer\InstallationManager; use Composer\Repository\RepositoryInterface; +use Composer\Repository\InstalledRepositoryInterface; use Composer\Package\PackageInterface; use Composer\DependencyResolver\Operation\InstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; From 06026d6b934bd36a1e8c162f2820711aac5f580d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 3 Mar 2013 17:32:41 +0100 Subject: [PATCH 6/6] Add @deprecated note --- src/Composer/Repository/RepositoryManager.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Repository/RepositoryManager.php b/src/Composer/Repository/RepositoryManager.php index 417cc6163..461783fd5 100644 --- a/src/Composer/Repository/RepositoryManager.php +++ b/src/Composer/Repository/RepositoryManager.php @@ -145,6 +145,7 @@ class RepositoryManager /** * Returns all local repositories for the project. * + * @deprecated getLocalDevRepository is gone, so this is useless now, just use getLocalRepository instead * @return array[WritableRepositoryInterface] */ public function getLocalRepositories()