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;