Separate locked packages from fixed packages in request
Locked packages are basically like removable fixed packages, so we still only load one version, but we do not require their installation unless something the user needs requires their use. So they automatically get removed if they are no longer needed on any update.pull/9223/head
parent
73e24ea9fb
commit
74fb313c39
|
@ -31,13 +31,13 @@ class Pool implements \Countable
|
||||||
protected $packageByName = array();
|
protected $packageByName = array();
|
||||||
protected $versionParser;
|
protected $versionParser;
|
||||||
protected $providerCache = array();
|
protected $providerCache = array();
|
||||||
protected $unacceptableFixedPackages;
|
protected $unacceptableFixedOrLockedPackages;
|
||||||
|
|
||||||
public function __construct(array $packages = array(), array $unacceptableFixedPackages = array())
|
public function __construct(array $packages = array(), array $unacceptableFixedOrLockedPackages = array())
|
||||||
{
|
{
|
||||||
$this->versionParser = new VersionParser;
|
$this->versionParser = new VersionParser;
|
||||||
$this->setPackages($packages);
|
$this->setPackages($packages);
|
||||||
$this->unacceptableFixedPackages = $unacceptableFixedPackages;
|
$this->unacceptableFixedOrLockedPackages = $unacceptableFixedOrLockedPackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setPackages(array $packages)
|
private function setPackages(array $packages)
|
||||||
|
@ -181,9 +181,9 @@ class Pool implements \Countable
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isUnacceptableFixedPackage(PackageInterface $package)
|
public function isUnacceptableFixedOrLockedPackage(PackageInterface $package)
|
||||||
{
|
{
|
||||||
return \in_array($package, $this->unacceptableFixedPackages, true);
|
return \in_array($package, $this->unacceptableFixedOrLockedPackages, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __toString()
|
public function __toString()
|
||||||
|
|
|
@ -81,7 +81,7 @@ class PoolBuilder
|
||||||
/**
|
/**
|
||||||
* @psalm-var list<Package>
|
* @psalm-var list<Package>
|
||||||
*/
|
*/
|
||||||
private $unacceptableFixedPackages = array();
|
private $unacceptableFixedOrLockedPackages = array();
|
||||||
private $updateAllowList = array();
|
private $updateAllowList = array();
|
||||||
private $skippedLoad = array();
|
private $skippedLoad = array();
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ class PoolBuilder
|
||||||
* have already their maximum required range loaded and can not be
|
* have already their maximum required range loaded and can not be
|
||||||
* extended by markPackageNameForLoading
|
* extended by markPackageNameForLoading
|
||||||
*
|
*
|
||||||
* Packages get cleared from this list if they get unfixed as in that case
|
* Packages get cleared from this list if they get unlocked as in that case
|
||||||
* we need to actually load them
|
* we need to actually load them
|
||||||
*/
|
*/
|
||||||
private $maxExtendedReqs = array();
|
private $maxExtendedReqs = array();
|
||||||
|
@ -123,16 +123,14 @@ class PoolBuilder
|
||||||
|
|
||||||
public function buildPool(array $repositories, Request $request)
|
public function buildPool(array $repositories, Request $request)
|
||||||
{
|
{
|
||||||
$singleVersionPackages = $request->getFixedPackages();
|
|
||||||
|
|
||||||
if ($request->getUpdateAllowList()) {
|
if ($request->getUpdateAllowList()) {
|
||||||
$this->updateAllowList = $request->getUpdateAllowList();
|
$this->updateAllowList = $request->getUpdateAllowList();
|
||||||
$this->warnAboutNonMatchingUpdateAllowList($request);
|
$this->warnAboutNonMatchingUpdateAllowList($request);
|
||||||
|
|
||||||
foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) {
|
foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) {
|
||||||
if (!$this->isUpdateAllowed($lockedPackage)) {
|
if (!$this->isUpdateAllowed($lockedPackage)) {
|
||||||
//$request->fixPackage($lockedPackage);
|
$request->lockPackage($lockedPackage);
|
||||||
$singleVersionPackages[] = $lockedPackage;
|
$fixedOrLockedPackages[] = $lockedPackage;
|
||||||
$lockedName = $lockedPackage->getName();
|
$lockedName = $lockedPackage->getName();
|
||||||
// remember which packages we skipped loading remote content for in this partial update
|
// remember which packages we skipped loading remote content for in this partial update
|
||||||
$this->skippedLoad[$lockedName] = $lockedName;
|
$this->skippedLoad[$lockedName] = $lockedName;
|
||||||
|
@ -143,7 +141,7 @@ class PoolBuilder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($singleVersionPackages as $package) {
|
foreach ($request->getFixedOrLockedPackages() as $package) {
|
||||||
// using MatchAllConstraint here because fixed packages do not need to retrigger
|
// using MatchAllConstraint here because fixed packages do not need to retrigger
|
||||||
// loading any packages
|
// loading any packages
|
||||||
$this->loadedPackages[$package->getName()] = new MatchAllConstraint();
|
$this->loadedPackages[$package->getName()] = new MatchAllConstraint();
|
||||||
|
@ -163,12 +161,12 @@ class PoolBuilder
|
||||||
) {
|
) {
|
||||||
$this->loadPackage($request, $package, false);
|
$this->loadPackage($request, $package, false);
|
||||||
} else {
|
} else {
|
||||||
$this->unacceptableFixedPackages[] = $package;
|
$this->unacceptableFixedOrLockedPackages[] = $package;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($request->getRequires() as $packageName => $constraint) {
|
foreach ($request->getRequires() as $packageName => $constraint) {
|
||||||
// fixed packages have already been added, so if a root require needs one of them, no need to do anything
|
// fixed and locked packages have already been added, so if a root require needs one of them, no need to do anything
|
||||||
if (isset($this->loadedPackages[$packageName])) {
|
if (isset($this->loadedPackages[$packageName])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -222,21 +220,21 @@ class PoolBuilder
|
||||||
$this->rootAliases,
|
$this->rootAliases,
|
||||||
$this->rootReferences,
|
$this->rootReferences,
|
||||||
$this->packages,
|
$this->packages,
|
||||||
$this->unacceptableFixedPackages
|
$this->unacceptableFixedOrLockedPackages
|
||||||
);
|
);
|
||||||
$this->eventDispatcher->dispatch($prePoolCreateEvent->getName(), $prePoolCreateEvent);
|
$this->eventDispatcher->dispatch($prePoolCreateEvent->getName(), $prePoolCreateEvent);
|
||||||
$this->packages = $prePoolCreateEvent->getPackages();
|
$this->packages = $prePoolCreateEvent->getPackages();
|
||||||
$this->unacceptableFixedPackages = $prePoolCreateEvent->getUnacceptableFixedPackages();
|
$this->unacceptableFixedOrLockedPackages = $prePoolCreateEvent->getUnacceptableFixedPackages();
|
||||||
}
|
}
|
||||||
|
|
||||||
$pool = new Pool($this->packages, $this->unacceptableFixedPackages);
|
$pool = new Pool($this->packages, $this->unacceptableFixedOrLockedPackages);
|
||||||
|
|
||||||
$this->aliasMap = array();
|
$this->aliasMap = array();
|
||||||
$this->packagesToLoad = array();
|
$this->packagesToLoad = array();
|
||||||
$this->loadedPackages = array();
|
$this->loadedPackages = array();
|
||||||
$this->loadedPerRepo = array();
|
$this->loadedPerRepo = array();
|
||||||
$this->packages = array();
|
$this->packages = array();
|
||||||
$this->unacceptableFixedPackages = array();
|
$this->unacceptableFixedOrLockedPackages = array();
|
||||||
$this->maxExtendedReqs = array();
|
$this->maxExtendedReqs = array();
|
||||||
$this->skippedLoad = array();
|
$this->skippedLoad = array();
|
||||||
$this->indexCounter = 0;
|
$this->indexCounter = 0;
|
||||||
|
@ -253,7 +251,7 @@ class PoolBuilder
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root require (which was not unfixed) already loaded the maximum range so no
|
// Root require (which was not unlocked) already loaded the maximum range so no
|
||||||
// need to check anything here
|
// need to check anything here
|
||||||
if (isset($this->maxExtendedReqs[$name])) {
|
if (isset($this->maxExtendedReqs[$name])) {
|
||||||
return;
|
return;
|
||||||
|
@ -261,7 +259,7 @@ class PoolBuilder
|
||||||
|
|
||||||
// Root requires can not be overruled by dependencies so there is no point in
|
// Root requires can not be overruled by dependencies so there is no point in
|
||||||
// extending the loaded constraint for those.
|
// extending the loaded constraint for those.
|
||||||
// This is triggered when loading a root require which was fixed but got unfixed, then
|
// This is triggered when loading a root require which was locked but got unlocked, then
|
||||||
// we make sure that we load at most the intervals covered by the root constraint.
|
// we make sure that we load at most the intervals covered by the root constraint.
|
||||||
$rootRequires = $request->getRequires();
|
$rootRequires = $request->getRequires();
|
||||||
if (isset($rootRequires[$name]) && !Intervals::isSubsetOf($constraint, $rootRequires[$name])) {
|
if (isset($rootRequires[$name]) && !Intervals::isSubsetOf($constraint, $rootRequires[$name])) {
|
||||||
|
@ -314,7 +312,7 @@ class PoolBuilder
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// these repos have their packages fixed if they need to be loaded so we
|
// these repos have their packages fixed or locked if they need to be loaded so we
|
||||||
// never need to load anything else from them
|
// never need to load anything else from them
|
||||||
if ($repository instanceof PlatformRepository || $repository === $request->getLockedRepository()) {
|
if ($repository instanceof PlatformRepository || $repository === $request->getLockedRepository()) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -347,14 +345,14 @@ class PoolBuilder
|
||||||
// right version. It'd be more work to figure out which versions and which aliases of those versions this may
|
// right version. It'd be more work to figure out which versions and which aliases of those versions this may
|
||||||
// apply to
|
// apply to
|
||||||
if (isset($this->rootReferences[$name])) {
|
if (isset($this->rootReferences[$name])) {
|
||||||
// do not modify the references on already locked packages
|
// do not modify the references on already locked or fixed packages
|
||||||
if ($request->getLockedRepository() !== $package->getRepository() && !$request->isFixedPackage($package)) {
|
if (!$request->isLockedPackage($package) && !$request->isFixedPackage($package)) {
|
||||||
$package->setSourceDistReferences($this->rootReferences[$name]);
|
$package->setSourceDistReferences($this->rootReferences[$name]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if propogateUpdate is false we are loading a fixed package, root aliases do not apply as they are manually
|
// if propogateUpdate is false we are loading a fixed or locked package, root aliases do not apply as they are
|
||||||
// loaded as separate packages in this case
|
// manually loaded as separate packages in this case
|
||||||
if ($propagateUpdate && isset($this->rootAliases[$name][$package->getVersion()])) {
|
if ($propagateUpdate && isset($this->rootAliases[$name][$package->getVersion()])) {
|
||||||
$alias = $this->rootAliases[$name][$package->getVersion()];
|
$alias = $this->rootAliases[$name][$package->getVersion()];
|
||||||
if ($package instanceof AliasPackage) {
|
if ($package instanceof AliasPackage) {
|
||||||
|
@ -375,11 +373,11 @@ class PoolBuilder
|
||||||
$linkConstraint = $link->getConstraint();
|
$linkConstraint = $link->getConstraint();
|
||||||
|
|
||||||
if ($propagateUpdate) {
|
if ($propagateUpdate) {
|
||||||
// if this is a partial update with transitive dependencies we need to unfix the package we now know is a
|
// if this is a partial update with transitive dependencies we need to unlock the package we now know is a
|
||||||
// dependency of another package which we are trying to update, and then attempt to load it again
|
// dependency of another package which we are trying to update, and then attempt to load it again
|
||||||
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->unlockPackage($request, $require);
|
||||||
$this->markPackageNameForLoading($request, $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;
|
||||||
|
@ -389,7 +387,7 @@ class PoolBuilder
|
||||||
$this->markPackageNameForLoading($request, $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 locked package
|
||||||
// unless it was skipped
|
// unless it was skipped
|
||||||
if (!isset($this->skippedLoad[$require])) {
|
if (!isset($this->skippedLoad[$require])) {
|
||||||
$this->markPackageNameForLoading($request, $require, $linkConstraint);
|
$this->markPackageNameForLoading($request, $require, $linkConstraint);
|
||||||
|
@ -397,14 +395,14 @@ class PoolBuilder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're doing a partial update with deps we also need to unfix packages which are being replaced in case they
|
// if we're doing a partial update with deps we also need to unlock packages which are being replaced in case
|
||||||
// are currently locked and thus prevent this updateable package from being installable/updateable
|
// they are currently locked and thus prevent this updateable package from being installable/updateable
|
||||||
if ($propagateUpdate && $request->getUpdateAllowTransitiveDependencies()) {
|
if ($propagateUpdate && $request->getUpdateAllowTransitiveDependencies()) {
|
||||||
foreach ($package->getReplaces() as $link) {
|
foreach ($package->getReplaces() as $link) {
|
||||||
$replace = $link->getTarget();
|
$replace = $link->getTarget();
|
||||||
if (isset($this->loadedPackages[$replace], $this->skippedLoad[$replace])) {
|
if (isset($this->loadedPackages[$replace], $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->unlockPackage($request, $replace);
|
||||||
$this->markPackageNameForLoading($request, $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;
|
||||||
|
@ -467,16 +465,16 @@ class PoolBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverts the decision to use a fixed package from lock file if a partial update with transitive dependencies
|
* Reverts the decision to use a locked package if a partial update with transitive dependencies
|
||||||
* found that this package actually needs to be updated
|
* found that this package actually needs to be updated
|
||||||
*/
|
*/
|
||||||
private function unfixPackage(Request $request, $name)
|
private function unlockPackage(Request $request, $name)
|
||||||
{
|
{
|
||||||
// remove locked package by this name which was already initialized
|
// remove locked package by this name which was already initialized
|
||||||
foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) {
|
foreach ($request->getLockedPackages() as $lockedPackage) {
|
||||||
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->unlockPackage($lockedPackage);
|
||||||
$this->removeLoadedPackage($request, $lockedPackage, $index);
|
$this->removeLoadedPackage($request, $lockedPackage, $index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -488,7 +486,7 @@ class PoolBuilder
|
||||||
// as long as it was not unfixed yet
|
// as long as it was not unfixed yet
|
||||||
&& isset($this->skippedLoad[$this->skippedLoad[$name]])
|
&& isset($this->skippedLoad[$this->skippedLoad[$name]])
|
||||||
) {
|
) {
|
||||||
$this->unfixPackage($request, $this->skippedLoad[$name]);
|
$this->unlockPackage($request, $this->skippedLoad[$name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
unset($this->skippedLoad[$name], $this->loadedPackages[$name], $this->maxExtendedReqs[$name]);
|
unset($this->skippedLoad[$name], $this->loadedPackages[$name], $this->maxExtendedReqs[$name]);
|
||||||
|
@ -499,7 +497,7 @@ class PoolBuilder
|
||||||
unset($this->packages[$index]);
|
unset($this->packages[$index]);
|
||||||
if (isset($this->aliasMap[spl_object_hash($package)])) {
|
if (isset($this->aliasMap[spl_object_hash($package)])) {
|
||||||
foreach ($this->aliasMap[spl_object_hash($package)] as $aliasIndex => $aliasPackage) {
|
foreach ($this->aliasMap[spl_object_hash($package)] as $aliasIndex => $aliasPackage) {
|
||||||
//$request->unfixPackage($aliasPackage);
|
$request->unlockPackage($aliasPackage);
|
||||||
unset($this->packages[$aliasIndex]);
|
unset($this->packages[$aliasIndex]);
|
||||||
}
|
}
|
||||||
unset($this->aliasMap[spl_object_hash($package)]);
|
unset($this->aliasMap[spl_object_hash($package)]);
|
||||||
|
|
|
@ -229,11 +229,11 @@ class Problem
|
||||||
return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', 'it has the wrong version installed or is missing from your system, make sure to load the extension providing it.');
|
return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', 'it has the wrong version installed or is missing from your system, make sure to load the extension providing it.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$fixedPackage = null;
|
$lockedPackage = null;
|
||||||
foreach ($request->getFixedPackages() as $package) {
|
foreach ($request->getLockedPackages() as $package) {
|
||||||
if ($package->getName() === $packageName) {
|
if ($package->getName() === $packageName) {
|
||||||
$fixedPackage = $package;
|
$lockedPackage = $package;
|
||||||
if ($pool->isUnacceptableFixedPackage($package)) {
|
if ($pool->isUnacceptableFixedOrLockedPackage($package)) {
|
||||||
return array("- ", $package->getPrettyName().' is fixed to '.$package->getPrettyVersion().' (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you list it as an argument for the update command.');
|
return array("- ", $package->getPrettyName().' is fixed to '.$package->getPrettyVersion().' (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you list it as an argument for the update command.');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -253,13 +253,13 @@ class Problem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($fixedPackage) {
|
if ($lockedPackage) {
|
||||||
$fixedConstraint = new Constraint('==', $fixedPackage->getVersion());
|
$fixedConstraint = new Constraint('==', $lockedPackage->getVersion());
|
||||||
$filtered = array_filter($packages, function ($p) use ($fixedConstraint) {
|
$filtered = array_filter($packages, function ($p) use ($fixedConstraint) {
|
||||||
return $fixedConstraint->matches(new Constraint('==', $p->getVersion()));
|
return $fixedConstraint->matches(new Constraint('==', $p->getVersion()));
|
||||||
});
|
});
|
||||||
if (0 === count($filtered)) {
|
if (0 === count($filtered)) {
|
||||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but the package is fixed to '.$fixedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.');
|
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but the package is fixed to '.$lockedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Request
|
||||||
protected $lockedRepository;
|
protected $lockedRepository;
|
||||||
protected $requires = array();
|
protected $requires = array();
|
||||||
protected $fixedPackages = array();
|
protected $fixedPackages = array();
|
||||||
protected $unlockables = array();
|
protected $lockedPackages = array();
|
||||||
protected $updateAllowList = array();
|
protected $updateAllowList = array();
|
||||||
protected $updateAllowTransitiveDependencies = false;
|
protected $updateAllowTransitiveDependencies = false;
|
||||||
|
|
||||||
|
@ -71,18 +71,22 @@ class Request
|
||||||
*
|
*
|
||||||
* @param bool $lockable if set to false, the package will not be written to the lock file
|
* @param bool $lockable if set to false, the package will not be written to the lock file
|
||||||
*/
|
*/
|
||||||
public function fixPackage(PackageInterface $package, $lockable = true)
|
public function fixPackage(PackageInterface $package)
|
||||||
{
|
{
|
||||||
$this->fixedPackages[spl_object_hash($package)] = $package;
|
$this->fixedPackages[spl_object_hash($package)] = $package;
|
||||||
|
|
||||||
if (!$lockable) {
|
|
||||||
$this->unlockables[spl_object_hash($package)] = $package;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unfixPackage(PackageInterface $package)
|
/**
|
||||||
|
* Mark an existing package as installed but removable
|
||||||
|
*/
|
||||||
|
public function lockPackage(PackageInterface $package)
|
||||||
{
|
{
|
||||||
unset($this->fixedPackages[spl_object_hash($package)], $this->unlockables[spl_object_hash($package)]);
|
$this->lockedPackages[spl_object_hash($package)] = $package;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unlockPackage(PackageInterface $package)
|
||||||
|
{
|
||||||
|
unset($this->lockedPackages[spl_object_hash($package)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUpdateAllowList($updateAllowList, $updateAllowTransitiveDependencies)
|
public function setUpdateAllowList($updateAllowList, $updateAllowTransitiveDependencies)
|
||||||
|
@ -121,6 +125,21 @@ class Request
|
||||||
return isset($this->fixedPackages[spl_object_hash($package)]);
|
return isset($this->fixedPackages[spl_object_hash($package)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getLockedPackages()
|
||||||
|
{
|
||||||
|
return $this->lockedPackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isLockedPackage(PackageInterface $package)
|
||||||
|
{
|
||||||
|
return isset($this->lockedPackages[spl_object_hash($package)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFixedOrLockedPackages()
|
||||||
|
{
|
||||||
|
return array_merge($this->fixedPackages, $this->lockedPackages);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO look into removing the packageIds option, the only place true is used is for the installed map in the solver problems
|
// TODO look into removing the packageIds option, the only place true is used is for the installed map in the solver problems
|
||||||
// some locked packages may not be in the pool so they have a package->id of -1
|
// some locked packages may not be in the pool so they have a package->id of -1
|
||||||
public function getPresentMap($packageIds = false)
|
public function getPresentMap($packageIds = false)
|
||||||
|
@ -140,15 +159,15 @@ class Request
|
||||||
return $presentMap;
|
return $presentMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUnlockableMap()
|
public function getFixedPackagesMap()
|
||||||
{
|
{
|
||||||
$unlockableMap = array();
|
$fixedPackagesMap = array();
|
||||||
|
|
||||||
foreach ($this->unlockables as $package) {
|
foreach ($this->fixedPackages as $package) {
|
||||||
$unlockableMap[$package->id] = $package;
|
$fixedPackagesMap[$package->id] = $package;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $unlockableMap;
|
return $fixedPackagesMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLockedRepository()
|
public function getLockedRepository()
|
||||||
|
|
|
@ -125,23 +125,25 @@ abstract class Rule
|
||||||
|
|
||||||
public function isCausedByLock(RepositorySet $repositorySet, Request $request, Pool $pool)
|
public function isCausedByLock(RepositorySet $repositorySet, Request $request, Pool $pool)
|
||||||
{
|
{
|
||||||
if ($this->getReason() === self::RULE_FIXED && $this->reasonData['lockable']) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->getReason() === self::RULE_PACKAGE_REQUIRES) {
|
if ($this->getReason() === self::RULE_PACKAGE_REQUIRES) {
|
||||||
if (PlatformRepository::isPlatformPackage($this->reasonData->getTarget())) {
|
if (PlatformRepository::isPlatformPackage($this->reasonData->getTarget())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
foreach ($request->getFixedPackages() as $package) {
|
if ($request->getLockedRepository()) {
|
||||||
if ($package->getName() === $this->reasonData->getTarget()) {
|
foreach ($request->getLockedRepository()->getPackages() as $package) {
|
||||||
if ($pool->isUnacceptableFixedPackage($package)) {
|
if ($package->getName() === $this->reasonData->getTarget()) {
|
||||||
return true;
|
if ($pool->isUnacceptableFixedOrLockedPackage($package)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!$this->reasonData->getConstraint()->matches(new Constraint('=', $package->getVersion()))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// required package was locked but has been unlocked and still matches
|
||||||
|
if (!$request->isLockedPackage($package)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (!$this->reasonData->getConstraint()->matches(new Constraint('=', $package->getVersion()))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,15 +152,17 @@ abstract class Rule
|
||||||
if (PlatformRepository::isPlatformPackage($this->reasonData['packageName'])) {
|
if (PlatformRepository::isPlatformPackage($this->reasonData['packageName'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
foreach ($request->getFixedPackages() as $package) {
|
if ($request->getLockedRepository()) {
|
||||||
if ($package->getName() === $this->reasonData['packageName']) {
|
foreach ($request->getLockedRepository()->getPackages() as $package) {
|
||||||
if ($pool->isUnacceptableFixedPackage($package)) {
|
if ($package->getName() === $this->reasonData['packageName']) {
|
||||||
return true;
|
if ($pool->isUnacceptableFixedOrLockedPackage($package)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!$this->reasonData['constraint']->matches(new Constraint('=', $package->getVersion()))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (!$this->reasonData['constraint']->matches(new Constraint('=', $package->getVersion()))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,13 +184,20 @@ abstract class Rule
|
||||||
return 'No package found to satisfy root composer.json require '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '');
|
return 'No package found to satisfy root composer.json require '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$packagesNonAlias = array_values(array_filter($packages, function ($p) {
|
||||||
|
return !($p instanceof AliasPackage);
|
||||||
|
}));
|
||||||
|
if (count($packagesNonAlias) === 1) {
|
||||||
|
$package = $packagesNonAlias[0];
|
||||||
|
if (!($package instanceof AliasPackage) && $request->isLockedPackage($package)) {
|
||||||
|
return $package->getPrettyName().' is locked to version '.$package->getPrettyVersion()." and an update of this package was not requested.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages, $isVerbose).'.';
|
return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages, $isVerbose).'.';
|
||||||
|
|
||||||
case self::RULE_FIXED:
|
case self::RULE_FIXED:
|
||||||
$package = $this->deduplicateDefaultBranchAlias($this->reasonData['package']);
|
$package = $this->deduplicateDefaultBranchAlias($this->reasonData['package']);
|
||||||
if ($this->reasonData['lockable']) {
|
|
||||||
return $package->getPrettyName().' is locked to version '.$package->getPrettyVersion().' and an update of this package was not requested.';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $package->getPrettyName().' is present at version '.$package->getPrettyVersion() . ' and cannot be modified by Composer';
|
return $package->getPrettyName().' is present at version '.$package->getPrettyVersion() . ' and cannot be modified by Composer';
|
||||||
|
|
||||||
|
|
|
@ -223,12 +223,10 @@ class RuleSetGenerator
|
||||||
|
|
||||||
protected function addRulesForRequest(Request $request, $ignorePlatformReqs)
|
protected function addRulesForRequest(Request $request, $ignorePlatformReqs)
|
||||||
{
|
{
|
||||||
$unlockableMap = $request->getUnlockableMap();
|
|
||||||
|
|
||||||
foreach ($request->getFixedPackages() as $package) {
|
foreach ($request->getFixedPackages() as $package) {
|
||||||
if ($package->id == -1) {
|
if ($package->id == -1) {
|
||||||
// fixed package was not added to the pool as it did not pass the stability requirements, this is fine
|
// fixed package was not added to the pool as it did not pass the stability requirements, this is fine
|
||||||
if ($this->pool->isUnacceptableFixedPackage($package)) {
|
if ($this->pool->isUnacceptableFixedOrLockedPackage($package)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +238,6 @@ class RuleSetGenerator
|
||||||
|
|
||||||
$rule = $this->createInstallOneOfRule(array($package), Rule::RULE_FIXED, array(
|
$rule = $this->createInstallOneOfRule(array($package), Rule::RULE_FIXED, array(
|
||||||
'package' => $package,
|
'package' => $package,
|
||||||
'lockable' => !isset($unlockableMap[$package->id]),
|
|
||||||
));
|
));
|
||||||
$this->addRule(RuleSet::TYPE_REQUEST, $rule);
|
$this->addRule(RuleSet::TYPE_REQUEST, $rule);
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,7 @@ class Solver
|
||||||
throw new SolverProblemsException($this->problems, $this->learnedPool);
|
throw new SolverProblemsException($this->problems, $this->learnedPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LockTransaction($this->pool, $request->getPresentMap(), $request->getUnlockableMap(), $this->decisions);
|
return new LockTransaction($this->pool, $request->getPresentMap(), $request->getFixedPackagesMap(), $this->decisions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -821,9 +821,9 @@ class Installer
|
||||||
{
|
{
|
||||||
$request = new Request($lockedRepository);
|
$request = new Request($lockedRepository);
|
||||||
|
|
||||||
$request->fixPackage($rootPackage, false);
|
$request->fixPackage($rootPackage);
|
||||||
if ($rootPackage instanceof RootAliasPackage) {
|
if ($rootPackage instanceof RootAliasPackage) {
|
||||||
$request->fixPackage($rootPackage->getAliasOf(), false);
|
$request->fixPackage($rootPackage->getAliasOf());
|
||||||
}
|
}
|
||||||
|
|
||||||
$fixedPackages = $platformRepo->getPackages();
|
$fixedPackages = $platformRepo->getPackages();
|
||||||
|
@ -841,7 +841,7 @@ class Installer
|
||||||
|| !isset($provided[$package->getName()])
|
|| !isset($provided[$package->getName()])
|
||||||
|| !$provided[$package->getName()]->getConstraint()->matches(new Constraint('=', $package->getVersion()))
|
|| !$provided[$package->getName()]->getConstraint()->matches(new Constraint('=', $package->getVersion()))
|
||||||
) {
|
) {
|
||||||
$request->fixPackage($package, false);
|
$request->fixPackage($package);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,9 +43,6 @@ Updating dependencies
|
||||||
Your requirements could not be resolved to an installable set of packages.
|
Your requirements could not be resolved to an installable set of packages.
|
||||||
|
|
||||||
Problem 1
|
Problem 1
|
||||||
- locked/pkg is locked to version dev-master and an update of this package was not requested.
|
|
||||||
- locked/pkg dev-master requires locked/dependency 1.0.0 -> found locked/dependency[1.0.0] in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file.
|
|
||||||
Problem 2
|
|
||||||
- locked/pkg dev-master requires locked/dependency 1.0.0 -> found locked/dependency[1.0.0] in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file.
|
- locked/pkg dev-master requires locked/dependency 1.0.0 -> found locked/dependency[1.0.0] in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file.
|
||||||
- locked/pkg is locked to version dev-master and an update of this package was not requested.
|
- locked/pkg is locked to version dev-master and an update of this package was not requested.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
--TEST--
|
--TEST--
|
||||||
If a new requirement cannot be installed on a partial update due to replace, there should be a suggestion to use --with-all-dependencies
|
A partial update for a new package yet to be installed should remove another dependency it replaces if that allows installation.
|
||||||
--COMPOSER--
|
--COMPOSER--
|
||||||
{
|
{
|
||||||
"repositories": [
|
"repositories": [
|
||||||
|
@ -39,17 +39,6 @@ If a new requirement cannot be installed on a partial update due to replace, the
|
||||||
}
|
}
|
||||||
--RUN--
|
--RUN--
|
||||||
update new/pkg
|
update new/pkg
|
||||||
--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
|
|
||||||
- current/dep is locked to version 1.0.0 and an update of this package was not requested.
|
|
||||||
- new/pkg[1.0.0] cannot be installed as that would require removing current/dep[1.0.0]. new/pkg replaces current/dep and thus cannot coexist with it.
|
|
||||||
- Root composer.json requires new/pkg 1.* -> satisfiable by new/pkg[1.0.0].
|
|
||||||
|
|
||||||
Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions.
|
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
|
Removing current/dep (1.0.0)
|
||||||
|
Installing new/pkg (1.0.0)
|
||||||
|
|
|
@ -45,6 +45,7 @@ Require a new package in the composer.json and updating with its name as an argu
|
||||||
--RUN--
|
--RUN--
|
||||||
update new/pkg --with-dependencies
|
update new/pkg --with-dependencies
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
|
Removing current/dep-provide (1.0.0)
|
||||||
Removing current/dep (1.0.0)
|
Removing current/dep (1.0.0)
|
||||||
Installing new/pkg (1.0.0)
|
Installing new/pkg (1.0.0)
|
||||||
Installing new/pkg-provide (1.0.0)
|
Installing new/pkg-provide (1.0.0)
|
||||||
|
|
Loading…
Reference in New Issue