diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 63804dfad..675f56719 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -31,6 +31,7 @@ class InstallCommand extends Command ->setDescription('Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json.') ->setDefinition(array( 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('no-custom-installers', null, InputOption::VALUE_NONE, 'Disables all custom installers.'), @@ -60,6 +61,7 @@ EOT ->setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) ->setPreferSource($input->getOption('prefer-source')) + ->setPreferDist($input->getOption('prefer-dist')) ->setDevMode($input->getOption('dev')) ->setRunScripts(!$input->getOption('no-scripts')) ; diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index acf541a3c..b92245e4e 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -31,6 +31,7 @@ class UpdateCommand extends Command ->setDefinition(array( new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that should be updated, if not provided all packages are.'), 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('no-custom-installers', null, InputOption::VALUE_NONE, 'Disables all custom installers.'), @@ -63,6 +64,7 @@ EOT ->setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) ->setPreferSource($input->getOption('prefer-source')) + ->setPreferDist($input->getOption('prefer-dist')) ->setDevMode($input->getOption('dev')) ->setRunScripts(!$input->getOption('no-scripts')) ->setUpdate(true) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 4b4bf3ec4..d88220db4 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -23,6 +23,7 @@ use Composer\Util\Filesystem; */ class DownloadManager { + private $preferDist = false; private $preferSource = false; private $filesystem; private $downloaders = array(); @@ -51,6 +52,18 @@ class DownloadManager return $this; } + /** + * Makes downloader prefer dist installation over the source. + * + * @param bool $preferDist prefer downloading from dist + */ + public function setPreferDist($preferDist) + { + $this->preferDist = $preferDist; + + return $this; + } + /** * Sets installer downloader for a specific installation type. * @@ -134,12 +147,12 @@ class DownloadManager $sourceType = $package->getSourceType(); $distType = $package->getDistType(); - if (!$package->isDev() && !($preferSource && $sourceType) && $distType) { + if ((!$package->isDev() || $this->preferDist) && !($preferSource && $sourceType) && $distType) { $package->setInstallationSource('dist'); } elseif ($sourceType) { $package->setInstallationSource('source'); - } elseif ($package->isDev()) { - throw new \InvalidArgumentException('Dev package '.$package.' must have a source specified'); + } elseif ($package->isDev() && $distType) { + throw new \InvalidArgumentException('Dev package '.$package.' should have a source specified because for dev packages dist is used only with --prefer-dist option'); } else { throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified'); } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 52e825f53..3b6e91d81 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -91,6 +91,7 @@ class Installer protected $autoloadGenerator; protected $preferSource = false; + protected $preferDist = false; protected $devMode = false; protected $dryRun = false; protected $verbose = false; @@ -148,6 +149,9 @@ class Installer if ($this->preferSource) { $this->downloadManager->setPreferSource(true); } + if ($this->preferDist) { + $this->downloadManager->setPreferDist(true); + } // create installed repo, this contains all local packages + platform packages (php & extensions) $installedRootPackage = clone $this->package; @@ -387,15 +391,17 @@ class Installer // force update to locked version if it does not match the installed version if ($installFromLock) { - unset($lockedReference); foreach ($lockedRepository->findPackages($package->getName()) as $lockedPackage) { if ( $lockedPackage->isDev() - && $lockedPackage->getSourceReference() - && $lockedPackage->getSourceReference() !== $package->getSourceReference() + && ( + ($lockedPackage->getSourceReference() && $lockedPackage->getSourceReference() !== $package->getSourceReference()) + || ($lockedPackage->getDistReference() && $lockedPackage->getDistReference() !== $package->getDistReference()) + ) ) { $newPackage = clone $package; $newPackage->setSourceReference($lockedPackage->getSourceReference()); + $newPackage->setDistReference($lockedPackage->getDistReference()); $operations[] = new UpdateOperation($package, $newPackage); break; @@ -671,6 +677,19 @@ class Installer return $this; } + /** + * prefer dist installation + * + * @param boolean $preferDist + * @return Installer + */ + public function setPreferDist($preferDist = true) + { + $this->preferDist = (boolean) $preferDist; + + return $this; + } + /** * update packages * diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index ea190636f..59e9b244d 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -284,8 +284,9 @@ class Locker if ($package->isDev()) { if ('git' === $package->getSourceType() && $path = $this->installationManager->getInstallPath($package)) { + $sourceRef = $package->getSourceReference() ?: $package->getDistReference(); $process = new ProcessExecutor(); - if (0 === $process->execute('git log -n1 --pretty=%ct '.escapeshellarg($package->getSourceReference()), $output, $path)) { + if (0 === $process->execute('git log -n1 --pretty=%ct '.escapeshellarg($sourceRef), $output, $path)) { $spec['time'] = trim($output); } } diff --git a/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test b/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test new file mode 100644 index 000000000..8517152a2 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/install-dev-using-dist.test @@ -0,0 +1,46 @@ +--TEST-- +Installs a dev package from lock using dist +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { + "name": "a/a", + "version": "dev-master", + "version_normalized": "9999999-dev", + "dist": { + "type": "zip", + "url": "http://www.example.com/dist.zip", + "reference": "459720ff3b74ee0c0d159277c6f2f5df89d8a4f6" + } + } + ] + } + ], + "require": { + "a/a": "dev-master" + }, + "minimum-stability": "dev" +} +--RUN-- +install --prefer-dist +--EXPECT-LOCK-- +{ + "packages": [ + { + "version": "dev-master", + "package": "a/a", + "source-reference": "459720ff3b74ee0c0d159277c6f2f5df89d8a4f6" + } + ], + "packages-dev": null, + "aliases": [], + "minimum-stability": "dev", + "stability-flags": { + "a/a": 20 + } +} +--EXPECT-- +Installing a/a (dev-master)