Merge pull request #8859 from Seldaek/deduplicate-problems
Deduplicate require/conflict rules which are the same but for different versions of the same packagepull/8880/head
commit
ff0717ad66
|
@ -95,11 +95,42 @@ class Problem
|
||||||
}
|
}
|
||||||
|
|
||||||
$messages = array();
|
$messages = array();
|
||||||
|
$templates = array();
|
||||||
|
$parser = new VersionParser;
|
||||||
|
$deduplicatableRuleTypes = array(Rule::RULE_PACKAGE_REQUIRES, Rule::RULE_PACKAGE_CONFLICT);
|
||||||
foreach ($reasons as $rule) {
|
foreach ($reasons as $rule) {
|
||||||
$messages[] = $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool);
|
$message = $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool);
|
||||||
|
if (in_array($rule->getReason(), $deduplicatableRuleTypes, true) && preg_match('{^(?P<package>\S+) (?P<version>\S+) (?P<type>requires|conflicts)}', $message, $m)) {
|
||||||
|
$template = preg_replace('{^\S+ \S+ }', '%s%s ', $message);
|
||||||
|
$messages[] = $template;
|
||||||
|
$templates[$template][$m[1]][$parser->normalize($m[2])] = $m[2];
|
||||||
|
} else {
|
||||||
|
$messages[] = $message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "\n - ".implode("\n - ", array_unique($messages));
|
$result = array();
|
||||||
|
foreach (array_unique($messages) as $message) {
|
||||||
|
if (isset($templates[$message])) {
|
||||||
|
foreach ($templates[$message] as $package => $versions) {
|
||||||
|
uksort($versions, 'version_compare');
|
||||||
|
if (!$isVerbose) {
|
||||||
|
$versions = self::condenseVersionList($versions, 1);
|
||||||
|
}
|
||||||
|
if (count($versions) > 1) {
|
||||||
|
// remove the s from requires/conflicts to correct grammar
|
||||||
|
$message = preg_replace('{^(%s%s (?:require|conflict))s}', '$1', $message);
|
||||||
|
$result[] = sprintf($message, $package, '['.implode(', ', $versions).']');
|
||||||
|
} else {
|
||||||
|
$result[] = sprintf($message, $package, ' '.reset($versions));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$result[] = $message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "\n - ".implode("\n - ", $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isCausedByLock()
|
public function isCausedByLock()
|
||||||
|
@ -299,32 +330,46 @@ class Problem
|
||||||
if (isset($package['versions'][VersionParser::DEV_MASTER_ALIAS]) && isset($package['versions']['dev-master'])) {
|
if (isset($package['versions'][VersionParser::DEV_MASTER_ALIAS]) && isset($package['versions']['dev-master'])) {
|
||||||
unset($package['versions'][VersionParser::DEV_MASTER_ALIAS]);
|
unset($package['versions'][VersionParser::DEV_MASTER_ALIAS]);
|
||||||
}
|
}
|
||||||
if (!$isVerbose && count($package['versions']) > 4) {
|
|
||||||
uksort($package['versions'], 'version_compare');
|
uksort($package['versions'], 'version_compare');
|
||||||
$filtered = array();
|
|
||||||
$byMajor = array();
|
|
||||||
foreach ($package['versions'] as $version => $pretty) {
|
|
||||||
$byMajor[preg_replace('{^(\d+)\..*}', '$1', $version)][] = $pretty;
|
|
||||||
}
|
|
||||||
foreach ($byMajor as $versions) {
|
|
||||||
if (count($versions) > 4) {
|
|
||||||
$filtered[] = $versions[0];
|
|
||||||
$filtered[] = '...';
|
|
||||||
$filtered[] = $versions[count($versions) - 1];
|
|
||||||
} else {
|
|
||||||
$filtered = array_merge($filtered, $versions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$package['versions'] = $filtered;
|
if (!$isVerbose) {
|
||||||
|
$package['versions'] = self::condenseVersionList($package['versions'], 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
$prepared[$name] = $package['name'].'['.implode(', ', $package['versions']).']';
|
$prepared[$name] = $package['name'].'['.implode(', ', $package['versions']).']';
|
||||||
}
|
}
|
||||||
|
|
||||||
return implode(', ', $prepared);
|
return implode(', ', $prepared);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $versions an array of pretty versions, with normalized versions as keys
|
||||||
|
* @return list<string> a list of pretty versions and '...' where versions were removed
|
||||||
|
*/
|
||||||
|
private static function condenseVersionList(array $versions, $max)
|
||||||
|
{
|
||||||
|
if (count($versions) <= $max) {
|
||||||
|
return $versions;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filtered = array();
|
||||||
|
$byMajor = array();
|
||||||
|
foreach ($versions as $version => $pretty) {
|
||||||
|
$byMajor[preg_replace('{^(\d+)\..*}', '$1', $version)][] = $pretty;
|
||||||
|
}
|
||||||
|
foreach ($byMajor as $versionsForMajor) {
|
||||||
|
if (count($versionsForMajor) > $max) {
|
||||||
|
$filtered[] = $versionsForMajor[0];
|
||||||
|
$filtered[] = '...';
|
||||||
|
$filtered[] = $versionsForMajor[count($versionsForMajor) - 1];
|
||||||
|
} else {
|
||||||
|
$filtered = array_merge($filtered, $versionsForMajor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filtered;
|
||||||
|
}
|
||||||
|
|
||||||
private static function hasMultipleNames(array $packages)
|
private static function hasMultipleNames(array $packages)
|
||||||
{
|
{
|
||||||
$name = null;
|
$name = null;
|
||||||
|
|
|
@ -47,7 +47,7 @@ Your requirements could not be resolved to an installable set of packages.
|
||||||
Problem 1
|
Problem 1
|
||||||
- Root composer.json requires b/b *@dev -> satisfiable by b/b[dev-master].
|
- Root composer.json requires b/b *@dev -> satisfiable by b/b[dev-master].
|
||||||
- a/a dev-master requires d/d 1.0.0 -> satisfiable by d/d[1.0.0].
|
- a/a dev-master requires d/d 1.0.0 -> satisfiable by d/d[1.0.0].
|
||||||
- You can only install one version of a package, so only one of these can be installed: d/d[2.0.0, 1.0.0].
|
- You can only install one version of a package, so only one of these can be installed: d/d[1.0.0, 2.0.0].
|
||||||
- Conclusion: install d/d 2.0.0, learned rules:
|
- Conclusion: install d/d 2.0.0, learned rules:
|
||||||
- Root composer.json requires b/b *@dev -> satisfiable by b/b[dev-master].
|
- Root composer.json requires b/b *@dev -> satisfiable by b/b[dev-master].
|
||||||
- b/b dev-master requires d/d 2.0.0 -> satisfiable by d/d[2.0.0].
|
- b/b dev-master requires d/d 2.0.0 -> satisfiable by d/d[2.0.0].
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
--TEST--
|
||||||
|
Test the error output of solver problems is deduplicated.
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "name": "package/a", "version": "2.0.0", "require": { "missing/dep": "^1.0" } },
|
||||||
|
{ "name": "package/a", "version": "2.0.1", "require": { "missing/dep": "^1.0" } },
|
||||||
|
{ "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": "missing/dep", "version": "2.0.0" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"package/a": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--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
|
||||||
|
- 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.
|
||||||
|
- Root composer.json requires package/a * -> satisfiable by package/a[2.0.0, ..., 2.6.0].
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
|
|
@ -39,14 +39,14 @@ Your requirements could not be resolved to an installable set of packages.
|
||||||
Problem 1
|
Problem 1
|
||||||
- Conclusion: don't install regular/pkg 1.0.3, learned rules:
|
- Conclusion: don't install regular/pkg 1.0.3, learned rules:
|
||||||
- Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.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].
|
||||||
- Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
|
- 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.
|
||||||
- Conclusion: don't install regular/pkg 1.0.2, learned rules:
|
- Conclusion: don't install regular/pkg 1.0.2, learned rules:
|
||||||
- Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.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].
|
||||||
- Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
|
- 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.
|
||||||
- Conclusion: don't install regular/pkg 1.0.1, learned rules:
|
- Conclusion: don't install regular/pkg 1.0.1, learned rules:
|
||||||
- Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.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].
|
||||||
- Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
|
- 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.
|
||||||
- Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
|
- 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 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].
|
- Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue