From 1f467046d78c2647c96a96073c4d9c60b107134d Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Wed, 11 Mar 2020 17:38:16 +0100 Subject: [PATCH] Implement getProviders on reposet for all repo types and add replacers This way errors during require dev extraction make more sense --- src/Composer/DependencyResolver/Problem.php | 2 +- .../SolverProblemsException.php | 4 +- src/Composer/Installer.php | 5 ++- src/Composer/Repository/RepositorySet.php | 22 ++++++++-- ...er-dev-require-cannot-satisfy-require.test | 40 +++++++++++++++++++ 5 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 tests/Composer/Test/Fixtures/installer/provider-dev-require-cannot-satisfy-require.test diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index a03694d31..a68d45b40 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -248,7 +248,7 @@ class Problem return array("- Root composer.json requires $packageName, it ", 'could not be found, it looks like its name is invalid, "'.$illegalChars.'" is not allowed in package names.'); } - if ($providers = $repositorySet->getProviders($packageName)) { + if ($providers = $repositorySet->getProvidersAndReplacers($packageName)) { $maxProviders = 20; $providersStr = implode(array_map(function ($p) { return " - ${p['name']} ".substr($p['description'], 0, 100)."\n"; diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php index 542fe0464..a47bd1c8c 100644 --- a/src/Composer/DependencyResolver/SolverProblemsException.php +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -31,7 +31,7 @@ class SolverProblemsException extends \RuntimeException parent::__construct('Failed resolving dependencies with '.count($problems).' problems, call getPrettyString to get formatted details', 2); } - public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool) + public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isDevExtraction = false) { $installedMap = $request->getPresentMap(true); $text = "\n"; @@ -44,7 +44,7 @@ class SolverProblemsException extends \RuntimeException } } - if (strpos($text, 'could not be found') || strpos($text, 'no matching package found')) { + if (!$isDevExtraction && (strpos($text, 'could not be found') || strpos($text, 'no matching package found'))) { $text .= "\nPotential causes:\n - A typo in the package name\n - The package is not available in a stable-enough version according to your minimum-stability setting\n see for more details.\n - It's a private package and you forgot to add a custom repository to find it\n\nRead for further common problems."; } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 9e9d32407..7ed0ccda6 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -573,8 +573,9 @@ class Installer $solver = null; } catch (SolverProblemsException $e) { $this->io->writeError('Unable to find a compatible set of packages based on your non-dev requirements alone.', true, IOInterface::QUIET); - $this->io->writeError('Your requirements can be successfully resolved when require-dev packages are present.'); - $this->io->writeError($e->getPrettyString($repositorySet, $request, $pool)); + $this->io->writeError('Your requirements can be resolved successfully when require-dev packages are present.'); + $this->io->writeError('You may need to move packages from require-dev or some of their dependencies to require.'); + $this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, true)); return max(1, $e->getCode()); } diff --git a/src/Composer/Repository/RepositorySet.php b/src/Composer/Repository/RepositorySet.php index 5c7ac1bb8..506596a1b 100644 --- a/src/Composer/Repository/RepositorySet.php +++ b/src/Composer/Repository/RepositorySet.php @@ -163,17 +163,31 @@ class RepositorySet return $candidates; } - public function getProviders($packageName) + public function getProvidersAndReplacers($packageName) { + $providers = array(); foreach ($this->repositories as $repository) { if ($repository instanceof ComposerRepository) { - if ($providers = $repository->getProviders($packageName)) { - return $providers; + if ($repoProviders = $repository->getProviders($packageName)) { + $providers = array_merge($providers, $repoProviders); + } + } else { + foreach ($repository->getPackages() as $candidate) { + foreach (array_merge($candidate->getProvides(), $candidate->getReplaces()) as $link) { + if ($packageName === $link->getTarget()) { + $providers[] = array( + 'name' => $candidate->getName(), + 'description' => $candidate->getDescription(), + 'type' => $candidate->getType(), + ); + continue 2; + } + } } } } - return array(); + return $providers; } public function isPackageAcceptable($names, $stability) diff --git a/tests/Composer/Test/Fixtures/installer/provider-dev-require-cannot-satisfy-require.test b/tests/Composer/Test/Fixtures/installer/provider-dev-require-cannot-satisfy-require.test new file mode 100644 index 000000000..05d03e69c --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/provider-dev-require-cannot-satisfy-require.test @@ -0,0 +1,40 @@ +--TEST-- +Test that an appropriate error is thrown if a requirement is only satisfied by a package provided by a dependency of a dev requirement. +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + {"name": "provider/requirer", "version": "1.0.0", "type": "metapackage", "require": {"b/b": "1.0.0"}}, + {"name": "b/b", "version": "1.0.0", "type": "metapackage", "provide": {"provided/pkg": "1.0.0"}} + ] + } + ], + "require": { + "provided/pkg": "1.0.0" + }, + "require-dev": { + "provider/requirer": "1.0.0" + } +} + +--RUN-- +update + +--EXPECT-EXIT-CODE-- +2 + +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Updating dependencies +Unable to find a compatible set of packages based on your non-dev requirements alone. +Your requirements can be resolved successfully when require-dev packages are present. +You may need to move packages from require-dev or some of their dependencies to require. + + Problem 1 + - Root composer.json requires provided/pkg 1.0.0, it could not be found in any version, but the following packages provide it: + - b/b + Consider requiring one of these to satisfy the provided/pkg requirement. + +--EXPECT--