Improve error reporting for conflicts/replaces of various kinds, fixes #7254
parent
6c9d9e775c
commit
1d4cdb60d0
|
@ -188,11 +188,52 @@ abstract class Rule
|
||||||
return $text;
|
return $text;
|
||||||
|
|
||||||
case self::RULE_PACKAGE_OBSOLETES:
|
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]);
|
||||||
|
$conflictingNames = array_values(array_intersect($package1->getNames(), $package2->getNames()));
|
||||||
|
$provideClash = count($conflictingNames) > 1 ? '['.implode(', ', $conflictingNames).']' : $conflictingNames[0];
|
||||||
|
|
||||||
|
if ($conflictingNames && isset($installedMap[$package1->id]) && !isset($installedMap[$package2->id])) {
|
||||||
|
// swap vars so the if below passes
|
||||||
|
$tmp = $package2;
|
||||||
|
$package2 = $package1;
|
||||||
|
$package1 = $tmp;
|
||||||
|
}
|
||||||
|
if ($conflictingNames && !isset($installedMap[$package1->id]) && isset($installedMap[$package2->id])) {
|
||||||
|
return $package1->getPrettyString().' can not be installed as that would require removing '.$package2->getPrettyString().'. They both provide '.$provideClash.' and can thus not coexist.';
|
||||||
|
}
|
||||||
|
if (!isset($installedMap[$package1->id]) && !isset($installedMap[$package2->id])) {
|
||||||
|
if ($conflictingNames) {
|
||||||
|
return 'Only one of these can be installed: '.$package1->getPrettyString().', '.$package2->getPrettyString().'. They both provide '.$provideClash.' and can thus not coexist.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Only one of these can be installed: '.$package1->getPrettyString().', '.$package2->getPrettyString().'.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $ruleText;
|
return $ruleText;
|
||||||
case self::RULE_INSTALLED_PACKAGE_OBSOLETES:
|
case self::RULE_INSTALLED_PACKAGE_OBSOLETES:
|
||||||
return $ruleText;
|
return $ruleText;
|
||||||
case self::RULE_PACKAGE_SAME_NAME:
|
case self::RULE_PACKAGE_SAME_NAME:
|
||||||
return 'Same name, can only install one of: ' . $this->formatPackagesUnique($pool, $literals) . '.';
|
$conflictingNames = null;
|
||||||
|
$allNames = array();
|
||||||
|
foreach ($literals as $literal) {
|
||||||
|
$package = $pool->literalToPackage($literal);
|
||||||
|
if ($conflictingNames === null) {
|
||||||
|
$conflictingNames = $package->getNames();
|
||||||
|
} else {
|
||||||
|
$conflictingNames = array_values(array_intersect($conflictingNames, $package->getNames()));
|
||||||
|
}
|
||||||
|
$allNames = array_unique(array_merge($allNames, $package->getNames()));
|
||||||
|
}
|
||||||
|
$provideClash = count($conflictingNames) > 1 ? '['.implode(', ', $conflictingNames).']' : $conflictingNames[0];
|
||||||
|
|
||||||
|
if ($conflictingNames && count($allNames) > 1) {
|
||||||
|
return 'Only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals) . '. They all provide '.$provideClash.' and can thus not coexist.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals) . '.';
|
||||||
case self::RULE_PACKAGE_IMPLICIT_OBSOLETES:
|
case self::RULE_PACKAGE_IMPLICIT_OBSOLETES:
|
||||||
return $ruleText;
|
return $ruleText;
|
||||||
case self::RULE_LEARNED:
|
case self::RULE_LEARNED:
|
||||||
|
|
|
@ -725,7 +725,7 @@ class SolverTest extends TestCase
|
||||||
$msg .= " - C 1.0 requires d >= 1.0 -> satisfiable by D[1.0].\n";
|
$msg .= " - C 1.0 requires d >= 1.0 -> satisfiable by D[1.0].\n";
|
||||||
$msg .= " - D 1.0 requires b < 1.0 -> satisfiable by B[0.9].\n";
|
$msg .= " - D 1.0 requires b < 1.0 -> satisfiable by B[0.9].\n";
|
||||||
$msg .= " - B 1.0 requires c >= 1.0 -> satisfiable by C[1.0].\n";
|
$msg .= " - B 1.0 requires c >= 1.0 -> satisfiable by C[1.0].\n";
|
||||||
$msg .= " - Same name, can only install one of: B[0.9, 1.0].\n";
|
$msg .= " - Only one of these can be installed: B[0.9, 1.0].\n";
|
||||||
$msg .= " - A 1.0 requires b >= 1.0 -> satisfiable by B[1.0].\n";
|
$msg .= " - A 1.0 requires b >= 1.0 -> satisfiable by B[1.0].\n";
|
||||||
$msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n";
|
$msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n";
|
||||||
$this->assertEquals($msg, $e->getMessage());
|
$this->assertEquals($msg, $e->getMessage());
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
--TEST--
|
||||||
|
Test that providers provided by a dependent and root package cause a conflict
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"version": "1.2.3",
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"name": "provider/pkg",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"provide": { "root-provided/transitive-provided": "2.*", "root-replaced/transitive-provided": "2.*" },
|
||||||
|
"replace": { "root-provided/transitive-replaced": "2.*", "root-replaced/transitive-replaced": "2.*" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"provider/pkg": "*"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"root-provided/transitive-replaced": "2.*",
|
||||||
|
"root-provided/transitive-provided": "2.*"
|
||||||
|
},
|
||||||
|
"replace": {
|
||||||
|
"root-replaced/transitive-replaced": "2.*",
|
||||||
|
"root-replaced/transitive-provided": "2.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--RUN--
|
||||||
|
update
|
||||||
|
|
||||||
|
--EXPECT-EXIT-CODE--
|
||||||
|
2
|
||||||
|
|
||||||
|
--EXPECT-OUTPUT--
|
||||||
|
Loading composer repositories with package information
|
||||||
|
Updating dependencies
|
||||||
|
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 can not be installed as that would require removing __root__ 1.2.3. They both provide [root-provided/transitive-provided, root-replaced/transitive-provided, root-provided/transitive-replaced, root-replaced/transitive-replaced] and can thus not coexist.
|
||||||
|
- Root composer.json requires provider/pkg * -> satisfiable by provider/pkg[1.0.0].
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
--TEST--
|
||||||
|
Test that providers provided by two dependents cause a conflict
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"name": "provider/pkg",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"provide": { "third/pkg": "2.*" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "replacer/pkg",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"replace": { "third/pkg": "2.*" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"provider/pkg": "*",
|
||||||
|
"replacer/pkg": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--RUN--
|
||||||
|
update
|
||||||
|
|
||||||
|
--EXPECT-EXIT-CODE--
|
||||||
|
2
|
||||||
|
|
||||||
|
--EXPECT-OUTPUT--
|
||||||
|
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 provider/pkg * -> satisfiable by provider/pkg[1.0.0].
|
||||||
|
- Only one of these can be installed: replacer/pkg 1.0.0, provider/pkg 1.0.0. They both provide third/pkg and can thus not coexist.
|
||||||
|
- Root composer.json requires replacer/pkg * -> satisfiable by replacer/pkg[1.0.0].
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
|
Loading…
Reference in New Issue