List identical/compatible removed versions in problem output as if they had not been removed
parent
34183f49f9
commit
ce2a40b259
|
@ -36,16 +36,57 @@ class Pool implements \Countable
|
|||
protected $providerCache = array();
|
||||
/** @var BasePackage[] */
|
||||
protected $unacceptableFixedOrLockedPackages;
|
||||
/** @var array<string, array<string, string>> Map of package name => normalized version => pretty version */
|
||||
protected $removedVersions = array();
|
||||
/** @var array<string, array<string, string>> Map of package object hash => removed normalized versions => removed pretty version */
|
||||
protected $removedVersionsByPackage = array();
|
||||
|
||||
/**
|
||||
* @param BasePackage[] $packages
|
||||
* @param BasePackage[] $unacceptableFixedOrLockedPackages
|
||||
* @param array<string, array<string, string>> $removedVersions
|
||||
* @param array<string, array<string, string>> $removedVersionsByPackage
|
||||
*/
|
||||
public function __construct(array $packages = array(), array $unacceptableFixedOrLockedPackages = array())
|
||||
public function __construct(array $packages = array(), array $unacceptableFixedOrLockedPackages = array(), array $removedVersions = array(), array $removedVersionsByPackage = array())
|
||||
{
|
||||
$this->versionParser = new VersionParser;
|
||||
$this->setPackages($packages);
|
||||
$this->unacceptableFixedOrLockedPackages = $unacceptableFixedOrLockedPackages;
|
||||
$this->removedVersions = $removedVersions;
|
||||
$this->removedVersionsByPackage = $removedVersionsByPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getRemovedVersions($name, ConstraintInterface $constraint)
|
||||
{
|
||||
if (!isset($this->removedVersions[$name])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach ($this->removedVersions[$name] as $version => $prettyVersion) {
|
||||
if ($constraint->matches(new Constraint('==', $version))) {
|
||||
$result[$version] = $prettyVersion;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $objectHash
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getRemovedVersionsByPackage($objectHash)
|
||||
{
|
||||
if (!isset($this->removedVersionsByPackage[$objectHash])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $this->removedVersionsByPackage[$objectHash];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\DependencyResolver;
|
|||
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Semver\CompilingMatcher;
|
||||
use Composer\Semver\Constraint\ConstraintInterface;
|
||||
use Composer\Semver\Constraint\Constraint;
|
||||
|
@ -57,6 +58,11 @@ class PoolOptimizer
|
|||
*/
|
||||
private $aliasesPerPackage = array();
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, string>>
|
||||
*/
|
||||
private $removedVersionsByPackage = array();
|
||||
|
||||
public function __construct(PolicyInterface $policy)
|
||||
{
|
||||
$this->policy = $policy;
|
||||
|
@ -81,6 +87,7 @@ class PoolOptimizer
|
|||
$this->conflictConstraintsPerPackage = array();
|
||||
$this->packagesToRemove = array();
|
||||
$this->aliasesPerPackage = array();
|
||||
$this->removedVersionsByPackage = array();
|
||||
|
||||
return $optimizedPool;
|
||||
}
|
||||
|
@ -165,13 +172,16 @@ class PoolOptimizer
|
|||
private function applyRemovalsToPool(Pool $pool)
|
||||
{
|
||||
$packages = array();
|
||||
$removedVersions = array();
|
||||
foreach ($pool->getPackages() as $package) {
|
||||
if (!isset($this->packagesToRemove[$package->id])) {
|
||||
$packages[] = $package;
|
||||
} else {
|
||||
$removedVersions[$package->getName()][$package->getVersion()] = $package->getPrettyVersion();
|
||||
}
|
||||
}
|
||||
|
||||
$optimizedPool = new Pool($packages, $pool->getUnacceptableFixedOrLockedPackages());
|
||||
$optimizedPool = new Pool($packages, $pool->getUnacceptableFixedOrLockedPackages(), $removedVersions, $this->removedVersionsByPackage);
|
||||
|
||||
// Reset package removals
|
||||
$this->packagesToRemove = array();
|
||||
|
@ -184,8 +194,8 @@ class PoolOptimizer
|
|||
*/
|
||||
private function optimizeByIdenticalDependencies(Pool $pool)
|
||||
{
|
||||
$identicalDefinitionPerPackage = array();
|
||||
$packageIdsToRemove = array();
|
||||
$identicalDefinitionsPerPackage = array();
|
||||
$packageIdenticalDefinitionLookup = array();
|
||||
|
||||
foreach ($pool->getPackages() as $package) {
|
||||
|
||||
|
@ -195,7 +205,7 @@ class PoolOptimizer
|
|||
continue;
|
||||
}
|
||||
|
||||
$packageIdsToRemove[$package->id] = true;
|
||||
$this->markPackageForRemoval($package->id);
|
||||
|
||||
$dependencyHash = $this->calculateDependencyHash($package);
|
||||
|
||||
|
@ -206,7 +216,6 @@ class PoolOptimizer
|
|||
}
|
||||
|
||||
foreach ($this->requireConstraintsPerPackage[$packageName] as $requireConstraint) {
|
||||
|
||||
$groupHashParts = array();
|
||||
|
||||
if (CompilingMatcher::match($requireConstraint, Constraint::OP_EQ, $package->getVersion())) {
|
||||
|
@ -234,32 +243,19 @@ class PoolOptimizer
|
|||
continue;
|
||||
}
|
||||
|
||||
$identicalDefinitionPerPackage[$packageName][implode('', $groupHashParts)][$dependencyHash][] = $package;
|
||||
$groupHash = implode('', $groupHashParts);
|
||||
$identicalDefinitionsPerPackage[$packageName][$groupHash][$dependencyHash][] = $package;
|
||||
$packageIdenticalDefinitionLookup[$package->id][$packageName] = array('groupHash' => $groupHash, 'dependencyHash' => $dependencyHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$keepPackage = function (BasePackage $package, $aliasesPerPackage) use (&$packageIdsToRemove, &$keepPackage) {
|
||||
unset($packageIdsToRemove[$package->id]);
|
||||
if ($package instanceof AliasPackage) {
|
||||
// recursing here so aliasesPerPackage for the aliasOf can be checked
|
||||
// and all its aliases marked to be kept as well
|
||||
$keepPackage($package->getAliasOf(), $aliasesPerPackage);
|
||||
}
|
||||
if (isset($aliasesPerPackage[$package->id])) {
|
||||
foreach ($aliasesPerPackage[$package->id] as $aliasPackage) {
|
||||
unset($packageIdsToRemove[$aliasPackage->id]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
foreach ($identicalDefinitionPerPackage as $package => $constraintGroups) {
|
||||
foreach ($identicalDefinitionsPerPackage as $constraintGroups) {
|
||||
foreach ($constraintGroups as $constraintGroup) {
|
||||
foreach ($constraintGroup as $hash => $packages) {
|
||||
|
||||
foreach ($constraintGroup as $packages) {
|
||||
// Only one package in this constraint group has the same requirements, we're not allowed to remove that package
|
||||
if (1 === \count($packages)) {
|
||||
$keepPackage($packages[0], $this->aliasesPerPackage);
|
||||
$this->keepPackage($packages[0], $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -272,17 +268,12 @@ class PoolOptimizer
|
|||
}
|
||||
|
||||
foreach ($this->policy->selectPreferredPackages($pool, $literals) as $preferredLiteral) {
|
||||
$keepPackage($pool->literalToPackage($preferredLiteral), $this->aliasesPerPackage);
|
||||
$this->keepPackage($pool->literalToPackage($preferredLiteral), $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($packageIdsToRemove as $id => $dummy) {
|
||||
$this->markPackageForRemoval($id);
|
||||
}
|
||||
|
||||
// Apply removals
|
||||
return $this->applyRemovalsToPool($pool);
|
||||
}
|
||||
|
||||
|
@ -342,4 +333,54 @@ class PoolOptimizer
|
|||
|
||||
$this->packagesToRemove[$id] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, array<string, list<BasePackage>>>> $identicalDefinitionsPerPackage
|
||||
* @param array<int, array<string, array{groupHash: string, dependencyHash: string}>> $packageIdenticalDefinitionLookup
|
||||
* @return void
|
||||
*/
|
||||
private function keepPackage(BasePackage $package, $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup)
|
||||
{
|
||||
unset($this->packagesToRemove[$package->id]);
|
||||
|
||||
if ($package instanceof AliasPackage) {
|
||||
// recursing here so aliasesPerPackage for the aliasOf can be checked
|
||||
// and all its aliases marked to be kept as well
|
||||
$this->keepPackage($package->getAliasOf(), $identicalDefinitionsPerPackage, $packageIdenticalDefinitionLookup);
|
||||
}
|
||||
|
||||
// record all the versions of the package group so we can list them later in Problem output
|
||||
foreach ($package->getNames(false) as $name) {
|
||||
if (isset($packageIdenticalDefinitionLookup[$package->id][$name])) {
|
||||
$packageGroupPointers = $packageIdenticalDefinitionLookup[$package->id][$name];
|
||||
$packageGroup = $identicalDefinitionsPerPackage[$name][$packageGroupPointers['groupHash']][$packageGroupPointers['dependencyHash']];
|
||||
foreach ($packageGroup as $pkg) {
|
||||
if ($pkg instanceof AliasPackage && $pkg->getPrettyVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) {
|
||||
$pkg = $pkg->getAliasOf();
|
||||
}
|
||||
$this->removedVersionsByPackage[spl_object_hash($package)][$pkg->getVersion()] = $pkg->getPrettyVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->aliasesPerPackage[$package->id])) {
|
||||
foreach ($this->aliasesPerPackage[$package->id] as $aliasPackage) {
|
||||
unset($this->packagesToRemove[$aliasPackage->id]);
|
||||
|
||||
// record all the versions of the package group so we can list them later in Problem output
|
||||
foreach ($aliasPackage->getNames(false) as $name) {
|
||||
if (isset($packageIdenticalDefinitionLookup[$aliasPackage->id][$name])) {
|
||||
$packageGroupPointers = $packageIdenticalDefinitionLookup[$aliasPackage->id][$name];
|
||||
$packageGroup = $identicalDefinitionsPerPackage[$name][$packageGroupPointers['groupHash']][$packageGroupPointers['dependencyHash']];
|
||||
foreach ($packageGroup as $pkg) {
|
||||
if ($pkg instanceof AliasPackage && $pkg->getPrettyVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) {
|
||||
$pkg = $pkg->getAliasOf();
|
||||
}
|
||||
$this->removedVersionsByPackage[spl_object_hash($aliasPackage)][$pkg->getVersion()] = $pkg->getPrettyVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,6 +126,10 @@ class Problem
|
|||
$template = preg_replace('{^\S+ \S+ }', '%s%s ', $message);
|
||||
$messages[] = $template;
|
||||
$templates[$template][$m[1]][$parser->normalize($m[2])] = $m[2];
|
||||
$sourcePackage = $rule->getSourcePackage($pool);
|
||||
foreach ($pool->getRemovedVersionsByPackage(spl_object_hash($sourcePackage)) as $version => $prettyVersion) {
|
||||
$templates[$template][$m[1]][$version] = $prettyVersion;
|
||||
}
|
||||
} elseif ($message !== '') {
|
||||
$messages[] = $message;
|
||||
}
|
||||
|
@ -267,7 +271,7 @@ class Problem
|
|||
return $rootReqs[$packageName]->matches(new Constraint('==', $p->getVersion()));
|
||||
});
|
||||
if (0 === count($filtered)) {
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with your root composer.json require ('.$rootReqs[$packageName]->getPrettyString().').');
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose, $pool, $constraint).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with your root composer.json require ('.$rootReqs[$packageName]->getPrettyString().').');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,7 +281,7 @@ class Problem
|
|||
return $fixedConstraint->matches(new Constraint('==', $p->getVersion()));
|
||||
});
|
||||
if (0 === count($filtered)) {
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but the package is fixed to '.$lockedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.');
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose, $pool, $constraint).' but the package is fixed to '.$lockedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,27 +290,27 @@ class Problem
|
|||
});
|
||||
|
||||
if (!$nonLockedPackages) {
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' in the lock file but not in remote repositories, make sure you avoid updating this package to keep the one from the lock file.');
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose, $pool, $constraint).' in the lock file but not in remote repositories, make sure you avoid updating this package to keep the one from the lock file.');
|
||||
}
|
||||
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but these were not loaded, likely because '.(self::hasMultipleNames($packages) ? 'they conflict' : 'it conflicts').' with another require.');
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose, $pool, $constraint).' but these were not loaded, likely because '.(self::hasMultipleNames($packages) ? 'they conflict' : 'it conflicts').' with another require.');
|
||||
}
|
||||
|
||||
// check if the package is found when bypassing stability checks
|
||||
if ($packages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) {
|
||||
// we must first verify if a valid package would be found in a lower priority repository
|
||||
if ($allReposPackages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) {
|
||||
return self::computeCheckForLowerPrioRepo($isVerbose, $packageName, $packages, $allReposPackages, 'minimum-stability', $constraint);
|
||||
return self::computeCheckForLowerPrioRepo($pool, $isVerbose, $packageName, $packages, $allReposPackages, 'minimum-stability', $constraint);
|
||||
}
|
||||
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your minimum-stability.');
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose, $pool, $constraint).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your minimum-stability.');
|
||||
}
|
||||
|
||||
// check if the package is found when bypassing the constraint and stability checks
|
||||
if ($packages = $repositorySet->findPackages($packageName, null, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) {
|
||||
// we must first verify if a valid package would be found in a lower priority repository
|
||||
if ($allReposPackages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) {
|
||||
return self::computeCheckForLowerPrioRepo($isVerbose, $packageName, $packages, $allReposPackages, 'constraint', $constraint);
|
||||
return self::computeCheckForLowerPrioRepo($pool, $isVerbose, $packageName, $packages, $allReposPackages, 'constraint', $constraint);
|
||||
}
|
||||
|
||||
$suffix = '';
|
||||
|
@ -326,7 +330,7 @@ class Problem
|
|||
$suffix = ' See https://getcomposer.org/dep-on-root for details and assistance.';
|
||||
}
|
||||
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match the constraint.' . $suffix);
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose, $pool, $constraint).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match the constraint.' . $suffix);
|
||||
}
|
||||
|
||||
if (!preg_match('{^[A-Za-z0-9_./-]+$}', $packageName)) {
|
||||
|
@ -356,15 +360,26 @@ class Problem
|
|||
* @internal
|
||||
* @param PackageInterface[] $packages
|
||||
* @param bool $isVerbose
|
||||
* @param bool $useRemovedVersionGroup
|
||||
* @return string
|
||||
*/
|
||||
public static function getPackageList(array $packages, $isVerbose)
|
||||
public static function getPackageList(array $packages, $isVerbose, Pool $pool = null, ConstraintInterface $constraint = null, $useRemovedVersionGroup = false)
|
||||
{
|
||||
$prepared = array();
|
||||
$hasDefaultBranch = array();
|
||||
foreach ($packages as $package) {
|
||||
$prepared[$package->getName()]['name'] = $package->getPrettyName();
|
||||
$prepared[$package->getName()]['versions'][$package->getVersion()] = $package->getPrettyVersion().($package instanceof AliasPackage ? ' (alias of '.$package->getAliasOf()->getPrettyVersion().')' : '');
|
||||
if ($pool && $constraint) {
|
||||
foreach ($pool->getRemovedVersions($package->getName(), $constraint) as $version => $prettyVersion) {
|
||||
$prepared[$package->getName()]['versions'][$version] = $prettyVersion;
|
||||
}
|
||||
}
|
||||
if ($pool && $useRemovedVersionGroup) {
|
||||
foreach ($pool->getRemovedVersionsByPackage(spl_object_hash($package)) as $version => $prettyVersion) {
|
||||
$prepared[$package->getName()]['versions'][$version] = $prettyVersion;
|
||||
}
|
||||
}
|
||||
if ($package->isDefaultBranch()) {
|
||||
$hasDefaultBranch[$package->getName()] = true;
|
||||
}
|
||||
|
@ -469,7 +484,7 @@ class Problem
|
|||
* @param string $reason
|
||||
* @return array{0: string, 1: string}
|
||||
*/
|
||||
private static function computeCheckForLowerPrioRepo($isVerbose, $packageName, array $higherRepoPackages, array $allReposPackages, $reason, ConstraintInterface $constraint = null)
|
||||
private static function computeCheckForLowerPrioRepo(Pool $pool, $isVerbose, $packageName, array $higherRepoPackages, array $allReposPackages, $reason, ConstraintInterface $constraint = null)
|
||||
{
|
||||
$nextRepoPackages = array();
|
||||
$nextRepo = null;
|
||||
|
@ -488,7 +503,7 @@ class Problem
|
|||
if ($topPackage instanceof RootPackageInterface) {
|
||||
return array(
|
||||
"- Root composer.json requires $packageName".self::constraintToText($constraint).', it is ',
|
||||
'satisfiable by '.self::getPackageList($nextRepoPackages, $isVerbose).' from '.$nextRepo->getRepoName().' but '.$topPackage->getPrettyName().' is the root package and cannot be modified. See https://getcomposer.org/dep-on-root for details and assistance.',
|
||||
'satisfiable by '.self::getPackageList($nextRepoPackages, $isVerbose, $pool, $constraint).' from '.$nextRepo->getRepoName().' but '.$topPackage->getPrettyName().' is the root package and cannot be modified. See https://getcomposer.org/dep-on-root for details and assistance.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -497,10 +512,10 @@ class Problem
|
|||
$singular = count($higherRepoPackages) === 1;
|
||||
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ',
|
||||
'found '.self::getPackageList($nextRepoPackages, $isVerbose).' in the lock file and '.self::getPackageList($higherRepoPackages, $isVerbose).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' but ' . ($singular ? 'it does' : 'these do') . ' not match your '.$reason.' and ' . ($singular ? 'is' : 'are') . ' therefore not installable. Make sure you either fix the '.$reason.' or avoid updating this package to keep the one from the lock file.', );
|
||||
'found '.self::getPackageList($nextRepoPackages, $isVerbose, $pool, $constraint).' in the lock file and '.self::getPackageList($higherRepoPackages, $isVerbose, $pool, $constraint).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' but ' . ($singular ? 'it does' : 'these do') . ' not match your '.$reason.' and ' . ($singular ? 'is' : 'are') . ' therefore not installable. Make sure you either fix the '.$reason.' or avoid updating this package to keep the one from the lock file.', );
|
||||
}
|
||||
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', 'satisfiable by '.self::getPackageList($nextRepoPackages, $isVerbose).' from '.$nextRepo->getRepoName().' but '.self::getPackageList($higherRepoPackages, $isVerbose).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' has higher repository priority. The packages with higher priority do not match your '.$reason.' and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.');
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', 'satisfiable by '.self::getPackageList($nextRepoPackages, $isVerbose, $pool, $constraint).' from '.$nextRepo->getRepoName().' but '.self::getPackageList($higherRepoPackages, $isVerbose, $pool, $constraint).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' has higher repository priority. The packages with higher priority do not match your '.$reason.' and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -228,6 +228,41 @@ abstract class Rule
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return BasePackage
|
||||
*/
|
||||
public function getSourcePackage(Pool $pool)
|
||||
{
|
||||
$literals = $this->getLiterals();
|
||||
|
||||
switch ($this->getReason()) {
|
||||
case self::RULE_PACKAGE_CONFLICT:
|
||||
$package1 = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[0]));
|
||||
$package2 = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[1]));
|
||||
|
||||
$conflictTarget = $package1->getPrettyString();
|
||||
if ($reasonData = $this->getReasonData()) {
|
||||
// swap literals if they are not in the right order with package2 being the conflicter
|
||||
if ($reasonData->getSource() === $package1->getName()) {
|
||||
list($package2, $package1) = array($package1, $package2);
|
||||
}
|
||||
}
|
||||
|
||||
return $package2;
|
||||
|
||||
case self::RULE_PACKAGE_REQUIRES:
|
||||
$sourceLiteral = array_shift($literals);
|
||||
$sourcePackage = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($sourceLiteral));
|
||||
|
||||
return $sourcePackage;
|
||||
|
||||
default:
|
||||
throw new \LogicException('Not implemented');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param bool $isVerbose
|
||||
* @param BasePackage[] $installedMap
|
||||
|
@ -258,7 +293,7 @@ abstract class Rule
|
|||
}
|
||||
}
|
||||
|
||||
return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages, $isVerbose).'.';
|
||||
return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages, $isVerbose, $constraint).'.';
|
||||
|
||||
case self::RULE_FIXED:
|
||||
$package = $this->deduplicateDefaultBranchAlias($this->reasonData['package']);
|
||||
|
@ -320,7 +355,7 @@ abstract class Rule
|
|||
|
||||
$text = $reasonData->getPrettyString($sourcePackage);
|
||||
if ($requires) {
|
||||
$text .= ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $requires, $isVerbose) . '.';
|
||||
$text .= ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $requires, $isVerbose, $this->reasonData->getConstraint()) . '.';
|
||||
} else {
|
||||
$targetName = $reasonData->getTarget();
|
||||
|
||||
|
@ -368,13 +403,13 @@ abstract class Rule
|
|||
}
|
||||
|
||||
if ($installedPackages && $removablePackages) {
|
||||
return $this->formatPackagesUnique($pool, $removablePackages, $isVerbose).' cannot be installed as that would require removing '.$this->formatPackagesUnique($pool, $installedPackages, $isVerbose).'. '.$reason;
|
||||
return $this->formatPackagesUnique($pool, $removablePackages, $isVerbose, null, true).' cannot be installed as that would require removing '.$this->formatPackagesUnique($pool, $installedPackages, $isVerbose, null, true).'. '.$reason;
|
||||
}
|
||||
|
||||
return 'Only one of these can be installed: '.$this->formatPackagesUnique($pool, $literals, $isVerbose).'. '.$reason;
|
||||
return 'Only one of these can be installed: '.$this->formatPackagesUnique($pool, $literals, $isVerbose, null, true).'. '.$reason;
|
||||
}
|
||||
|
||||
return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals, $isVerbose) . '.';
|
||||
return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals, $isVerbose, null, true) . '.';
|
||||
case self::RULE_LEARNED:
|
||||
/** @TODO currently still generates way too much output to be helpful, and in some cases can even lead to endless recursion */
|
||||
// if (isset($learnedPool[$this->reasonData])) {
|
||||
|
@ -445,9 +480,10 @@ abstract class Rule
|
|||
/**
|
||||
* @param array<int|BasePackage> $packages An array containing packages or literals
|
||||
* @param bool $isVerbose
|
||||
* @param bool $useRemovedVersionGroup
|
||||
* @return string
|
||||
*/
|
||||
protected function formatPackagesUnique(Pool $pool, array $packages, $isVerbose)
|
||||
protected function formatPackagesUnique(Pool $pool, array $packages, $isVerbose, ConstraintInterface $constraint = null, $useRemovedVersionGroup = false)
|
||||
{
|
||||
foreach ($packages as $index => $package) {
|
||||
if (!\is_object($package)) {
|
||||
|
@ -455,7 +491,7 @@ abstract class Rule
|
|||
}
|
||||
}
|
||||
|
||||
return Problem::getPackageList($packages, $isVerbose);
|
||||
return Problem::getPackageList($packages, $isVerbose, $pool, $constraint, $useRemovedVersionGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -460,6 +460,8 @@ class Installer
|
|||
$this->io->writeError("Analyzed ".count($pool)." packages to resolve dependencies", true, IOInterface::VERBOSE);
|
||||
$this->io->writeError("Analyzed ".$ruleSetSize." rules to resolve dependencies", true, IOInterface::VERBOSE);
|
||||
|
||||
$pool = null;
|
||||
|
||||
if (!$lockTransaction->getOperations()) {
|
||||
$this->io->writeError('Nothing to modify in lock file');
|
||||
}
|
||||
|
|
|
@ -11,15 +11,15 @@ Test the error output of solver problems is deduplicated.
|
|||
{ "name": "package/a", "version": "2.0.2", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.0.3", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.1.0", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.2.0", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.3.1", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.3.2", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.3.3", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.3.4", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.3.5", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.4.0", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.5.0", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.6.0", "require": { "missing/dep": "^1.0" } },
|
||||
{ "name": "package/a", "version": "2.2.0", "require": { "missing/dep": "^1.1" } },
|
||||
{ "name": "package/a", "version": "2.3.1", "require": { "missing/dep": "^1.1" } },
|
||||
{ "name": "package/a", "version": "2.3.2", "require": { "missing/dep": "^1.1" } },
|
||||
{ "name": "package/a", "version": "2.3.3", "require": { "missing/dep": "^1.1" } },
|
||||
{ "name": "package/a", "version": "2.3.4", "require": { "missing/dep": "^1.1" } },
|
||||
{ "name": "package/a", "version": "2.3.5", "require": { "missing/dep": "^1.1" } },
|
||||
{ "name": "package/a", "version": "2.4.0", "require": { "missing/dep": "^1.1" } },
|
||||
{ "name": "package/a", "version": "2.5.0", "require": { "missing/dep": "^1.1" } },
|
||||
{ "name": "package/a", "version": "2.6.0", "require": { "missing/dep": "^1.1" } },
|
||||
{ "name": "missing/dep", "version": "2.0.0" }
|
||||
]
|
||||
}
|
||||
|
@ -41,17 +41,9 @@ Updating dependencies
|
|||
Your requirements could not be resolved to an installable set of packages.
|
||||
|
||||
Problem 1
|
||||
- package/a[2.0.0, ..., 2.6.0] require missing/dep ^1.0 -> found missing/dep[2.0.0] but it does not match the constraint.
|
||||
- package/a[2.2.0, ..., 2.6.0] require missing/dep ^1.1 -> found missing/dep[2.0.0] but it does not match the constraint.
|
||||
- package/a[2.0.0, ..., 2.1.0] require missing/dep ^1.0 -> found missing/dep[2.0.0] but it does not match the constraint.
|
||||
- Root composer.json requires package/a * -> satisfiable by package/a[2.0.0, ..., 2.6.0].
|
||||
|
||||
--EXPECT-OUTPUT-OPTIMIZED--
|
||||
Loading composer repositories with package information
|
||||
Updating dependencies
|
||||
Your requirements could not be resolved to an installable set of packages.
|
||||
|
||||
Problem 1
|
||||
- Root composer.json requires package/a * -> satisfiable by package/a[2.6.0].
|
||||
- package/a 2.6.0 requires missing/dep ^1.0 -> found missing/dep[2.0.0] but it does not match the constraint.
|
||||
|
||||
--EXPECT--
|
||||
|
||||
|
|
|
@ -137,11 +137,11 @@ Your requirements could not be resolved to an installable set of packages.
|
|||
Problem 1
|
||||
- Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0].
|
||||
- illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26].
|
||||
- illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.10].
|
||||
- illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.8].
|
||||
- You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.8, v3.1.10, v3.4.29].
|
||||
- friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.4.29].
|
||||
- Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.5].
|
||||
- illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10].
|
||||
- illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8].
|
||||
- You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29].
|
||||
- friendsofphp/php-cs-fixer[v2.10.4, ..., v2.10.5] require symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29].
|
||||
- Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5].
|
||||
|
||||
--EXPECT--
|
||||
|
||||
|
|
|
@ -50,8 +50,8 @@ Updating dependencies
|
|||
Your requirements could not be resolved to an installable set of packages.
|
||||
|
||||
Problem 1
|
||||
- Only one of these can be installed: regular/pkg[1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
|
||||
- Root composer.json requires regular/pkg 1.* -> satisfiable by regular/pkg[1.0.3].
|
||||
- Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
|
||||
- Root composer.json requires regular/pkg 1.* -> satisfiable by regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3].
|
||||
- Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].
|
||||
|
||||
--EXPECT--
|
||||
|
|
Loading…
Reference in New Issue