Let the solver handle aliases instead of the installer
parent
d74eec9bd5
commit
0c1944a9d0
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\DependencyResolver\Operation;
|
||||||
|
|
||||||
|
use Composer\Package\AliasPackage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solver install operation.
|
||||||
|
*
|
||||||
|
* @author Konstantin Kudryashov <ever.zet@gmail.com>
|
||||||
|
*/
|
||||||
|
class MarkAliasInstalledOperation extends SolverOperation
|
||||||
|
{
|
||||||
|
protected $package;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes operation.
|
||||||
|
*
|
||||||
|
* @param PackageInterface $package package instance
|
||||||
|
* @param string $reason operation reason
|
||||||
|
*/
|
||||||
|
public function __construct(AliasPackage $package, $reason = null)
|
||||||
|
{
|
||||||
|
parent::__construct($reason);
|
||||||
|
|
||||||
|
$this->package = $package;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns package instance.
|
||||||
|
*
|
||||||
|
* @return PackageInterface
|
||||||
|
*/
|
||||||
|
public function getPackage()
|
||||||
|
{
|
||||||
|
return $this->package;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns job type.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getJobType()
|
||||||
|
{
|
||||||
|
return 'markAliasInstalled';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return 'Marking '.$this->package->getPrettyName().' ('.$this->package->getPrettyVersion().') as installed, alias of '.$this->package->getAliasOf()->getPrettyName().' ('.$this->package->getAliasOf()->getPrettyVersion().')';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\DependencyResolver\Operation;
|
||||||
|
|
||||||
|
use Composer\Package\AliasPackage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solver install operation.
|
||||||
|
*
|
||||||
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
*/
|
||||||
|
class MarkAliasUninstalledOperation extends SolverOperation
|
||||||
|
{
|
||||||
|
protected $package;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes operation.
|
||||||
|
*
|
||||||
|
* @param PackageInterface $package package instance
|
||||||
|
* @param string $reason operation reason
|
||||||
|
*/
|
||||||
|
public function __construct(AliasPackage $package, $reason = null)
|
||||||
|
{
|
||||||
|
parent::__construct($reason);
|
||||||
|
|
||||||
|
$this->package = $package;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns package instance.
|
||||||
|
*
|
||||||
|
* @return PackageInterface
|
||||||
|
*/
|
||||||
|
public function getPackage()
|
||||||
|
{
|
||||||
|
return $this->package;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns job type.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getJobType()
|
||||||
|
{
|
||||||
|
return 'markAliasUninstalled';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return 'Marking '.$this->package->getPrettyName().' ('.$this->package->getPrettyVersion().') as uninstalled, alias of '.$this->package->getAliasOf()->getPrettyName().' ('.$this->package->getAliasOf()->getPrettyVersion().')';
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ class Rule
|
||||||
const RULE_PACKAGE_SAME_NAME = 10;
|
const RULE_PACKAGE_SAME_NAME = 10;
|
||||||
const RULE_PACKAGE_IMPLICIT_OBSOLETES = 11;
|
const RULE_PACKAGE_IMPLICIT_OBSOLETES = 11;
|
||||||
const RULE_LEARNED = 12;
|
const RULE_LEARNED = 12;
|
||||||
|
const RULE_PACKAGE_ALIAS = 13;
|
||||||
|
|
||||||
protected $disabled;
|
protected $disabled;
|
||||||
protected $literals;
|
protected $literals;
|
||||||
|
@ -235,6 +236,8 @@ class Rule
|
||||||
return $ruleText;
|
return $ruleText;
|
||||||
case self::RULE_LEARNED:
|
case self::RULE_LEARNED:
|
||||||
return 'learned: '.$ruleText;
|
return 'learned: '.$ruleText;
|
||||||
|
case self::RULE_PACKAGE_ALIAS:
|
||||||
|
return $ruleText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\DependencyResolver;
|
||||||
|
|
||||||
use Composer\Repository\RepositoryInterface;
|
use Composer\Repository\RepositoryInterface;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
|
use Composer\Package\AliasPackage;
|
||||||
use Composer\DependencyResolver\Operation;
|
use Composer\DependencyResolver\Operation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -255,10 +256,12 @@ class Solver
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
|
||||||
$reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
|
$reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
|
||||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link));
|
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check implicit obsoletes
|
// check implicit obsoletes
|
||||||
// for installed packages we only need to check installed/installed problems,
|
// for installed packages we only need to check installed/installed problems,
|
||||||
|
@ -271,16 +274,30 @@ class Solver
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($isInstalled && !isset($this->installedMap[$provider->getId()])) {
|
if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) {
|
||||||
continue;
|
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, (string) $package));
|
||||||
}
|
} else if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
|
||||||
|
|
||||||
$reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES;
|
$reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES;
|
||||||
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, (string) $package));
|
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, (string) $package));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function obsoleteImpossibleForAlias($package, $provider)
|
||||||
|
{
|
||||||
|
$packageIsAlias = $package instanceof AliasPackage;
|
||||||
|
$providerIsAlias = $provider instanceof AliasPackage;
|
||||||
|
|
||||||
|
$impossible = (
|
||||||
|
($packageIsAlias && $package->getAliasOf() === $provider) ||
|
||||||
|
($providerIsAlias && $provider->getAliasOf() === $package) ||
|
||||||
|
($packageIsAlias && $providerIsAlias && $provider->getAliasOf() === $package->getAliasOf())
|
||||||
|
);
|
||||||
|
|
||||||
|
return $impossible;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds all rules for all update packages of a given package
|
* Adds all rules for all update packages of a given package
|
||||||
|
@ -646,7 +663,15 @@ class Solver
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($literal->isWanted()) {
|
if ($literal->isWanted()) {
|
||||||
|
if ($package instanceof AliasPackage) {
|
||||||
|
$transaction[] = new Operation\MarkAliasInstalledOperation(
|
||||||
|
$package, $this->decisionQueueWhy[$i]
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($installMeansUpdateMap[$literal->getPackageId()])) {
|
if (isset($installMeansUpdateMap[$literal->getPackageId()])) {
|
||||||
|
|
||||||
$source = $installMeansUpdateMap[$literal->getPackageId()];
|
$source = $installMeansUpdateMap[$literal->getPackageId()];
|
||||||
|
|
||||||
$transaction[] = new Operation\UpdateOperation(
|
$transaction[] = new Operation\UpdateOperation(
|
||||||
|
@ -662,11 +687,49 @@ class Solver
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (!isset($ignoreRemove[$package->getId()])) {
|
} else if (!isset($ignoreRemove[$package->getId()])) {
|
||||||
|
if ($package instanceof AliasPackage) {
|
||||||
|
$transaction[] = new Operation\MarkAliasInstalledOperation(
|
||||||
|
$package, $this->decisionQueueWhy[$i]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
$transaction[] = new Operation\UninstallOperation(
|
$transaction[] = new Operation\UninstallOperation(
|
||||||
$package, $this->decisionQueueWhy[$i]
|
$package, $this->decisionQueueWhy[$i]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$allDecidedMap = $this->decisionMap;
|
||||||
|
foreach ($this->decisionMap as $packageId => $decision) {
|
||||||
|
if ($decision != 0) {
|
||||||
|
$package = $this->pool->packageById($packageId);
|
||||||
|
if ($package instanceof AliasPackage) {
|
||||||
|
$allDecidedMap[$package->getAliasOf()->getId()] = $decision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($allDecidedMap as $packageId => $decision) {
|
||||||
|
if ($packageId === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == $decision && isset($this->installedMap[$packageId])) {
|
||||||
|
$package = $this->pool->packageById($packageId);
|
||||||
|
|
||||||
|
if ($package instanceof AliasPackage) {
|
||||||
|
$transaction[] = new Operation\MarkAliasInstalledOperation(
|
||||||
|
$package, null
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$transaction[] = new Operation\UninstallOperation(
|
||||||
|
$package, null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->decisionMap[$packageId] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($this->decisionMap as $packageId => $decision) {
|
foreach ($this->decisionMap as $packageId => $decision) {
|
||||||
if ($packageId === 0) {
|
if ($packageId === 0) {
|
||||||
|
@ -674,9 +737,19 @@ class Solver
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 == $decision && isset($this->installedMap[$packageId])) {
|
if (0 == $decision && isset($this->installedMap[$packageId])) {
|
||||||
$transaction[] = new Operation\UninstallOperation(
|
$package = $this->pool->packageById($packageId);
|
||||||
$this->pool->packageById($packageId), null
|
|
||||||
|
if ($package instanceof AliasPackage) {
|
||||||
|
$transaction[] = new Operation\MarkAliasInstalledOperation(
|
||||||
|
$package, null
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
$transaction[] = new Operation\UninstallOperation(
|
||||||
|
$package, null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->decisionMap[$packageId] = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,7 +806,7 @@ class Solver
|
||||||
protected function decisionsSatisfy(Literal $l)
|
protected function decisionsSatisfy(Literal $l)
|
||||||
{
|
{
|
||||||
return ($l->isWanted() && $this->decisionMap[$l->getPackageId()] > 0) ||
|
return ($l->isWanted() && $this->decisionMap[$l->getPackageId()] > 0) ||
|
||||||
(!$l->isWanted() && $this->decisionMap[$l->getPackageId()] <= 0);
|
(!$l->isWanted() && $this->decisionMap[$l->getPackageId()] < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function decisionsConflict(Literal $l)
|
protected function decisionsConflict(Literal $l)
|
||||||
|
@ -876,11 +949,6 @@ class Solver
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** TODO: implement recommendations
|
|
||||||
*if (v > 0 && solv->recommendations.count && v == solv->recommendations.elements[solv->recommendations.count - 1])
|
|
||||||
* solv->recommendations.count--;
|
|
||||||
*/
|
|
||||||
|
|
||||||
$this->decisionMap[$literal->getPackageId()] = 0;
|
$this->decisionMap[$literal->getPackageId()] = 0;
|
||||||
array_pop($this->decisionQueue);
|
array_pop($this->decisionQueue);
|
||||||
array_pop($this->decisionQueueWhy);
|
array_pop($this->decisionQueueWhy);
|
||||||
|
@ -1060,14 +1128,19 @@ class Solver
|
||||||
$l1num++;
|
$l1num++;
|
||||||
$l1retry = true;
|
$l1retry = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$rule = $this->decisionQueueWhy[$decisionId];
|
$rule = $this->decisionQueueWhy[$decisionId];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$why = count($this->learnedPool) - 1;
|
$why = count($this->learnedPool) - 1;
|
||||||
|
|
||||||
|
if (count($learnedLiterals) === 1 && $learnedLiterals[0] === null) {
|
||||||
|
$newRule = new Rule(array(), Rule::RULE_LEARNED, $why);
|
||||||
|
} else {
|
||||||
assert($learnedLiterals[0] !== null);
|
assert($learnedLiterals[0] !== null);
|
||||||
$newRule = new Rule($learnedLiterals, Rule::RULE_LEARNED, $why);
|
$newRule = new Rule($learnedLiterals, Rule::RULE_LEARNED, $why);
|
||||||
|
}
|
||||||
|
|
||||||
return array($learnedLiterals[0], $ruleLevel, $newRule, $why);
|
return array($learnedLiterals[0], $ruleLevel, $newRule, $why);
|
||||||
}
|
}
|
||||||
|
@ -1178,7 +1251,7 @@ class Solver
|
||||||
$this->disableProblem($why);
|
$this->disableProblem($why);
|
||||||
$this->resetSolver();
|
$this->resetSolver();
|
||||||
|
|
||||||
return true;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($disableRules) {
|
if ($disableRules) {
|
||||||
|
@ -1191,10 +1264,10 @@ class Solver
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->resetSolver();
|
$this->resetSolver();
|
||||||
return true;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function disableProblem($why)
|
private function disableProblem($why)
|
||||||
|
@ -1266,8 +1339,8 @@ class Solver
|
||||||
// * here's the main loop:
|
// * here's the main loop:
|
||||||
// * 1) propagate new decisions (only needed once)
|
// * 1) propagate new decisions (only needed once)
|
||||||
// * 2) fulfill jobs
|
// * 2) fulfill jobs
|
||||||
// * 4) fulfill all unresolved rules
|
// * 3) fulfill all unresolved rules
|
||||||
// * 6) minimalize solution if we had choices
|
// * 4) minimalize solution if we had choices
|
||||||
// * if we encounter a problem, we rewind to a safe level and restart
|
// * if we encounter a problem, we rewind to a safe level and restart
|
||||||
// * with step 1
|
// * with step 1
|
||||||
// */
|
// */
|
||||||
|
@ -1403,18 +1476,14 @@ class Solver
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// open suse sat-solver uses this, but why is $level == 1 trouble?
|
|
||||||
// SYSTEMSOLVABLE related? we don't have that, so should work
|
|
||||||
//if ($level < $systemLevel || $level == 1) {
|
|
||||||
|
|
||||||
if ($level < $systemLevel) {
|
|
||||||
break; // trouble
|
|
||||||
}
|
|
||||||
|
|
||||||
// something changed, so look at all rules again
|
// something changed, so look at all rules again
|
||||||
$n = -1;
|
$n = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($level < $systemLevel) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// minimization step
|
// minimization step
|
||||||
if (count($this->branches)) {
|
if (count($this->branches)) {
|
||||||
|
|
||||||
|
|
|
@ -326,14 +326,6 @@ class Installer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// anti-alias local repository to allow updates to work fine
|
|
||||||
foreach ($localRepo->getPackages() as $package) {
|
|
||||||
if ($package instanceof AliasPackage) {
|
|
||||||
$package->getRepository()->addPackage(clone $package->getAliasOf());
|
|
||||||
$package->getRepository()->removePackage($package);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// execute operations
|
// execute operations
|
||||||
if (!$operations) {
|
if (!$operations) {
|
||||||
$this->io->write('Nothing to install or update');
|
$this->io->write('Nothing to install or update');
|
||||||
|
@ -356,7 +348,10 @@ class Installer
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->dryRun) {
|
if (!$this->dryRun) {
|
||||||
$this->eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType())), $operation);
|
$event = 'Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType());
|
||||||
|
if (defined($event)) {
|
||||||
|
$this->eventDispatcher->dispatchPackageEvent(constant($event), $operation);
|
||||||
|
}
|
||||||
|
|
||||||
// if installing from lock, restore dev packages' references to their locked state
|
// if installing from lock, restore dev packages' references to their locked state
|
||||||
if ($installFromLock) {
|
if ($installFromLock) {
|
||||||
|
@ -378,17 +373,15 @@ class Installer
|
||||||
}
|
}
|
||||||
$this->installationManager->execute($localRepo, $operation);
|
$this->installationManager->execute($localRepo, $operation);
|
||||||
|
|
||||||
$this->eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation);
|
$event = 'Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType());
|
||||||
|
if (defined($event)) {
|
||||||
|
$this->eventDispatcher->dispatchPackageEvent(constant($event), $operation);
|
||||||
|
}
|
||||||
|
|
||||||
$localRepo->write();
|
$localRepo->write();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reload local repository for the dev pass to work ok with aliases since it was anti-aliased above
|
|
||||||
if (!$devMode) {
|
|
||||||
$localRepo->reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ use Composer\DependencyResolver\Operation\OperationInterface;
|
||||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||||
use Composer\DependencyResolver\Operation\UpdateOperation;
|
use Composer\DependencyResolver\Operation\UpdateOperation;
|
||||||
use Composer\DependencyResolver\Operation\UninstallOperation;
|
use Composer\DependencyResolver\Operation\UninstallOperation;
|
||||||
|
use Composer\DependencyResolver\Operation\MarkAliasInstalledOperation;
|
||||||
|
use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation;
|
||||||
use Composer\Util\Filesystem;
|
use Composer\Util\Filesystem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,6 +106,10 @@ class InstallationManager
|
||||||
*/
|
*/
|
||||||
public function isPackageInstalled(InstalledRepositoryInterface $repo, PackageInterface $package)
|
public function isPackageInstalled(InstalledRepositoryInterface $repo, PackageInterface $package)
|
||||||
{
|
{
|
||||||
|
if ($package instanceof AliasPackage) {
|
||||||
|
return $repo->hasPackage($package);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->getInstaller($package->getType())->isInstalled($repo, $package);
|
return $this->getInstaller($package->getType())->isInstalled($repo, $package);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +133,7 @@ class InstallationManager
|
||||||
*/
|
*/
|
||||||
public function install(RepositoryInterface $repo, InstallOperation $operation)
|
public function install(RepositoryInterface $repo, InstallOperation $operation)
|
||||||
{
|
{
|
||||||
$package = $this->antiAlias($operation->getPackage());
|
$package = $operation->getPackage();
|
||||||
$installer = $this->getInstaller($package->getType());
|
$installer = $this->getInstaller($package->getType());
|
||||||
$installer->install($repo, $package);
|
$installer->install($repo, $package);
|
||||||
$this->notifyInstall($package);
|
$this->notifyInstall($package);
|
||||||
|
@ -141,8 +147,8 @@ class InstallationManager
|
||||||
*/
|
*/
|
||||||
public function update(RepositoryInterface $repo, UpdateOperation $operation)
|
public function update(RepositoryInterface $repo, UpdateOperation $operation)
|
||||||
{
|
{
|
||||||
$initial = $this->antiAlias($operation->getInitialPackage());
|
$initial = $operation->getInitialPackage();
|
||||||
$target = $this->antiAlias($operation->getTargetPackage());
|
$target = $operation->getTargetPackage();
|
||||||
|
|
||||||
$initialType = $initial->getType();
|
$initialType = $initial->getType();
|
||||||
$targetType = $target->getType();
|
$targetType = $target->getType();
|
||||||
|
@ -165,11 +171,41 @@ class InstallationManager
|
||||||
*/
|
*/
|
||||||
public function uninstall(RepositoryInterface $repo, UninstallOperation $operation)
|
public function uninstall(RepositoryInterface $repo, UninstallOperation $operation)
|
||||||
{
|
{
|
||||||
$package = $this->antiAlias($operation->getPackage());
|
$package = $operation->getPackage();
|
||||||
$installer = $this->getInstaller($package->getType());
|
$installer = $this->getInstaller($package->getType());
|
||||||
$installer->uninstall($repo, $package);
|
$installer->uninstall($repo, $package);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes markAliasInstalled operation.
|
||||||
|
*
|
||||||
|
* @param RepositoryInterface $repo repository in which to check
|
||||||
|
* @param MarkAliasInstalledOperation $operation operation instance
|
||||||
|
*/
|
||||||
|
public function markAliasInstalled(RepositoryInterface $repo, MarkAliasInstalledOperation $operation)
|
||||||
|
{
|
||||||
|
$package = $operation->getPackage();
|
||||||
|
|
||||||
|
if (!$repo->hasPackage($package)) {
|
||||||
|
$repo->addPackage(clone $package);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->notifyInstall($package);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes markAlias operation.
|
||||||
|
*
|
||||||
|
* @param RepositoryInterface $repo repository in which to check
|
||||||
|
* @param MarkAliasUninstalledOperation $operation operation instance
|
||||||
|
*/
|
||||||
|
public function markAliasUninstalled(RepositoryInterface $repo, MarkAliasUninstalledOperation $operation)
|
||||||
|
{
|
||||||
|
$package = $operation->getPackage();
|
||||||
|
|
||||||
|
$repo->removePackage($package);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the installation path of a package
|
* Returns the installation path of a package
|
||||||
*
|
*
|
||||||
|
@ -203,17 +239,4 @@ class InstallationManager
|
||||||
$package->getRepository()->notifyInstall($package);
|
$package->getRepository()->notifyInstall($package);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function antiAlias(PackageInterface $package)
|
|
||||||
{
|
|
||||||
if ($package instanceof AliasPackage) {
|
|
||||||
$alias = $package;
|
|
||||||
$package = $package->getAliasOf();
|
|
||||||
$package->setInstalledAsAlias(true);
|
|
||||||
$package->setAlias($alias->getVersion());
|
|
||||||
$package->setPrettyAlias($alias->getPrettyVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $package;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,8 +41,9 @@ class ArrayDumper
|
||||||
|
|
||||||
$data = array();
|
$data = array();
|
||||||
$data['name'] = $package->getPrettyName();
|
$data['name'] = $package->getPrettyName();
|
||||||
|
|
||||||
$data['version'] = $package->getPrettyVersion();
|
$data['version'] = $package->getPrettyVersion();
|
||||||
$data['version_normalized'] = $package->getVersion();
|
$data['version_normalized'] = $package->getVersion();;
|
||||||
|
|
||||||
if ($package->getTargetDir()) {
|
if ($package->getTargetDir()) {
|
||||||
$data['target-dir'] = $package->getTargetDir();
|
$data['target-dir'] = $package->getTargetDir();
|
||||||
|
|
|
@ -89,7 +89,7 @@ class Locker
|
||||||
$repo = $dev ? $this->repositoryManager->getLocalDevRepository() : $this->repositoryManager->getLocalRepository();
|
$repo = $dev ? $this->repositoryManager->getLocalDevRepository() : $this->repositoryManager->getLocalRepository();
|
||||||
|
|
||||||
foreach ($lockedPackages as $info) {
|
foreach ($lockedPackages as $info) {
|
||||||
$resolvedVersion = !empty($info['alias']) ? $info['alias'] : $info['version'];
|
$resolvedVersion = !empty($info['alias-version']) ? $info['alias-version'] : $info['version'];
|
||||||
|
|
||||||
// try to find the package in the local repo (best match)
|
// try to find the package in the local repo (best match)
|
||||||
$package = $repo->findPackage($info['package'], $resolvedVersion);
|
$package = $repo->findPackage($info['package'], $resolvedVersion);
|
||||||
|
@ -100,10 +100,10 @@ class Locker
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to find the package in any repo (second pass without alias + rebuild alias since it disappeared)
|
// try to find the package in any repo (second pass without alias + rebuild alias since it disappeared)
|
||||||
if (!$package && !empty($info['alias'])) {
|
if (!$package && !empty($info['alias-version'])) {
|
||||||
$package = $this->repositoryManager->findPackage($info['package'], $info['version']);
|
$package = $this->repositoryManager->findPackage($info['package'], $info['version']);
|
||||||
if ($package) {
|
if ($package) {
|
||||||
$alias = new AliasPackage($package, $info['alias'], $info['alias']);
|
$alias = new AliasPackage($package, $info['alias-version'], $info['alias-pretty-version']);
|
||||||
$package->getRepository()->addPackage($alias);
|
$package->getRepository()->addPackage($alias);
|
||||||
$package = $alias;
|
$package = $alias;
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,10 @@ class Locker
|
||||||
$locked = array();
|
$locked = array();
|
||||||
|
|
||||||
foreach ($packages as $package) {
|
foreach ($packages as $package) {
|
||||||
|
$alias = null;
|
||||||
|
|
||||||
if ($package instanceof AliasPackage) {
|
if ($package instanceof AliasPackage) {
|
||||||
|
$alias = $package;
|
||||||
$package = $package->getAliasOf();
|
$package = $package->getAliasOf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,8 +201,9 @@ class Locker
|
||||||
$spec['source-reference'] = $package->getSourceReference();
|
$spec['source-reference'] = $package->getSourceReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($package->getAlias() && $package->isInstalledAsAlias()) {
|
if ($alias) {
|
||||||
$spec['alias'] = $package->getAlias();
|
$spec['alias-pretty-version'] = $alias->getPrettyVersion();
|
||||||
|
$spec['alias-version'] = $alias->getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
$locked[] = $spec;
|
$locked[] = $spec;
|
||||||
|
|
|
@ -46,7 +46,6 @@ class MemoryPackage extends BasePackage
|
||||||
protected $aliases = array();
|
protected $aliases = array();
|
||||||
protected $alias;
|
protected $alias;
|
||||||
protected $prettyAlias;
|
protected $prettyAlias;
|
||||||
protected $installedAsAlias;
|
|
||||||
protected $dev;
|
protected $dev;
|
||||||
|
|
||||||
protected $requires = array();
|
protected $requires = array();
|
||||||
|
@ -211,24 +210,6 @@ class MemoryPackage extends BasePackage
|
||||||
return $this->prettyAlias;
|
return $this->prettyAlias;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Enabled if the package is installed from its alias package
|
|
||||||
*
|
|
||||||
* @param string $installedAsAlias
|
|
||||||
*/
|
|
||||||
public function setInstalledAsAlias($installedAsAlias)
|
|
||||||
{
|
|
||||||
$this->installedAsAlias = $installedAsAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function isInstalledAsAlias()
|
|
||||||
{
|
|
||||||
return $this->installedAsAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -103,8 +103,8 @@ class ArrayRepository implements RepositoryInterface
|
||||||
$package->setRepository($this);
|
$package->setRepository($this);
|
||||||
$this->packages[] = $package;
|
$this->packages[] = $package;
|
||||||
|
|
||||||
// create alias package on the fly if needed (installed repos manage aliases themselves)
|
// create alias package on the fly if needed
|
||||||
if ($package->getAlias() && !$this instanceof InstalledRepositoryInterface) {
|
if ($package->getAlias()) {
|
||||||
$this->addPackage($this->createAliasPackage($package));
|
$this->addPackage($this->createAliasPackage($package));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\Repository;
|
||||||
|
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
|
use Composer\Package\AliasPackage;
|
||||||
use Composer\Package\Loader\ArrayLoader;
|
use Composer\Package\Loader\ArrayLoader;
|
||||||
use Composer\Package\Dumper\ArrayDumper;
|
use Composer\Package\Dumper\ArrayDumper;
|
||||||
|
|
||||||
|
@ -54,21 +55,32 @@ class FilesystemRepository extends ArrayRepository implements WritableRepository
|
||||||
throw new \UnexpectedValueException('Could not parse package list from the '.$this->file->getPath().' repository');
|
throw new \UnexpectedValueException('Could not parse package list from the '.$this->file->getPath().' repository');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$aliases = array();
|
||||||
|
|
||||||
$loader = new ArrayLoader();
|
$loader = new ArrayLoader();
|
||||||
foreach ($packages as $packageData) {
|
foreach ($packages as $packageData) {
|
||||||
$package = $loader->load($packageData);
|
$package = $loader->load($packageData);
|
||||||
|
|
||||||
// package was installed as alias, so we only add the alias
|
// aliases need to be looked up in the end to set up references correctly
|
||||||
if ($this instanceof InstalledRepositoryInterface && !empty($packageData['installed-as-alias'])) {
|
if ($this instanceof InstalledRepositoryInterface && !empty($packageData['alias'])) {
|
||||||
$alias = $packageData['installed-as-alias'];
|
$aliases[] = array(
|
||||||
$package->setAlias($alias);
|
'package' => $package,
|
||||||
$package->setPrettyAlias($alias);
|
'alias' => $packageData['alias'],
|
||||||
$package->setInstalledAsAlias(true);
|
'alias_pretty' => $packageData['alias_pretty']
|
||||||
$this->addPackage($this->createAliasPackage($package, $alias, $alias));
|
);
|
||||||
} else {
|
}
|
||||||
// only add regular package - if it's not an installed repo the alias will be created on the fly
|
|
||||||
$this->addPackage($package);
|
$this->addPackage($package);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($aliases as $aliasData) {
|
||||||
|
$temporaryPackage = $aliasData['package'];
|
||||||
|
|
||||||
|
$package = $this->findPackage($package->getName(), $package->getVersion());
|
||||||
|
|
||||||
|
$package->setAlias($aliasData['alias']);
|
||||||
|
$package->setPrettyAlias($aliasData['alias_pretty']);
|
||||||
|
$this->addPackage($this->createAliasPackage($package, $aliasData['alias'], $aliasData['alias_pretty']));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,12 +98,11 @@ class FilesystemRepository extends ArrayRepository implements WritableRepository
|
||||||
$packages = array();
|
$packages = array();
|
||||||
$dumper = new ArrayDumper();
|
$dumper = new ArrayDumper();
|
||||||
foreach ($this->getPackages() as $package) {
|
foreach ($this->getPackages() as $package) {
|
||||||
|
if (!$package instanceof AliasPackage) {
|
||||||
$data = $dumper->dump($package);
|
$data = $dumper->dump($package);
|
||||||
if ($this instanceof InstalledRepositoryInterface && $package->isInstalledAsAlias()) {
|
|
||||||
$data['installed-as-alias'] = $package->getAlias();
|
|
||||||
}
|
|
||||||
$packages[] = $data;
|
$packages[] = $data;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->file->write($packages);
|
$this->file->write($packages);
|
||||||
}
|
}
|
||||||
|
|
|
@ -670,6 +670,55 @@ class SolverTest extends TestCase
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testInstallRecursiveAliasDependencies()
|
||||||
|
{
|
||||||
|
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
|
||||||
|
$this->repo->addPackage($packageB = $this->getPackage('B', '2.0'));
|
||||||
|
$this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0'));
|
||||||
|
|
||||||
|
$packageA2->setRequires(array(
|
||||||
|
new Link('A', 'B', $this->getVersionConstraint('==', '2.0'), 'requires', '== 2.0'),
|
||||||
|
));
|
||||||
|
$packageB->setRequires(array(
|
||||||
|
new Link('B', 'A', $this->getVersionConstraint('>=', '2.0'), 'requires'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->repo->addPackage($packageA2Alias = $this->getAliasPackage($packageA2, '1.1'));
|
||||||
|
|
||||||
|
$this->reposComplete();
|
||||||
|
|
||||||
|
$this->request->install('A', $this->getVersionConstraint('==', '1.1.0.0'));
|
||||||
|
|
||||||
|
$this->checkSolverResult(array(
|
||||||
|
array('job' => 'install', 'package' => $packageB),
|
||||||
|
array('job' => 'install', 'package' => $packageA2),
|
||||||
|
array('job' => 'install', 'package' => $packageA2Alias),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInstallDevAlias()
|
||||||
|
{
|
||||||
|
$this->repo->addPackage($packageA = $this->getPackage('A', '2.0'));
|
||||||
|
$this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));
|
||||||
|
|
||||||
|
$packageB->setRequires(array(
|
||||||
|
new Link('B', 'A', $this->getVersionConstraint('<', '2.0'), 'requires'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->repo->addPackage($packageAAlias = $this->getAliasPackage($packageA, '1.1'));
|
||||||
|
|
||||||
|
$this->reposComplete();
|
||||||
|
|
||||||
|
$this->request->install('A', $this->getVersionConstraint('==', '2.0'));
|
||||||
|
$this->request->install('B');
|
||||||
|
|
||||||
|
$this->checkSolverResult(array(
|
||||||
|
array('job' => 'install', 'package' => $packageAAlias),
|
||||||
|
array('job' => 'install', 'package' => $packageB),
|
||||||
|
array('job' => 'install', 'package' => $packageA),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
protected function reposComplete()
|
protected function reposComplete()
|
||||||
{
|
{
|
||||||
$this->pool->addRepository($this->repoInstalled);
|
$this->pool->addRepository($this->repoInstalled);
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\Test;
|
||||||
|
|
||||||
use Composer\Package\Version\VersionParser;
|
use Composer\Package\Version\VersionParser;
|
||||||
use Composer\Package\MemoryPackage;
|
use Composer\Package\MemoryPackage;
|
||||||
|
use Composer\Package\AliasPackage;
|
||||||
use Composer\Package\LinkConstraint\VersionConstraint;
|
use Composer\Package\LinkConstraint\VersionConstraint;
|
||||||
use Composer\Util\Filesystem;
|
use Composer\Util\Filesystem;
|
||||||
|
|
||||||
|
@ -45,6 +46,12 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
||||||
return new MemoryPackage($name, $normVersion, $version);
|
return new MemoryPackage($name, $normVersion, $version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getAliasPackage($package, $version)
|
||||||
|
{
|
||||||
|
$normVersion = self::getVersionParser()->normalize($version);
|
||||||
|
return new AliasPackage($package, $normVersion, $version);
|
||||||
|
}
|
||||||
|
|
||||||
protected function ensureDirectoryExistsAndClear($directory)
|
protected function ensureDirectoryExistsAndClear($directory)
|
||||||
{
|
{
|
||||||
$fs = new Filesystem();
|
$fs = new Filesystem();
|
||||||
|
|
Loading…
Reference in New Issue