Always install aliases together with their original package
Restores some Composer 1.x behavior like unbound constraints conflicting with default branches unless they are branch aliased. Simplifies conflicts with aliases because packages cannot be installed without their aliases, so we do not need to know which aliases are uninstalled in lock file or installed.json.pull/9477/head
parent
58f358d028
commit
7197278fe9
|
@ -35,7 +35,7 @@ abstract class Rule
|
||||||
const RULE_PACKAGE_SAME_NAME = 10;
|
const RULE_PACKAGE_SAME_NAME = 10;
|
||||||
const RULE_LEARNED = 12;
|
const RULE_LEARNED = 12;
|
||||||
const RULE_PACKAGE_ALIAS = 13;
|
const RULE_PACKAGE_ALIAS = 13;
|
||||||
const RULE_PACKAGE_ROOT_ALIAS = 14;
|
const RULE_PACKAGE_INVERSE_ALIAS = 14;
|
||||||
|
|
||||||
// bitfield defs
|
// bitfield defs
|
||||||
const BITFIELD_TYPE = 0;
|
const BITFIELD_TYPE = 0;
|
||||||
|
@ -313,22 +313,26 @@ abstract class Rule
|
||||||
|
|
||||||
return 'Conclusion: '.$ruleText.$learnedString;
|
return 'Conclusion: '.$ruleText.$learnedString;
|
||||||
case self::RULE_PACKAGE_ALIAS:
|
case self::RULE_PACKAGE_ALIAS:
|
||||||
case self::RULE_PACKAGE_ROOT_ALIAS:
|
$aliasPackage = $pool->literalToPackage($literals[0]);
|
||||||
if ($this->getReason() === self::RULE_PACKAGE_ALIAS) {
|
|
||||||
$aliasPackage = $pool->literalToPackage($literals[0]);
|
|
||||||
$otherLiteral = 1;
|
|
||||||
} else {
|
|
||||||
// root alias rules work the other way around
|
|
||||||
$aliasPackage = $pool->literalToPackage($literals[1]);
|
|
||||||
$otherLiteral = 0;
|
|
||||||
}
|
|
||||||
// avoid returning content like "9999999-dev is an alias of dev-master" as it is useless
|
// avoid returning content like "9999999-dev is an alias of dev-master" as it is useless
|
||||||
if ($aliasPackage->getVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) {
|
if ($aliasPackage->getVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
$package = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[$otherLiteral]));
|
$package = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[1]));
|
||||||
|
|
||||||
return $aliasPackage->getPrettyString() .' is an alias of '.$package->getPrettyString().' and thus requires it to be installed too.';
|
return $aliasPackage->getPrettyString() .' is an alias of '.$package->getPrettyString().' and thus requires it to be installed too.';
|
||||||
|
case self::RULE_PACKAGE_INVERSE_ALIAS:
|
||||||
|
// inverse alias rules work the other way around than above
|
||||||
|
$aliasPackage = $pool->literalToPackage($literals[1]);
|
||||||
|
|
||||||
|
// avoid returning content like "9999999-dev is an alias of dev-master" as it is useless
|
||||||
|
if ($aliasPackage->getVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$package = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[0]));
|
||||||
|
|
||||||
|
return $aliasPackage->getPrettyString() .' is an alias of '.$package->getPrettyString().' and must be installed with it.';
|
||||||
default:
|
default:
|
||||||
$ruleText = '';
|
$ruleText = '';
|
||||||
foreach ($literals as $i => $literal) {
|
foreach ($literals as $i => $literal) {
|
||||||
|
|
|
@ -166,10 +166,8 @@ class RuleSetGenerator
|
||||||
$workQueue->enqueue($package->getAliasOf());
|
$workQueue->enqueue($package->getAliasOf());
|
||||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($package->getAliasOf()), Rule::RULE_PACKAGE_ALIAS, $package));
|
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($package->getAliasOf()), Rule::RULE_PACKAGE_ALIAS, $package));
|
||||||
|
|
||||||
// root aliases must be installed with their main package, so create a rule the other way around as well
|
// aliases must be installed with their main package, so create a rule the other way around as well
|
||||||
if ($package->isRootPackageAlias()) {
|
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package->getAliasOf(), array($package), Rule::RULE_PACKAGE_INVERSE_ALIAS, $package->getAliasOf()));
|
||||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package->getAliasOf(), array($package), Rule::RULE_PACKAGE_ROOT_ALIAS, $package->getAliasOf()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// if alias package has no self.version requires, its requirements do not
|
// if alias package has no self.version requires, its requirements do not
|
||||||
// need to be added as the aliased package processing will take care of it
|
// need to be added as the aliased package processing will take care of it
|
||||||
|
@ -199,7 +197,7 @@ class RuleSetGenerator
|
||||||
/** @var PackageInterface $package */
|
/** @var PackageInterface $package */
|
||||||
foreach ($this->addedMap as $package) {
|
foreach ($this->addedMap as $package) {
|
||||||
foreach ($package->getConflicts() as $link) {
|
foreach ($package->getConflicts() as $link) {
|
||||||
// even if conlict ends up being with an alias, there would be a conflict with at least one actual package by this name
|
// even if conlict ends up being with an alias, there would be at least one actual package by this name
|
||||||
if (!isset($this->addedPackagesByNames[$link->getTarget()])) {
|
if (!isset($this->addedPackagesByNames[$link->getTarget()])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -273,9 +271,13 @@ class RuleSetGenerator
|
||||||
protected function addRulesForRootAliases($ignorePlatformReqs)
|
protected function addRulesForRootAliases($ignorePlatformReqs)
|
||||||
{
|
{
|
||||||
foreach ($this->pool->getPackages() as $package) {
|
foreach ($this->pool->getPackages() as $package) {
|
||||||
// ensure that rules for root alias packages get loaded even if the root alias itself isn't required
|
// ensure that rules for root alias packages and aliases of packages which were loaded are also loaded
|
||||||
// otherwise a package could be installed without its root alias which leads to unexpected behavior
|
// even if the alias itself isn't required, otherwise a package could be installed without its alias which
|
||||||
if ($package instanceof AliasPackage && $package->isRootPackageAlias()) {
|
// leads to unexpected behavior
|
||||||
|
if (!isset($this->addedMap[$package->id]) &&
|
||||||
|
$package instanceof AliasPackage &&
|
||||||
|
($package->isRootPackageAlias() || isset($this->addedMap[$package->getAliasOf()->id]))
|
||||||
|
) {
|
||||||
$this->addRulesForPackage($package, $ignorePlatformReqs);
|
$this->addRulesForPackage($package, $ignorePlatformReqs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -842,6 +842,7 @@ class SolverTest extends TestCase
|
||||||
array('job' => 'install', 'package' => $packageB),
|
array('job' => 'install', 'package' => $packageB),
|
||||||
array('job' => 'markAliasInstalled', 'package' => $packageBAlias),
|
array('job' => 'markAliasInstalled', 'package' => $packageBAlias),
|
||||||
array('job' => 'install', 'package' => $packageC),
|
array('job' => 'install', 'package' => $packageC),
|
||||||
|
array('job' => 'markAliasInstalled', 'package' => $packageCAlias),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
--TEST--
|
|
||||||
Test that root package conflict on a branch alias is ignored if the alias is not required for installation.
|
|
||||||
--COMPOSER--
|
|
||||||
{
|
|
||||||
"repositories": [
|
|
||||||
{
|
|
||||||
"type": "package",
|
|
||||||
"package": [
|
|
||||||
{ "name": "some/dep", "version": "1.0.0" },
|
|
||||||
{ "name": "some/dep", "version": "1.1.0" },
|
|
||||||
{ "name": "some/dep", "version": "1.2.0" },
|
|
||||||
{ "name": "some/dep", "version": "dev-main", "extra": {"branch-alias": {"dev-main": "1.3.x-dev"} } },
|
|
||||||
{ "name": "some/dep", "version": "1.2.x-dev" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"require": {
|
|
||||||
"some/dep": "dev-main"
|
|
||||||
},
|
|
||||||
"conflict": {
|
|
||||||
"some/dep": ">=1.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
--RUN--
|
|
||||||
update
|
|
||||||
--EXPECT-LOCK--
|
|
||||||
{
|
|
||||||
"packages": [
|
|
||||||
{ "name": "some/dep", "version": "dev-main", "type": "library", "extra": {"branch-alias": {"dev-main": "1.3.x-dev"} } }
|
|
||||||
],
|
|
||||||
"packages-dev": [],
|
|
||||||
"aliases": [],
|
|
||||||
"minimum-stability": "stable",
|
|
||||||
"stability-flags": {
|
|
||||||
"some/dep": 20
|
|
||||||
},
|
|
||||||
"prefer-stable": false,
|
|
||||||
"prefer-lowest": false,
|
|
||||||
"platform": [],
|
|
||||||
"platform-dev": []
|
|
||||||
}
|
|
||||||
--EXPECT--
|
|
||||||
Installing some/dep (dev-main)
|
|
||||||
Marking some/dep (1.3.x-dev) as installed, alias of some/dep (dev-main)
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
--TEST--
|
||||||
|
Test that a root package conflict with a branch alias leads to an error, even if the branch alias isn't required.
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "name": "some/dep", "version": "1.0.0" },
|
||||||
|
{ "name": "some/dep", "version": "1.1.0" },
|
||||||
|
{ "name": "some/dep", "version": "1.2.0" },
|
||||||
|
{ "name": "some/dep", "version": "dev-main", "extra": {"branch-alias": {"dev-main": "1.3.x-dev"} } },
|
||||||
|
{ "name": "some/dep", "version": "1.2.x-dev" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"some/dep": "dev-main"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"some/dep": ">=1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--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.0.0+no-version-set and cannot be modified by Composer
|
||||||
|
- some/dep 1.3.x-dev is an alias of some/dep dev-main and must be installed with it.
|
||||||
|
- __root__ 1.0.0+no-version-set conflicts with some/dep 1.3.x-dev.
|
||||||
|
- Root composer.json requires some/dep dev-main -> satisfiable by some/dep[dev-main].
|
||||||
|
--EXPECT--
|
|
@ -1,5 +1,5 @@
|
||||||
--TEST--
|
--TEST--
|
||||||
Test that conflict on a branch alias is ignored if the alias is not required for installation.
|
Test that conflict with a branch alias in the lock file leads to an error on install from lock, even if the branch alias was removed on the remote end.
|
||||||
--COMPOSER--
|
--COMPOSER--
|
||||||
{
|
{
|
||||||
"repositories": [
|
"repositories": [
|
||||||
|
@ -9,7 +9,7 @@ Test that conflict on a branch alias is ignored if the alias is not required for
|
||||||
{ "name": "some/dep", "version": "1.0.0" },
|
{ "name": "some/dep", "version": "1.0.0" },
|
||||||
{ "name": "some/dep", "version": "1.1.0" },
|
{ "name": "some/dep", "version": "1.1.0" },
|
||||||
{ "name": "some/dep", "version": "1.2.0" },
|
{ "name": "some/dep", "version": "1.2.0" },
|
||||||
{ "name": "some/dep", "version": "dev-main", "extra": {"branch-alias": {"dev-main": "1.3.x-dev"} } },
|
{ "name": "some/dep", "version": "dev-main" },
|
||||||
{ "name": "some/dep", "version": "1.2.x-dev" },
|
{ "name": "some/dep", "version": "1.2.x-dev" },
|
||||||
{ "name": "conflictor/foo", "version": "1.0.0", "conflict": { "some/dep": ">=1.3" } }
|
{ "name": "conflictor/foo", "version": "1.0.0", "conflict": { "some/dep": ">=1.3" } }
|
||||||
]
|
]
|
||||||
|
@ -39,7 +39,16 @@ Test that conflict on a branch alias is ignored if the alias is not required for
|
||||||
}
|
}
|
||||||
--RUN--
|
--RUN--
|
||||||
install
|
install
|
||||||
|
--EXPECT-EXIT-CODE--
|
||||||
|
2
|
||||||
|
--EXPECT-OUTPUT--
|
||||||
|
Installing dependencies from lock file (including require-dev)
|
||||||
|
Verifying lock file contents can be installed on current platform.
|
||||||
|
Your lock file does not contain a compatible set of packages. Please run composer update.
|
||||||
|
|
||||||
|
Problem 1
|
||||||
|
- conflictor/foo is locked to version 1.0.0 and an update of this package was not requested.
|
||||||
|
- conflictor/foo 1.0.0 conflicts with some/dep 1.3.x-dev.
|
||||||
|
- some/dep is locked to version 1.3.x-dev and an update of this package was not requested.
|
||||||
|
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
Installing conflictor/foo (1.0.0)
|
|
||||||
Installing some/dep (dev-main)
|
|
||||||
Marking some/dep (1.3.x-dev) as installed, alias of some/dep (dev-main)
|
|
|
@ -1,5 +1,5 @@
|
||||||
--TEST--
|
--TEST--
|
||||||
Test that conflict of a dependency with a branch alias of another dependency is ignored if the alias is not required for installation.
|
Test that conflict of a dependency with a branch alias of another dependency is not ignored, even if the alias is not required for installation.
|
||||||
--COMPOSER--
|
--COMPOSER--
|
||||||
{
|
{
|
||||||
"repositories": [
|
"repositories": [
|
||||||
|
@ -22,24 +22,17 @@ Test that conflict of a dependency with a branch alias of another dependency is
|
||||||
}
|
}
|
||||||
--RUN--
|
--RUN--
|
||||||
update
|
update
|
||||||
--EXPECT-LOCK--
|
--EXPECT-EXIT-CODE--
|
||||||
{
|
2
|
||||||
"packages": [
|
--EXPECT-OUTPUT--
|
||||||
{ "name": "conflictor/foo", "version": "1.0.0", "conflict": { "some/dep": ">=1.3" }, "type": "library" },
|
Loading composer repositories with package information
|
||||||
{ "name": "some/dep", "version": "dev-main", "extra": {"branch-alias": {"dev-main": "1.3.x-dev"} }, "type": "library" }
|
Updating dependencies
|
||||||
],
|
Your requirements could not be resolved to an installable set of packages.
|
||||||
"packages-dev": [],
|
|
||||||
"aliases": [],
|
Problem 1
|
||||||
"minimum-stability": "stable",
|
- Root composer.json requires some/dep dev-main -> satisfiable by some/dep[dev-main].
|
||||||
"stability-flags": {
|
- conflictor/foo 1.0.0 conflicts with some/dep 1.3.x-dev.
|
||||||
"some/dep": 20
|
- some/dep 1.3.x-dev is an alias of some/dep dev-main and must be installed with it.
|
||||||
},
|
- Root composer.json requires conflictor/foo 1.0.0 -> satisfiable by conflictor/foo[1.0.0].
|
||||||
"prefer-stable": false,
|
|
||||||
"prefer-lowest": false,
|
|
||||||
"platform": [],
|
|
||||||
"platform-dev": []
|
|
||||||
}
|
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
Installing conflictor/foo (1.0.0)
|
|
||||||
Installing some/dep (dev-main)
|
|
||||||
Marking some/dep (1.3.x-dev) as installed, alias of some/dep (dev-main)
|
|
|
@ -1,5 +1,5 @@
|
||||||
--TEST--
|
--TEST--
|
||||||
Test that a conflict against >=5 does not include dev-master or other dev-x
|
Test that a conflict against >=5 does not include the default branch if it has a branch alias defined.
|
||||||
--COMPOSER--
|
--COMPOSER--
|
||||||
{
|
{
|
||||||
"repositories": [
|
"repositories": [
|
||||||
|
@ -7,7 +7,7 @@ Test that a conflict against >=5 does not include dev-master or other dev-x
|
||||||
"type": "package",
|
"type": "package",
|
||||||
"package": [
|
"package": [
|
||||||
{ "name": "conflicter/pkg", "version": "1.0.0", "conflict": { "victim/pkg": ">=5", "victim/pkg2": ">=5" } },
|
{ "name": "conflicter/pkg", "version": "1.0.0", "conflict": { "victim/pkg": ">=5", "victim/pkg2": ">=5" } },
|
||||||
{ "name": "victim/pkg", "version": "dev-master", "default-branch": true },
|
{ "name": "victim/pkg", "version": "dev-master", "default-branch": true, "extra": { "branch-alias": { "dev-master": "2.x-dev" } } },
|
||||||
{ "name": "victim/pkg2", "version": "dev-foo" }
|
{ "name": "victim/pkg2", "version": "dev-foo" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -27,5 +27,5 @@ update
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
Installing conflicter/pkg (1.0.0)
|
Installing conflicter/pkg (1.0.0)
|
||||||
Installing victim/pkg (dev-master)
|
Installing victim/pkg (dev-master)
|
||||||
Marking victim/pkg (9999999-dev) as installed, alias of victim/pkg (dev-master)
|
Marking victim/pkg (2.x-dev) as installed, alias of victim/pkg (dev-master)
|
||||||
Installing victim/pkg2 (dev-foo)
|
Installing victim/pkg2 (dev-foo)
|
|
@ -0,0 +1,39 @@
|
||||||
|
--TEST--
|
||||||
|
Test that a conflict against >=5 includes the default branch if it has a branch alias defined.
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "name": "conflicter/pkg", "version": "1.0.0", "conflict": { "victim/pkg": ">=5", "victim/pkg2": ">=5" } },
|
||||||
|
{ "name": "victim/pkg", "version": "dev-master", "default-branch": true },
|
||||||
|
{ "name": "victim/pkg2", "version": "dev-foo" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"conflicter/pkg": "1.0.0",
|
||||||
|
"victim/pkg": "*",
|
||||||
|
"victim/pkg2": "*"
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--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
|
||||||
|
- conflicter/pkg 1.0.0 conflicts with victim/pkg dev-master.
|
||||||
|
- Root composer.json requires conflicter/pkg 1.0.0 -> satisfiable by conflicter/pkg[1.0.0].
|
||||||
|
- Root composer.json requires victim/pkg * -> satisfiable by victim/pkg[dev-master].
|
||||||
|
|
||||||
|
--EXPECT--
|
Loading…
Reference in New Issue