1
0
Fork 0

Fix issue loading aliases and fix markPackageNameForLoading when called twice in a row for same package it would overwrite the constraint the second time

pull/8850/head
Jordi Boggiano 2020-06-06 17:16:54 +02:00
parent b7f1550896
commit 4b9b499ce5
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
5 changed files with 175 additions and 33 deletions

View File

@ -85,6 +85,8 @@ class PoolBuilder
*/ */
private $updateAllowWarned = array(); private $updateAllowWarned = array();
private $indexCounter = 0;
/** /**
* @param int[] $acceptableStabilities array of stability => BasePackage::STABILITY_* value * @param int[] $acceptableStabilities array of stability => BasePackage::STABILITY_* value
* @psalm-param array<string, int> $acceptableStabilities * @psalm-param array<string, int> $acceptableStabilities
@ -154,7 +156,7 @@ class PoolBuilder
continue; continue;
} }
$this->markPackageNameForLoading($packageName, $constraint); $this->markPackageNameForLoading($request, $packageName, $constraint);
} }
// clean up packagesToLoad for anything we manually marked loaded above // clean up packagesToLoad for anything we manually marked loaded above
@ -216,19 +218,28 @@ class PoolBuilder
$this->loadedPackages = array(); $this->loadedPackages = array();
$this->packages = array(); $this->packages = array();
$this->unacceptableFixedPackages = array(); $this->unacceptableFixedPackages = array();
$this->indexCounter = 0;
return $pool; return $pool;
} }
private function markPackageNameForLoading($name, ConstraintInterface $constraint) private function markPackageNameForLoading(Request $request, $name, ConstraintInterface $constraint)
{ {
// Maybe it was already marked before but not loaded yet. In that case // Maybe it was already marked before but not loaded yet. In that case
// we have to extend the constraint (we don't check if they match because // we have to extend the constraint (we don't check if they match because
// MultiConstraint::create() will optimize anyway) // MultiConstraint::create() will optimize anyway)
if (isset($this->packagesToLoad[$name]) && !Intervals::isSubsetOf($constraint, $this->packagesToLoad[$name])) { if (isset($this->packagesToLoad[$name])) {
// Already marked for loading and this does not expand the constraint to be loaded, nothing to do
if (Intervals::isSubsetOf($constraint, $this->packagesToLoad[$name])) {
return;
}
// extend the constraint to be loaded
$constraint = MultiConstraint::create(array($this->packagesToLoad[$name], $constraint), false); $constraint = MultiConstraint::create(array($this->packagesToLoad[$name], $constraint), false);
} }
// Not yet loaded or already marked for a reload, override the existing constraint
// (either it's a new one to load, or it has already been extended above)
if (!isset($this->loadedPackages[$name])) { if (!isset($this->loadedPackages[$name])) {
$this->packagesToLoad[$name] = $constraint; $this->packagesToLoad[$name] = $constraint;
return; return;
@ -245,18 +256,18 @@ class PoolBuilder
// yet so we get the required package versions // yet so we get the required package versions
$this->packagesToLoad[$name] = MultiConstraint::create(array($this->loadedPackages[$name], $constraint), false); $this->packagesToLoad[$name] = MultiConstraint::create(array($this->loadedPackages[$name], $constraint), false);
unset($this->loadedPackages[$name]); unset($this->loadedPackages[$name]);
// remove all already-loaded packages matching those to be loaded to avoid duplicates
foreach ($this->packages as $index => $pkg) {
if ($pkg->getName() === $name) {
unset($this->packages[$index]);
}
}
} }
private function loadPackagesMarkedForLoading(Request $request, $repositories) private function loadPackagesMarkedForLoading(Request $request, $repositories)
{ {
foreach ($this->packagesToLoad as $name => $constraint) { foreach ($this->packagesToLoad as $name => $constraint) {
// remove all already-loaded packages matching those to be loaded to avoid duplicates
foreach ($this->packages as $index => $pkg) {
if ($pkg->getName() === $name) {
$this->removeLoadedPackage($request, $pkg, $index);
}
}
$this->loadedPackages[$name] = $constraint; $this->loadedPackages[$name] = $constraint;
} }
@ -287,9 +298,8 @@ class PoolBuilder
private function loadPackage(Request $request, PackageInterface $package, $propagateUpdate = true) private function loadPackage(Request $request, PackageInterface $package, $propagateUpdate = true)
{ {
end($this->packages); $index = $this->indexCounter++;
$index = key($this->packages) + 1; $this->packages[$index] = $package;
$this->packages[] = $package;
if ($package instanceof AliasPackage) { if ($package instanceof AliasPackage) {
$this->aliasMap[spl_object_hash($package->getAliasOf())][$index] = $package; $this->aliasMap[spl_object_hash($package->getAliasOf())][$index] = $package;
@ -319,8 +329,9 @@ class PoolBuilder
$aliasPackage = new AliasPackage($basePackage, $alias['alias_normalized'], $alias['alias']); $aliasPackage = new AliasPackage($basePackage, $alias['alias_normalized'], $alias['alias']);
$aliasPackage->setRootPackageAlias(true); $aliasPackage->setRootPackageAlias(true);
$this->packages[] = $aliasPackage; $newIndex = $this->indexCounter++;
$this->aliasMap[spl_object_hash($aliasPackage->getAliasOf())][$index+1] = $aliasPackage; $this->packages[$newIndex] = $aliasPackage;
$this->aliasMap[spl_object_hash($aliasPackage->getAliasOf())][$newIndex] = $aliasPackage;
} }
foreach ($package->getRequires() as $link) { foreach ($package->getRequires() as $link) {
@ -333,19 +344,19 @@ class PoolBuilder
if ($request->getUpdateAllowTransitiveDependencies() && isset($this->skippedLoad[$require])) { if ($request->getUpdateAllowTransitiveDependencies() && isset($this->skippedLoad[$require])) {
if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$require])) { if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$require])) {
$this->unfixPackage($request, $require); $this->unfixPackage($request, $require);
$this->markPackageNameForLoading($require, $linkConstraint); $this->markPackageNameForLoading($request, $require, $linkConstraint);
} elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $require) && !isset($this->updateAllowWarned[$require])) { } elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $require) && !isset($this->updateAllowWarned[$require])) {
$this->updateAllowWarned[$require] = true; $this->updateAllowWarned[$require] = true;
$this->io->writeError('<warning>Dependency "'.$require.'" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies to include root dependencies.</warning>'); $this->io->writeError('<warning>Dependency "'.$require.'" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies to include root dependencies.</warning>');
} }
} else { } else {
$this->markPackageNameForLoading($require, $linkConstraint); $this->markPackageNameForLoading($request, $require, $linkConstraint);
} }
} else { } else {
// We also need to load the requirements of a fixed package // We also need to load the requirements of a fixed package
// unless it was skipped // unless it was skipped
if (!isset($this->skippedLoad[$require])) { if (!isset($this->skippedLoad[$require])) {
$this->markPackageNameForLoading($require, $linkConstraint); $this->markPackageNameForLoading($request, $require, $linkConstraint);
} }
} }
} }
@ -358,7 +369,7 @@ class PoolBuilder
if (isset($this->loadedPackages[$replace]) && isset($this->skippedLoad[$replace])) { if (isset($this->loadedPackages[$replace]) && isset($this->skippedLoad[$replace])) {
if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$replace])) { if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$replace])) {
$this->unfixPackage($request, $replace); $this->unfixPackage($request, $replace);
$this->markPackageNameForLoading($replace, $link->getConstraint()); $this->markPackageNameForLoading($request, $replace, $link->getConstraint());
} elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $replace) && !isset($this->updateAllowWarned[$replace])) { } elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $replace) && !isset($this->updateAllowWarned[$replace])) {
$this->updateAllowWarned[$replace] = true; $this->updateAllowWarned[$replace] = true;
$this->io->writeError('<warning>Dependency "'.$replace.'" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies to include root dependencies.</warning>'); $this->io->writeError('<warning>Dependency "'.$replace.'" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies to include root dependencies.</warning>');
@ -430,14 +441,7 @@ class PoolBuilder
if (!($lockedPackage instanceof AliasPackage) && $lockedPackage->getName() === $name) { if (!($lockedPackage instanceof AliasPackage) && $lockedPackage->getName() === $name) {
if (false !== $index = array_search($lockedPackage, $this->packages, true)) { if (false !== $index = array_search($lockedPackage, $this->packages, true)) {
$request->unfixPackage($lockedPackage); $request->unfixPackage($lockedPackage);
unset($this->packages[$index]); $this->removeLoadedPackage($request, $lockedPackage, $index);
if (isset($this->aliasMap[spl_object_hash($lockedPackage)])) {
foreach ($this->aliasMap[spl_object_hash($lockedPackage)] as $aliasIndex => $aliasPackage) {
$request->unfixPackage($aliasPackage);
unset($this->packages[$aliasIndex]);
}
unset($this->aliasMap[spl_object_hash($lockedPackage)]);
}
} }
} }
} }
@ -455,6 +459,18 @@ class PoolBuilder
unset($this->loadedPackages[$name]); unset($this->loadedPackages[$name]);
} }
private function removeLoadedPackage(Request $request, PackageInterface $package, $index)
{
unset($this->packages[$index]);
if (isset($this->aliasMap[spl_object_hash($package)])) {
foreach ($this->aliasMap[spl_object_hash($package)] as $aliasIndex => $aliasPackage) {
$request->unfixPackage($aliasPackage);
unset($this->packages[$aliasIndex]);
}
unset($this->aliasMap[spl_object_hash($package)]);
}
}
private function getRootAliasesPerPackage(array $aliases) private function getRootAliasesPerPackage(array $aliases)
{ {
$normalizedAliases = array(); $normalizedAliases = array();

View File

@ -0,0 +1,61 @@
--TEST--
Check root aliases are loaded
--ROOT--
{
"minimum-stability": "dev",
"aliases": [
{
"package": "req/pkg",
"version": "dev-feature-foo",
"alias": "dev-master"
}
]
}
--REQUEST--
{
"require": {
"package/a": "dev-master",
"req/pkg": "dev-feature-foo"
}
}
--FIXED--
[
]
--PACKAGES--
[
{
"name": "req/pkg", "version": "dev-feature-foo",
"source": { "reference": "feat.f", "type": "git", "url": "" }
},
{
"name": "req/pkg", "version": "dev-master",
"extra": { "branch-alias": { "dev-master": "1.0.x-dev" } },
"source": { "reference": "forked", "type": "git", "url": "" }
},
{
"name": "req/pkg", "version": "dev-master",
"extra": { "branch-alias": { "dev-master": "1.0.x-dev" } },
"source": { "reference": "master", "type": "git", "url": "" }
},
{
"name": "package/a", "version": "dev-master",
"require": { "req/pkg": "dev-master" }
}
]
--EXPECT--
[
"package/a-dev-master",
"package/a-9999999-dev (alias of dev-master)",
"req/pkg-dev-feature-foo#feat.f",
"req/pkg-dev-master#feat.f (alias of dev-feature-foo)",
"req/pkg-dev-master#forked",
"req/pkg-dev-master#master",
"req/pkg-1.0.9999999.9999999-dev#forked (alias of dev-master)",
"req/pkg-1.0.9999999.9999999-dev#master (alias of dev-master)"
]

View File

@ -0,0 +1,57 @@
--TEST--
Check root aliases get selected correctly
--ROOT--
{
"stability-flags": {
"a/aliased": "dev"
},
"aliases": [
{
"package": "a/aliased",
"version": "dev-master",
"alias": "1.0.0"
}
],
"references": {
"a/aliased": "abcd"
}
}
--REQUEST--
{
"require": {
"a/aliased": "dev-master",
"b/requirer": "*"
}
}
--FIXED--
[
]
--PACKAGES--
[
{
"name": "a/aliased", "version": "dev-master",
"source": { "reference": "orig", "type": "git", "url": "" }
},
{
"name": "a/aliased", "version": "1.0"
},
{
"name": "b/requirer", "version": "1.0.0",
"require": { "a/aliased": "1.0.0" },
"source": { "reference": "1.0.0", "type": "git", "url": "" }
}
]
--EXPECT--
[
"b/requirer-1.0.0.0#1.0.0",
"a/aliased-dev-master#abcd",
"a/aliased-1.0.0.0#abcd (alias of dev-master)",
"a/aliased-1.0.0.0#abcd",
"a/aliased-9999999-dev#abcd (alias of dev-master)"
]

View File

@ -42,5 +42,5 @@ Stability flags apply
4, 4,
5, 5,
6, 6,
"default/pkg-1.2.0.0 alias of 6" "default/pkg-1.2.0.0 (alias of 6)"
] ]

View File

@ -41,6 +41,7 @@ class PoolBuilderTest extends TestCase
$rootAliases = !empty($root['aliases']) ? $root['aliases'] : array(); $rootAliases = !empty($root['aliases']) ? $root['aliases'] : array();
$minimumStability = !empty($root['minimum-stability']) ? $root['minimum-stability'] : 'stable'; $minimumStability = !empty($root['minimum-stability']) ? $root['minimum-stability'] : 'stable';
$stabilityFlags = !empty($root['stability-flags']) ? $root['stability-flags'] : array(); $stabilityFlags = !empty($root['stability-flags']) ? $root['stability-flags'] : array();
$rootReferences = !empty($root['references']) ? $root['references'] : array();
$stabilityFlags = array_map(function ($stability) { $stabilityFlags = array_map(function ($stability) {
return BasePackage::$stabilities[$stability]; return BasePackage::$stabilities[$stability];
}, $stabilityFlags); }, $stabilityFlags);
@ -71,7 +72,7 @@ class PoolBuilderTest extends TestCase
return $pkg; return $pkg;
}; };
$repositorySet = new RepositorySet($minimumStability, $stabilityFlags, $rootAliases); $repositorySet = new RepositorySet($minimumStability, $stabilityFlags, $rootAliases, $rootReferences);
$repositorySet->addRepository($repo = new ArrayRepository()); $repositorySet->addRepository($repo = new ArrayRepository());
$repositorySet->addRepository($lockedRepo = new LockArrayRepository()); $repositorySet->addRepository($lockedRepo = new LockArrayRepository());
foreach ($packages as $package) { foreach ($packages as $package) {
@ -114,15 +115,22 @@ class PoolBuilderTest extends TestCase
} }
$suffix = ''; $suffix = '';
if ($package->getSourceReference()) {
$suffix = '#'.$package->getSourceReference();
}
if ($package->getRepository() instanceof LockArrayRepository) { if ($package->getRepository() instanceof LockArrayRepository) {
$suffix = ' (locked)'; $suffix .= ' (locked)';
} }
if ($package instanceof AliasPackage && $id = array_search($package->getAliasOf(), $packageIds, true)) { if ($package instanceof AliasPackage) {
return (string) $package->getName().'-'.$package->getVersion() .' alias of '.$id . $suffix; if ($id = array_search($package->getAliasOf(), $packageIds, true)) {
return (string) $package->getName().'-'.$package->getVersion() . $suffix . ' (alias of '.$id . ')';
}
return (string) $package->getName().'-'.$package->getVersion() . $suffix . ' (alias of '.$package->getAliasOf()->getVersion().')';
} }
return (string) $package . $suffix; return (string) $package->getName().'-'.$package->getVersion() . $suffix;
}, $result); }, $result);
$this->assertSame($expect, $result); $this->assertSame($expect, $result);