Remove obsolete rules and their generation

The only automatic conflict we have results from packages using the same name
either by literally having the same name and being different versions or they
replace the same name, so

- removed all types of obsolete rules
- simplified rule generation significantly
- got rid of provide filtering in the pool
- fixed some language in error handling
main
Nils Adermann 4 years ago
parent 4e3d989978
commit 80a5fdf398

@ -87,20 +87,20 @@ class Pool implements \Countable
* packages must match or null to return all
* @return PackageInterface[] A set of packages
*/
public function whatProvides($name, ConstraintInterface $constraint = null, $allowProvide = true)
public function whatProvides($name, ConstraintInterface $constraint = null)
{
$key = ((int) $allowProvide).$constraint;
$key = (string) $constraint;
if (isset($this->providerCache[$name][$key])) {
return $this->providerCache[$name][$key];
}
return $this->providerCache[$name][$key] = $this->computeWhatProvides($name, $constraint, $allowProvide);
return $this->providerCache[$name][$key] = $this->computeWhatProvides($name, $constraint);
}
/**
* @see whatProvides
*/
private function computeWhatProvides($name, $constraint, $allowProvide = true)
private function computeWhatProvides($name, $constraint)
{
if (!isset($this->packageByName[$name])) {
return array();
@ -113,14 +113,9 @@ class Pool implements \Countable
case self::MATCH_NONE:
break;
case self::MATCH_PROVIDE:
if ($allowProvide) {
$matches[] = $candidate;
}
break;
case self::MATCH:
case self::MATCH_REPLACE:
case self::MATCH_PROVIDE:
$matches[] = $candidate;
break;

@ -29,10 +29,7 @@ abstract class Rule
const RULE_FIXED = 3;
const RULE_PACKAGE_CONFLICT = 6;
const RULE_PACKAGE_REQUIRES = 7;
const RULE_PACKAGE_OBSOLETES = 8;
const RULE_INSTALLED_PACKAGE_OBSOLETES = 9;
const RULE_PACKAGE_SAME_NAME = 10;
const RULE_PACKAGE_IMPLICIT_OBSOLETES = 11;
const RULE_LEARNED = 12;
const RULE_PACKAGE_ALIAS = 13;
@ -191,83 +188,50 @@ abstract class Rule
return $text;
case self::RULE_PACKAGE_OBSOLETES:
if (count($literals) === 2 && $literals[0] < 0 && $literals[1] < 0) {
$package1 = $pool->literalToPackage($literals[0]);
$package2 = $pool->literalToPackage($literals[1]);
$replaces1 = $this->getReplacedNames($package1);
$replaces2 = $this->getReplacedNames($package2);
$reason = null;
if ($conflictingNames = array_values(array_intersect($replaces1, $replaces2))) {
$reason = 'They both replace '.(count($conflictingNames) > 1 ? '['.implode(', ', $conflictingNames).']' : $conflictingNames[0]).' and thus cannot coexist.';
} elseif (in_array($package1->getName(), $replaces2, true)) {
$reason = $package2->getName().' replaces '.$package1->getName().' and thus cannot coexist with it.';
} elseif (in_array($package2->getName(), $replaces1, true)) {
$reason = $package1->getName().' replaces '.$package2->getName().' and thus cannot coexist with it.';
}
if ($reason) {
if (isset($installedMap[$package1->id]) && !isset($installedMap[$package2->id])) {
// swap vars so the if below passes
$tmp = $package2;
$package2 = $package1;
$package1 = $tmp;
}
if (!isset($installedMap[$package1->id]) && isset($installedMap[$package2->id])) {
return $package1->getPrettyString().' cannot be installed as that would require removing '.$package2->getPrettyString().'. '.$reason;
}
if (!isset($installedMap[$package1->id]) && !isset($installedMap[$package2->id])) {
return 'Only one of these can be installed: '.$package1->getPrettyString().', '.$package2->getPrettyString().'. '.$reason;
}
}
return 'Only one of these can be installed: '.$package1->getPrettyString().', '.$package2->getPrettyString().'.';
}
return $ruleText;
case self::RULE_INSTALLED_PACKAGE_OBSOLETES:
return $ruleText;
case self::RULE_PACKAGE_SAME_NAME:
$replacedNames = null;
$packageNames = array();
foreach ($literals as $literal) {
$package = $pool->literalToPackage($literal);
$pkgReplaces = $this->getReplacedNames($package);
if ($pkgReplaces) {
if ($replacedNames === null) {
$replacedNames = $this->getReplacedNames($package);
$packageNames[$package->getName()] = true;
}
$replacedName = $this->reasonData;
if (count($packageNames) > 1) {
$reason = null;
if (!isset($packageNames[$replacedName])) {
$reason = 'They '.(count($literals) == 2 ? 'both' : 'all').' replace '.$replacedName.' and thus cannot coexist.';
} else {
$replacerNames = $packageNames;
unset($replacerNames[$replacedName]);
$replacerNames = array_keys($replacerNames);
if (count($replacerNames) == 1) {
$reason = $replacerNames[0] . ' replaces ';
} else {
$replacedNames = array_intersect($replacedNames, $this->getReplacedNames($package));
$reason = '['.implode(', ', $replacerNames).'] replace ';
}
$reason .= $replacedName.' and thus cannot coexist with it.';
}
$packageNames[$package->getName()] = true;
}
if ($replacedNames) {
$replacedNames = array_values(array_intersect(array_keys($packageNames), $replacedNames));
}
if ($replacedNames && count($packageNames) > 1) {
$replacer = null;
$installedPackages = array();
$removablePackages = array();
foreach ($literals as $literal) {
$package = $pool->literalToPackage($literal);
if (array_intersect($replacedNames, $this->getReplacedNames($package))) {
$replacer = $package;
break;
if (isset($installedMap[abs($literal)])) {
$installedPackages[] = $pool->literalToPackage($literal);
} else {
$removablePackages[] = $pool->literalToPackage($literal);
}
}
$replacedNames = count($replacedNames) > 1 ? '['.implode(', ', $replacedNames).']' : $replacedNames[0];
if ($replacer) {
return 'Only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals) . '. '.$replacer->getName().' replaces '.$replacedNames.' and thus cannot coexist with it.';
if ($installedPackages && $removablePackages) {
return $this->formatPackagesUnique($pool, $removablePackages).' cannot be installed as that would require removing '.$this->formatPackagesUnique($pool, $installedPackages).'. '.$reason;
}
return 'Only one of these can be installed: '.$this->formatPackagesUnique($pool, $literals).'. '.$reason;
}
return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals) . '.';
case self::RULE_PACKAGE_IMPLICIT_OBSOLETES:
return $ruleText;
case self::RULE_LEARNED:
if (isset($learnedPool[$this->reasonData])) {
$learnedString = ', learned rules:'."\n - ";

@ -160,8 +160,12 @@ class RuleSetGenerator
$this->addedMap[$package->id] = true;
$this->addedPackages[] = $package;
foreach ($package->getNames(false) as $name) {
$this->addedPackagesByNames[$name][] = $package;
if (!$package instanceof AliasPackage) {
foreach ($package->getNames(false) as $name) {
$this->addedPackagesByNames[$name][] = $package;
}
} else {
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($package->getAliasOf()), Rule::RULE_PACKAGE_ALIAS, $package));
}
foreach ($package->getRequires() as $link) {
@ -177,29 +181,6 @@ class RuleSetGenerator
$workQueue->enqueue($require);
}
}
$packageName = $package->getName();
$obsoleteProviders = $this->pool->whatProvides($packageName, null, false);
foreach ($obsoleteProviders as $provider) {
if ($provider === $package) {
continue;
}
if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) {
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, $package));
} else {
if (!isset($this->conflictsForName[$packageName])) {
$this->conflictsForName[$packageName] = array();
}
if (!$package instanceof AliasPackage) {
$this->conflictsForName[$packageName][$package->id] = $package;
}
if (!$provider instanceof AliasPackage) {
$this->conflictsForName[$packageName][$provider->id] = $provider;
}
}
}
}
}
@ -218,39 +199,17 @@ class RuleSetGenerator
/** @var PackageInterface $possibleConflict */
foreach ($this->addedPackagesByNames[$link->getTarget()] as $possibleConflict) {
$conflictMatch = $this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint());
if ($conflictMatch === Pool::MATCH || $conflictMatch === Pool::MATCH_REPLACE) {
if ($this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint())) {
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $possibleConflict, Rule::RULE_PACKAGE_CONFLICT, $link));
}
}
}
// check obsoletes and implicit obsoletes of a package
foreach ($package->getReplaces() as $link) {
if (!isset($this->addedPackagesByNames[$link->getTarget()])) {
continue;
}
/** @var PackageInterface $possibleConflict */
foreach ($this->addedPackagesByNames[$link->getTarget()] as $provider) {
if ($provider === $package) {
continue;
}
if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
$reason = Rule::RULE_PACKAGE_OBSOLETES;
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $link));
}
}
}
}
foreach ($this->conflictsForName as $name => $packages) {
foreach ($this->addedPackagesByNames as $name => $packages) {
if (count($packages) > 1) {
$reason = Rule::RULE_PACKAGE_SAME_NAME;
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createMultiConflictRule($packages, $reason, null));
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createMultiConflictRule($packages, $reason, $name));
}
}
}

@ -42,7 +42,7 @@ Your requirements could not be resolved to an installable set of packages.
Problem 1
- __root__ is present at version 1.2.3 and cannot be modified by Composer
- provider/pkg 1.0.0 cannot be installed as that would require removing __root__ 1.2.3. They both replace root-replaced/transitive-replaced and thus cannot coexist.
- provider/pkg[1.0.0] cannot be installed as that would require removing __root__[1.2.3]. They both replace root-replaced/transitive-replaced and thus cannot coexist.
- Root composer.json requires provider/pkg * -> satisfiable by provider/pkg[1.0.0].
--EXPECT--

Loading…
Cancel
Save