1
0
Fork 0

Merge pull request #1644 from Seldaek/newdevrequires

New require-dev handling
pull/1654/head
Jordi Boggiano 2013-03-04 00:56:12 -08:00
commit c95127b80e
18 changed files with 188 additions and 206 deletions

View File

@ -76,6 +76,8 @@ resolution.
* **--dev:** By default composer will only install required packages. By * **--dev:** By default composer will only install required packages. By
passing this option you can also make it install packages referenced by passing this option you can also make it install packages referenced by
`require-dev`. `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-scripts:** Skips execution of scripts defined in `composer.json`.
* **--no-custom-installers:** Disables custom installers. * **--no-custom-installers:** Disables custom installers.
* **--no-progress:** Removes the progress display that can mess with some * **--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-source:** Install packages from `source` when available.
* **--prefer-dist:** Install packages from `dist` when available. * **--prefer-dist:** Install packages from `dist` when available.
* **--dry-run:** Simulate the command without actually doing anything. * **--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-scripts:** Skips execution of scripts defined in `composer.json`.
* **--no-custom-installers:** Disables custom installers. * **--no-custom-installers:** Disables custom installers.
* **--no-progress:** Removes the progress display that can mess with some * **--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. * **--installed (-i):** List the packages that are installed.
* **--platform (-p):** List only platform packages (php & extensions). * **--platform (-p):** List only platform packages (php & extensions).
* **--self (-s):** List the root package info. * **--self (-s):** List the root package info.
* **--dev:** Include dev-required packages when combined with **--installed** or **--platform**.
## depends ## depends

View File

@ -296,17 +296,7 @@ unless those requirements can be met.
Lists packages required for developing this package, or running Lists packages required for developing this package, or running
tests, etc. The dev requirements of the root package only will be installed tests, etc. The dev requirements of the root package only will be installed
if `install` or `update` is ran with `--dev`. if `install` is run with `--dev` or if `update` is run without `--no-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.
#### conflict #### conflict

View File

@ -50,13 +50,11 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$repos = $this->getComposer()->getRepositoryManager()->getLocalRepositories(); $repo = $this->getComposer()->getRepositoryManager()->getLocalRepository();
$needle = $input->getArgument('package'); $needle = $input->getArgument('package');
$pool = new Pool(); $pool = new Pool();
foreach ($repos as $repo) { $pool->addRepository($repo);
$pool->addRepository($repo);
}
$packages = $pool->whatProvides($needle); $packages = $pool->whatProvides($needle);
if (empty($packages)) { if (empty($packages)) {
@ -75,22 +73,20 @@ EOT
}, $input->getOption('link-type')); }, $input->getOption('link-type'));
$messages = array(); $messages = array();
foreach ($repos as $repo) { $repo->filterPackages(function ($package) use ($needle, $types, $linkTypes, &$messages) {
$repo->filterPackages(function ($package) use ($needle, $types, $linkTypes, &$messages) { static $outputPackages = array();
static $outputPackages = array();
foreach ($types as $type) { foreach ($types as $type) {
foreach ($package->{'get'.$linkTypes[$type][0]}() as $link) { foreach ($package->{'get'.$linkTypes[$type][0]}() as $link) {
if ($link->getTarget() === $needle) { if ($link->getTarget() === $needle) {
if (!isset($outputPackages[$package->getName()])) { if (!isset($outputPackages[$package->getName()])) {
$messages[] = '<info>'.$package->getPrettyName() . '</info> ' . $linkTypes[$type][1] . ' ' . $needle .' (<info>' . $link->getPrettyConstraint() . '</info>)'; $messages[] = '<info>'.$package->getPrettyName() . '</info> ' . $linkTypes[$type][1] . ' ' . $needle .' (<info>' . $link->getPrettyConstraint() . '</info>)';
$outputPackages[$package->getName()] = true; $outputPackages[$package->getName()] = true;
}
} }
} }
} }
}); }
} });
if ($messages) { if ($messages) {
sort($messages); sort($messages);

View File

@ -45,10 +45,10 @@ EOT
$composer = $this->getComposer(); $composer = $this->getComposer();
$installationManager = $composer->getInstallationManager(); $installationManager = $composer->getInstallationManager();
$localRepos = new CompositeRepository($composer->getRepositoryManager()->getLocalRepositories()); $localRepo = $composer->getRepositoryManager()->getLocalRepository();
$package = $composer->getPackage(); $package = $composer->getPackage();
$config = $composer->getConfig(); $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'));
} }
} }

View File

@ -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-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('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('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-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-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.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),

View File

@ -45,7 +45,6 @@ class ShowCommand extends Command
new InputOption('platform', 'p', InputOption::VALUE_NONE, 'List platform packages only'), new InputOption('platform', 'p', InputOption::VALUE_NONE, 'List platform packages only'),
new InputOption('available', 'a', InputOption::VALUE_NONE, 'List available 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('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'), new InputOption('name-only', 'N', InputOption::VALUE_NONE, 'List package names only'),
)) ))
->setHelp(<<<EOT ->setHelp(<<<EOT
@ -63,15 +62,6 @@ EOT
// init repos // init repos
$platformRepo = new PlatformRepository; $platformRepo = new PlatformRepository;
$getRepositories = function (Composer $composer, $dev) {
$manager = $composer->getRepositoryManager();
$repos = new CompositeRepository(array($manager->getLocalRepository()));
if ($dev) {
$repos->addRepository($manager->getLocalDevRepository());
}
return $repos;
};
if ($input->getOption('self')) { if ($input->getOption('self')) {
$package = $this->getComposer(false)->getPackage(); $package = $this->getComposer(false)->getPackage();
@ -79,7 +69,7 @@ EOT
} elseif ($input->getOption('platform')) { } elseif ($input->getOption('platform')) {
$repos = $installedRepo = $platformRepo; $repos = $installedRepo = $platformRepo;
} elseif ($input->getOption('installed')) { } elseif ($input->getOption('installed')) {
$repos = $installedRepo = $getRepositories($this->getComposer(), $input->getOption('dev')); $repos = $installedRepo = $this->getComposer()->getRepositoryManager()->getLocalRepository();
} elseif ($input->getOption('available')) { } elseif ($input->getOption('available')) {
$installedRepo = $platformRepo; $installedRepo = $platformRepo;
if ($composer = $this->getComposer(false)) { 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))); $output->writeln('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
} }
} elseif ($composer = $this->getComposer(false)) { } elseif ($composer = $this->getComposer(false)) {
$localRepo = $getRepositories($composer, $input->getOption('dev')); $localRepo = $composer = $this->getComposer()->getRepositoryManager()->getLocalRepository();
$installedRepo = new CompositeRepository(array($localRepo, $platformRepo)); $installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories())); $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
} else { } else {

View File

@ -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-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('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('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-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-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.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
@ -68,7 +69,7 @@ EOT
->setVerbose($input->getOption('verbose')) ->setVerbose($input->getOption('verbose'))
->setPreferSource($input->getOption('prefer-source')) ->setPreferSource($input->getOption('prefer-source'))
->setPreferDist($input->getOption('prefer-dist')) ->setPreferDist($input->getOption('prefer-dist'))
->setDevMode($input->getOption('dev')) ->setDevMode(!$input->getOption('no-dev'))
->setRunScripts(!$input->getOption('no-scripts')) ->setRunScripts(!$input->getOption('no-scripts'))
->setOptimizeAutoloader($input->getOption('optimize-autoloader')) ->setOptimizeAutoloader($input->getOption('optimize-autoloader'))
->setUpdate(true) ->setUpdate(true)

View File

@ -291,7 +291,6 @@ class Factory
protected function addLocalRepository(RepositoryManager $rm, $vendorDir) protected function addLocalRepository(RepositoryManager $rm, $vendorDir)
{ {
$rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json'))); $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) protected function purgePackages(Repository\RepositoryManager $rm, Installer\InstallationManager $im)
{ {
foreach ($rm->getLocalRepositories() as $repo) { $repo = $rm->getLocalRepository();
/* @var $repo Repository\WritableRepositoryInterface */ foreach ($repo->getPackages() as $package) {
foreach ($repo->getPackages() as $package) { if (!$im->isPackageInstalled($repo, $package)) {
if (!$im->isPackageInstalled($repo, $package)) { $repo->removePackage($package);
$repo->removePackage($package);
}
} }
} }
} }

View File

@ -15,6 +15,7 @@ namespace Composer;
use Composer\Autoload\AutoloadGenerator; use Composer\Autoload\AutoloadGenerator;
use Composer\DependencyResolver\DefaultPolicy; use Composer\DependencyResolver\DefaultPolicy;
use Composer\DependencyResolver\Operation\UpdateOperation; use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Operation\UninstallOperation;
use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Pool;
use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Request;
use Composer\DependencyResolver\Solver; use Composer\DependencyResolver\Solver;
@ -24,6 +25,7 @@ use Composer\Installer\InstallationManager;
use Composer\Config; use Composer\Config;
use Composer\Installer\NoopInstaller; use Composer\Installer\NoopInstaller;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Json\JsonFile;
use Composer\Package\AliasPackage; use Composer\Package\AliasPackage;
use Composer\Package\Link; use Composer\Package\Link;
use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\LinkConstraint\VersionConstraint;
@ -32,6 +34,7 @@ use Composer\Package\PackageInterface;
use Composer\Package\RootPackageInterface; use Composer\Package\RootPackageInterface;
use Composer\Repository\CompositeRepository; use Composer\Repository\CompositeRepository;
use Composer\Repository\InstalledArrayRepository; use Composer\Repository\InstalledArrayRepository;
use Composer\Repository\InstalledFilesystemRepository;
use Composer\Repository\PlatformRepository; use Composer\Repository\PlatformRepository;
use Composer\Repository\RepositoryInterface; use Composer\Repository\RepositoryInterface;
use Composer\Repository\RepositoryManager; use Composer\Repository\RepositoryManager;
@ -148,6 +151,20 @@ class Installer
$this->mockLocalRepositories($this->repositoryManager); $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('<warning>BC Notice: Removing old dev packages to migrate to the new require-dev handling.</warning>');
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) { if ($this->preferSource) {
$this->downloadManager->setPreferSource(true); $this->downloadManager->setPreferSource(true);
} }
@ -160,13 +177,12 @@ class Installer
$installedRootPackage->setRequires(array()); $installedRootPackage->setRequires(array());
$installedRootPackage->setDevRequires(array()); $installedRootPackage->setDevRequires(array());
$localRepo = $this->repositoryManager->getLocalRepository();
$platformRepo = new PlatformRepository(); $platformRepo = new PlatformRepository();
$repos = array_merge( $repos = array(
$this->repositoryManager->getLocalRepositories(), $localRepo,
array( new InstalledArrayRepository(array($installedRootPackage)),
new InstalledArrayRepository(array($installedRootPackage)), $platformRepo,
$platformRepo,
)
); );
$installedRepo = new CompositeRepository($repos); $installedRepo = new CompositeRepository($repos);
if ($this->additionalInstalledRepository) { if ($this->additionalInstalledRepository) {
@ -184,14 +200,9 @@ class Installer
try { try {
$this->suggestedPackages = array(); $this->suggestedPackages = array();
if (!$this->doInstall($this->repositoryManager->getLocalRepository(), $installedRepo, $aliases)) { if (!$this->doInstall($localRepo, $installedRepo, $platformRepo, $aliases, $this->devMode)) {
return false; return false;
} }
if ($this->devMode) {
if (!$this->doInstall($this->repositoryManager->getLocalDevRepository(), $installedRepo, $aliases, true)) {
return false;
}
}
} catch (\Exception $e) { } catch (\Exception $e) {
$this->installationManager->notifyInstalls(); $this->installationManager->notifyInstalls();
@ -214,9 +225,34 @@ class Installer
if (!$this->dryRun) { if (!$this->dryRun) {
// write lock // write lock
if ($this->update || !$this->locker->isLocked()) { 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( $updatedLock = $this->locker->setLockData(
$this->repositoryManager->getLocalRepository()->getPackages(), array_diff($localRepo->getPackages(), (array) $devPackages),
$this->devMode ? $this->repositoryManager->getLocalDevRepository()->getPackages() : null, $devPackages,
$aliases, $aliases,
$this->package->getMinimumStability(), $this->package->getMinimumStability(),
$this->package->getStabilityFlags() $this->package->getStabilityFlags()
@ -228,8 +264,7 @@ class Installer
// write autoloader // write autoloader
$this->io->write('<info>Generating autoload files</info>'); $this->io->write('<info>Generating autoload files</info>');
$localRepos = new CompositeRepository($this->repositoryManager->getLocalRepositories()); $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader);
$this->autoloadGenerator->dump($this->config, $localRepos, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader);
if ($this->runScripts) { if ($this->runScripts) {
// dispatch post event // dispatch post event
@ -241,27 +276,22 @@ class Installer
return true; 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 // init vars
$lockedRepository = null; $lockedRepository = null;
$repositories = null; $repositories = null;
// initialize locker to create aliased packages // initialize locker to create aliased packages
$installFromLock = false; $installFromLock = false;
if (!$this->update && $this->locker->isLocked($devMode)) { if (!$this->update && $this->locker->isLocked()) {
$installFromLock = true; $installFromLock = true;
$lockedRepository = $this->locker->getLockedRepository($devMode); $lockedRepository = $this->locker->getLockedRepository($withDevReqs);
$minimumStability = $this->locker->getMinimumStability();
$stabilityFlags = $this->locker->getStabilityFlags();
} }
$this->whitelistUpdateDependencies( $this->whitelistUpdateDependencies(
$localRepo, $localRepo,
$devMode, $withDevReqs,
$this->package->getRequires(), $this->package->getRequires(),
$this->package->getDevRequires() $this->package->getDevRequires()
); );
@ -270,13 +300,13 @@ class Installer
// creating repository pool // creating repository pool
$policy = new DefaultPolicy(); $policy = new DefaultPolicy();
$pool = new Pool($minimumStability, $stabilityFlags); $pool = $this->createPool();
$pool->addRepository($installedRepo, $aliases); $pool->addRepository($installedRepo, $aliases);
if ($installFromLock) { if ($installFromLock) {
$pool->addRepository($lockedRepository, $aliases); $pool->addRepository($lockedRepository, $aliases);
} }
if (!$installFromLock || !$this->locker->isCompleteFormat($devMode)) { if (!$installFromLock || !$this->locker->isCompleteFormat()) {
$repositories = $this->repositoryManager->getRepositories(); $repositories = $this->repositoryManager->getRepositories();
foreach ($repositories as $repository) { foreach ($repositories as $repository) {
$pool->addRepository($repository, $aliases); $pool->addRepository($repository, $aliases);
@ -284,30 +314,30 @@ class Installer
} }
// creating requirements request // creating requirements request
$request = new Request($pool); $request = $this->createRequest($pool, $this->package, $platformRepo);
$constraint = new VersionConstraint('=', $this->package->getVersion());
$constraint->setPrettyString($this->package->getPrettyVersion());
$request->install($this->package->getName(), $constraint);
if ($this->update) { if ($this->update) {
$this->io->write('<info>Updating '.($devMode ? 'dev ': '').'dependencies</info>'); $this->io->write('<info>Updating dependencies'.($withDevReqs?' (including require-dev)':'').'</info>');
$request->updateAll(); $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) { foreach ($links as $link) {
$request->install($link->getTarget(), $link->getConstraint()); $request->install($link->getTarget(), $link->getConstraint());
} }
} elseif ($installFromLock) { } elseif ($installFromLock) {
$this->io->write('<info>Installing '.($devMode ? 'dev ': '').'dependencies from lock file</info>'); $this->io->write('<info>Installing dependencies'.($withDevReqs?' (including require-dev)':'').' from lock file</info>');
if (!$this->locker->isCompleteFormat($devMode)) { if (!$this->locker->isCompleteFormat($withDevReqs)) {
$this->io->write('<warning>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.</warning>'); $this->io->write('<warning>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.</warning>');
} }
if (!$this->locker->isFresh() && !$devMode) { if (!$this->locker->isFresh()) {
$this->io->write('<warning>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.</warning>'); $this->io->write('<warning>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.</warning>');
} }
@ -321,40 +351,24 @@ class Installer
$request->install($package->getName(), $constraint); $request->install($package->getName(), $constraint);
} }
} else { } else {
$this->io->write('<info>Installing '.($devMode ? 'dev ': '').'dependencies</info>'); $this->io->write('<info>Installing dependencies'.($withDevReqs?' (including require-dev)':'').'</info>');
$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) { foreach ($links as $link) {
$request->install($link->getTarget(), $link->getConstraint()); $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 // if the updateWhitelist is enabled, packages not in it are also fixed
// to the version specified in the lock, or their currently installed version // to the version specified in the lock, or their currently installed version
if ($this->update && $this->updateWhitelist) { if ($this->update && $this->updateWhitelist) {
if ($this->locker->isLocked($devMode)) { if ($this->locker->isLocked()) {
$currentPackages = $this->locker->getLockedRepository($devMode)->getPackages(); $currentPackages = $this->locker->getLockedRepository($withDevReqs)->getPackages();
} else { } else {
$currentPackages = $installedRepo->getPackages(); $currentPackages = $installedRepo->getPackages();
} }
@ -397,19 +411,6 @@ class Installer
return false; 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 // 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); $operations = $this->processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, 'force-updates', $operations);
@ -472,6 +473,45 @@ class Installer
return true; 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) private function processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, $task, array $operations = null)
{ {
if ($task === 'force-updates' && null === $operations) { if ($task === 'force-updates' && null === $operations) {
@ -732,18 +772,6 @@ class Installer
$rm->setLocalRepository( $rm->setLocalRepository(
new InstalledArrayRepository($packages) 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)
);
} }
/** /**

View File

@ -41,11 +41,10 @@ class InstallerInstaller extends LibraryInstaller
parent::__construct($io, $composer, 'composer-installer'); parent::__construct($io, $composer, 'composer-installer');
$this->installationManager = $composer->getInstallationManager(); $this->installationManager = $composer->getInstallationManager();
foreach ($composer->getRepositoryManager()->getLocalRepositories() as $repo) { $repo = $composer->getRepositoryManager()->getLocalRepository();
foreach ($repo->getPackages() as $package) { foreach ($repo->getPackages() as $package) {
if ('composer-installer' === $package->getType()) { if ('composer-installer' === $package->getType()) {
$this->registerInstaller($package); $this->registerInstaller($package);
}
} }
} }
} }

View File

@ -58,19 +58,15 @@ class Locker
/** /**
* Checks whether locker were been locked (lockfile found). * Checks whether locker were been locked (lockfile found).
* *
* @param bool $dev true to check if dev packages are locked
* @return bool * @return bool
*/ */
public function isLocked($dev = false) public function isLocked()
{ {
if (!$this->lockFile->exists()) { if (!$this->lockFile->exists()) {
return false; return false;
} }
$data = $this->getLockData(); $data = $this->getLockData();
if ($dev) {
return isset($data['packages-dev']);
}
return isset($data['packages']); return isset($data['packages']);
} }
@ -90,13 +86,12 @@ class Locker
/** /**
* Checks whether the lock file is in the new complete format or not * Checks whether the lock file is in the new complete format or not
* *
* @param bool $dev true to check in dev mode
* @return bool * @return bool
*/ */
public function isCompleteFormat($dev) public function isCompleteFormat()
{ {
$lockData = $this->getLockData(); $lockData = $this->getLockData();
$lockedPackages = $dev ? $lockData['packages-dev'] : $lockData['packages']; $lockedPackages = $lockData['packages'];
if (empty($lockedPackages) || isset($lockedPackages[0]['name'])) { if (empty($lockedPackages) || isset($lockedPackages[0]['name'])) {
return true; return true;
@ -108,15 +103,22 @@ class Locker
/** /**
* Searches and returns an array of locked packages, retrieved from registered repositories. * 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 * @return \Composer\Repository\RepositoryInterface
*/ */
public function getLockedRepository($dev = false) public function getLockedRepository($withDevReqs = false)
{ {
$lockData = $this->getLockData(); $lockData = $this->getLockData();
$packages = new ArrayRepository(); $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)) { if (empty($lockedPackages)) {
return $packages; return $packages;
@ -131,7 +133,7 @@ class Locker
} }
// legacy lock file support // legacy lock file support
$repo = $dev ? $this->repositoryManager->getLocalDevRepository() : $this->repositoryManager->getLocalRepository(); $repo = $this->repositoryManager->getLocalRepository();
foreach ($lockedPackages as $info) { foreach ($lockedPackages as $info) {
$resolvedVersion = !empty($info['alias-version']) ? $info['alias-version'] : $info['version']; $resolvedVersion = !empty($info['alias-version']) ? $info['alias-version'] : $info['version'];

View File

@ -25,7 +25,6 @@ use Composer\Config;
class RepositoryManager class RepositoryManager
{ {
private $localRepository; private $localRepository;
private $localDevRepository;
private $repositories = array(); private $repositories = array();
private $repositoryClasses = array(); private $repositoryClasses = array();
private $io; private $io;
@ -143,33 +142,16 @@ class RepositoryManager
return $this->localRepository; 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. * Returns all local repositories for the project.
* *
* @deprecated getLocalDevRepository is gone, so this is useless now, just use getLocalRepository instead
* @return array[WritableRepositoryInterface] * @return array[WritableRepositoryInterface]
*/ */
public function getLocalRepositories() 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);
} }
} }

View File

@ -155,10 +155,7 @@ class EventDispatcher
} }
$generator = $this->composer->getAutoloadGenerator(); $generator = $this->composer->getAutoloadGenerator();
$packages = array_merge( $packages = $this->composer->getRepositoryManager()->getLocalRepository()->getPackages();
$this->composer->getRepositoryManager()->getLocalRepository()->getPackages(),
$this->composer->getRepositoryManager()->getLocalDevRepository()->getPackages()
);
$packageMap = $generator->buildPackageMap($this->composer->getInstallationManager(), $package, $packages); $packageMap = $generator->buildPackageMap($this->composer->getInstallationManager(), $package, $packages);
$map = $generator->parseAutoloads($packageMap, $package); $map = $generator->parseAutoloads($packageMap, $package);
$this->loader = $generator->createLoader($map); $this->loader = $generator->createLoader($map);

View File

@ -29,7 +29,7 @@ class CacheTest extends TestCase
file_put_contents("{$this->root}/cached.file{$i}.zip", $zeros); file_put_contents("{$this->root}/cached.file{$i}.zip", $zeros);
$this->files[] = new \SplFileInfo("{$this->root}/cached.file{$i}.zip"); $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'); $io = $this->getMock('Composer\IO\IOInterface');
$this->cache = $this->getMock( $this->cache = $this->getMock(
@ -65,7 +65,7 @@ class CacheTest extends TestCase
public function testRemoveFilesWhenCacheIsTooLarge() public function testRemoveFilesWhenCacheIsTooLarge()
{ {
$emptyFinder = $this->getMock('Symfony\Component\Finder\Finder'); $emptyFinder = $this->getMockBuilder('Symfony\Component\Finder\Finder')->disableOriginalConstructor()->getMock();
$emptyFinder $emptyFinder
->expects($this->once()) ->expects($this->once())
->method('getIterator') ->method('getIterator')

View File

@ -30,10 +30,7 @@ Updates updateable packages
--INSTALLED-- --INSTALLED--
[ [
{ "name": "a/a", "version": "1.0.0" }, { "name": "a/a", "version": "1.0.0" },
{ "name": "a/c", "version": "1.0.0" } { "name": "a/c", "version": "1.0.0" },
]
--INSTALLED-DEV--
[
{ "name": "a/b", "version": "1.0.0" } { "name": "a/b", "version": "1.0.0" }
] ]
--RUN-- --RUN--

View File

@ -51,8 +51,8 @@ class InstallerInstallerTest extends \PHPUnit_Framework_TestCase
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$rm->expects($this->any()) $rm->expects($this->any())
->method('getLocalRepositories') ->method('getLocalRepository')
->will($this->returnValue(array($this->repository))); ->will($this->returnValue($this->repository));
$this->io = $this->getMock('Composer\IO\IOInterface'); $this->io = $this->getMock('Composer\IO\IOInterface');

View File

@ -29,6 +29,19 @@ use Symfony\Component\Console\Output\StreamOutput;
class InstallerTest extends TestCase class InstallerTest extends TestCase
{ {
protected $prevCwd;
public function setUp()
{
$this->prevCwd = getcwd();
chdir(__DIR__);
}
public function tearDown()
{
chdir($this->prevCwd);
}
/** /**
* @dataProvider provideInstaller * @dataProvider provideInstaller
*/ */
@ -41,7 +54,6 @@ class InstallerTest extends TestCase
$repositoryManager = new RepositoryManager($io, $config); $repositoryManager = new RepositoryManager($io, $config);
$repositoryManager->setLocalRepository(new WritableRepositoryMock()); $repositoryManager->setLocalRepository(new WritableRepositoryMock());
$repositoryManager->setLocalDevRepository(new WritableRepositoryMock());
if (!is_array($repositories)) { if (!is_array($repositories)) {
$repositories = array($repositories); $repositories = array($repositories);
@ -124,7 +136,7 @@ class InstallerTest extends TestCase
/** /**
* @dataProvider getIntegrationTests * @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) { if ($condition) {
eval('$res = '.$condition.';'); eval('$res = '.$condition.';');
@ -151,17 +163,8 @@ class InstallerTest extends TestCase
->method('exists') ->method('exists')
->will($this->returnValue(true)); ->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 = $composer->getRepositoryManager();
$repositoryManager->setLocalRepository(new InstalledFilesystemRepositoryMock($jsonMock)); $repositoryManager->setLocalRepository(new InstalledFilesystemRepositoryMock($jsonMock));
$repositoryManager->setLocalDevRepository(new InstalledFilesystemRepositoryMock($devJsonMock));
$lockJsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock(); $lockJsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock();
$lockJsonMock->expects($this->any()) $lockJsonMock->expects($this->any())
@ -253,7 +256,6 @@ class InstallerTest extends TestCase
--COMPOSER--\s*(?P<composer>'.$content.')\s* --COMPOSER--\s*(?P<composer>'.$content.')\s*
(?:--LOCK--\s*(?P<lock>'.$content.'))?\s* (?:--LOCK--\s*(?P<lock>'.$content.'))?\s*
(?:--INSTALLED--\s*(?P<installed>'.$content.'))?\s* (?:--INSTALLED--\s*(?P<installed>'.$content.'))?\s*
(?:--INSTALLED-DEV--\s*(?P<installedDev>'.$content.'))?\s*
--RUN--\s*(?P<run>.*?)\s* --RUN--\s*(?P<run>.*?)\s*
(?:--EXPECT-LOCK--\s*(?P<expectLock>'.$content.'))?\s* (?:--EXPECT-LOCK--\s*(?P<expectLock>'.$content.'))?\s*
(?:--EXPECT-OUTPUT--\s*(?P<expectOutput>'.$content.'))?\s* (?:--EXPECT-OUTPUT--\s*(?P<expectOutput>'.$content.'))?\s*
@ -279,9 +281,6 @@ class InstallerTest extends TestCase
if (!empty($match['installed'])) { if (!empty($match['installed'])) {
$installed = JsonFile::parseJson($match['installed']); $installed = JsonFile::parseJson($match['installed']);
} }
if (!empty($match['installedDev'])) {
$installedDev = JsonFile::parseJson($match['installedDev']);
}
$run = $match['run']; $run = $match['run'];
if (!empty($match['expectLock'])) { if (!empty($match['expectLock'])) {
$expectLock = JsonFile::parseJson($match['expectLock']); $expectLock = JsonFile::parseJson($match['expectLock']);
@ -295,7 +294,7 @@ class InstallerTest extends TestCase
die(sprintf('Test "%s" is not valid, did not match the expected format.', str_replace($fixturesDir.'/', '', $file))); 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; return $tests;

View File

@ -13,6 +13,7 @@ namespace Composer\Test\Mock;
use Composer\Installer\InstallationManager; use Composer\Installer\InstallationManager;
use Composer\Repository\RepositoryInterface; use Composer\Repository\RepositoryInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\DependencyResolver\Operation\InstallOperation; use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\UpdateOperation; use Composer\DependencyResolver\Operation\UpdateOperation;