Merge commit '45a2070feebdaf9ead7eccf5cd679cb92f0d24c9'

main
Jordi Boggiano 12 years ago
commit 5959493679

@ -61,6 +61,6 @@ class InstallOperation extends SolverOperation
*/
public function __toString()
{
return 'Installing '.$this->package->getPrettyName().' ('.$this->package->getPrettyVersion().')';
return 'Installing '.$this->package->getPrettyName().' ('.$this->formatVersion($this->package).')';
}
}

@ -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 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().')';
}
}

@ -42,4 +42,13 @@ abstract class SolverOperation implements OperationInterface
{
return $this->reason;
}
protected function formatVersion(PackageInterface $package)
{
if (!$package->isDev() || !in_array($package->getSourceType(), array('hg', 'git'))) {
return $package->getPrettyVersion();
}
return $package->getPrettyVersion().' '.substr($package->getSourceReference(), 0, 6);
}
}

@ -61,6 +61,6 @@ class UninstallOperation extends SolverOperation
*/
public function __toString()
{
return 'Uninstalling '.$this->package->getPrettyName().' ('.$this->package->getPrettyVersion().')';
return 'Uninstalling '.$this->package->getPrettyName().' ('.$this->formatVersion($this->package).')';
}
}

@ -74,7 +74,7 @@ class UpdateOperation extends SolverOperation
*/
public function __toString()
{
return 'Updating '.$this->initialPackage->getPrettyName().' ('.$this->initialPackage->getPrettyVersion().') to '.
$this->targetPackage->getPrettyName(). ' ('.$this->targetPackage->getPrettyVersion().')';
return 'Updating '.$this->initialPackage->getPrettyName().' ('.$this->formatVersion($this->initialPackage).') to '.
$this->targetPackage->getPrettyName(). ' ('.$this->formatVersion($this->targetPackage).')';
}
}

@ -29,6 +29,7 @@ class Rule
const RULE_PACKAGE_SAME_NAME = 10;
const RULE_PACKAGE_IMPLICIT_OBSOLETES = 11;
const RULE_LEARNED = 12;
const RULE_PACKAGE_ALIAS = 13;
protected $disabled;
protected $literals;
@ -235,6 +236,8 @@ class Rule
return $ruleText;
case self::RULE_LEARNED:
return 'learned: '.$ruleText;
case self::RULE_PACKAGE_ALIAS:
return $ruleText;
}
}

@ -20,16 +20,12 @@ class RuleSet implements \IteratorAggregate, \Countable
// highest priority => lowest number
const TYPE_PACKAGE = 0;
const TYPE_JOB = 1;
const TYPE_FEATURE = 3;
const TYPE_CHOICE = 4;
const TYPE_LEARNED = 5;
const TYPE_LEARNED = 4;
protected static $types = array(
-1 => 'UNKNOWN',
self::TYPE_PACKAGE => 'PACKAGE',
self::TYPE_FEATURE => 'FEATURE',
self::TYPE_JOB => 'JOB',
self::TYPE_CHOICE => 'CHOICE',
self::TYPE_LEARNED => 'LEARNED',
);

@ -14,6 +14,7 @@ namespace Composer\DependencyResolver;
use Composer\Repository\RepositoryInterface;
use Composer\Package\PackageInterface;
use Composer\Package\AliasPackage;
use Composer\DependencyResolver\Operation;
/**
@ -30,13 +31,21 @@ class Solver
protected $ruleToJob = array();
protected $addedMap = array();
protected $updateMap = array();
protected $noObsoletes = array();
protected $watches = array();
protected $removeWatches = array();
protected $decisionMap;
protected $installedMap;
protected $packageToFeatureRule = array();
protected $packageToUpdateRule = array();
protected $decisionQueue = array();
protected $decisionQueueWhy = array();
protected $decisionQueueFree = array();
protected $propagateIndex;
protected $branches = array();
protected $problems = array();
protected $learnedPool = array();
protected $recommendsIndex;
public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
{
@ -237,47 +246,37 @@ class Solver
}
// check obsoletes and implicit obsoletes of a package
// if ignoreinstalledsobsoletes is not set, we're also checking
// obsoletes of installed packages (like newer rpm versions)
//
/** TODO if ($this->noInstalledObsoletes) */
if (true) {
$noObsoletes = isset($this->noObsoletes[$package->getId()]);
$isInstalled = (isset($this->installedMap[$package->getId()]));
foreach ($package->getReplaces() as $link) {
$obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
foreach ($obsoleteProviders as $provider) {
if ($provider === $package) {
continue;
}
$isInstalled = (isset($this->installedMap[$package->getId()]));
foreach ($package->getReplaces() as $link) {
$obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
foreach ($obsoleteProviders as $provider) {
if ($provider === $package) {
continue;
}
if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
$reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link));
}
}
}
// check implicit obsoletes
// for installed packages we only need to check installed/installed problems,
// as the others are picked up when looking at the uninstalled package.
if (!$isInstalled) {
$obsoleteProviders = $this->pool->whatProvides($package->getName(), null);
foreach ($obsoleteProviders as $provider) {
if ($provider === $package) {
continue;
}
// check implicit obsoletes
// for installed packages we only need to check installed/installed problems,
// as the others are picked up when looking at the uninstalled package.
if (!$isInstalled) {
$obsoleteProviders = $this->pool->whatProvides($package->getName(), null);
if ($isInstalled && !isset($this->installedMap[$provider->getId()])) {
continue;
}
// obsolete same packages even when noObsoletes
if ($noObsoletes && (!$package->equals($provider))) {
continue;
}
foreach ($obsoleteProviders as $provider) {
if ($provider === $package) {
continue;
}
if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) {
$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;
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, (string) $package));
}
@ -286,6 +285,20 @@ class Solver
}
}
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
*
@ -431,7 +444,7 @@ class Solver
// push all of our rules (can only be feature or job rules)
// asserting this literal on the problem stack
foreach ($this->rules->getIteratorFor(array(RuleSet::TYPE_JOB, RuleSet::TYPE_FEATURE)) as $assertRule) {
foreach ($this->rules->getIteratorFor(RuleSet::TYPE_JOB) as $assertRule) {
if ($assertRule->isDisabled() || !$assertRule->isAssertion() || $assertRule->isWeak()) {
continue;
}
@ -492,370 +505,22 @@ class Solver
}
$this->disableProblem($why);
/** TODO solver_reenablepolicyrules(solv, -(v + 1)); */
}
}
protected function addChoiceRules()
{
// void
// solver_addchoicerules(Solver *solv)
// {
// Pool *pool = solv->pool;
// Map m, mneg;
// Rule *r;
// Queue q, qi;
// int i, j, rid, havechoice;
// Id p, d, *pp;
// Id p2, pp2;
// Solvable *s, *s2;
//
// solv->choicerules = solv->nrules;
// if (!pool->installed)
// {
// solv->choicerules_end = solv->nrules;
// return;
// }
// solv->choicerules_ref = sat_calloc(solv->rpmrules_end, sizeof(Id));
// queue_init(&q);
// queue_init(&qi);
// map_init(&m, pool->nsolvables);
// map_init(&mneg, pool->nsolvables);
// /* set up negative assertion map from infarch and dup rules */
// for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++)
// if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
// MAPSET(&mneg, -r->p);
// for (rid = solv->duprules, r = solv->rules + rid; rid < solv->duprules_end; rid++, r++)
// if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
// MAPSET(&mneg, -r->p);
// for (rid = 1; rid < solv->rpmrules_end ; rid++)
// {
// r = solv->rules + rid;
// if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0))
// continue; /* only look at requires rules */
// // solver_printrule(solv, SAT_DEBUG_RESULT, r);
// queue_empty(&q);
// queue_empty(&qi);
// havechoice = 0;
// FOR_RULELITERALS(p, pp, r)
// {
// if (p < 0)
// continue;
// s = pool->solvables + p;
// if (!s->repo)
// continue;
// if (s->repo == pool->installed)
// {
// queue_push(&q, p);
// continue;
// }
// /* check if this package is "blocked" by a installed package */
// s2 = 0;
// FOR_PROVIDES(p2, pp2, s->name)
// {
// s2 = pool->solvables + p2;
// if (s2->repo != pool->installed)
// continue;
// if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
// continue;
// if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
// continue;
// break;
// }
// if (p2)
// {
// /* found installed package p2 that we can update to p */
// if (MAPTST(&mneg, p))
// continue;
// if (policy_is_illegal(solv, s2, s, 0))
// continue;
// queue_push(&qi, p2);
// queue_push(&q, p);
// continue;
// }
// if (s->obsoletes)
// {
// Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
// s2 = 0;
// while ((obs = *obsp++) != 0)
// {
// FOR_PROVIDES(p2, pp2, obs)
// {
// s2 = pool->solvables + p2;
// if (s2->repo != pool->installed)
// continue;
// if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
// continue;
// if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
// continue;
// break;
// }
// if (p2)
// break;
// }
// if (obs)
// {
// /* found installed package p2 that we can update to p */
// if (MAPTST(&mneg, p))
// continue;
// if (policy_is_illegal(solv, s2, s, 0))
// continue;
// queue_push(&qi, p2);
// queue_push(&q, p);
// continue;
// }
// }
// /* package p is independent of the installed ones */
// havechoice = 1;
// }
// if (!havechoice || !q.count)
// continue; /* no choice */
//
// /* now check the update rules of the installed package.
// * if all packages of the update rules are contained in
// * the dependency rules, there's no need to set up the choice rule */
// map_empty(&m);
// FOR_RULELITERALS(p, pp, r)
// if (p > 0)
// MAPSET(&m, p);
// for (i = 0; i < qi.count; i++)
// {
// if (!qi.elements[i])
// continue;
// Rule *ur = solv->rules + solv->updaterules + (qi.elements[i] - pool->installed->start);
// if (!ur->p)
// ur = solv->rules + solv->featurerules + (qi.elements[i] - pool->installed->start);
// if (!ur->p)
// continue;
// FOR_RULELITERALS(p, pp, ur)
// if (!MAPTST(&m, p))
// break;
// if (p)
// break;
// for (j = i + 1; j < qi.count; j++)
// if (qi.elements[i] == qi.elements[j])
// qi.elements[j] = 0;
// }
// if (i == qi.count)
// {
// #if 0
// printf("skipping choice ");
// solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid);
// #endif
// continue;
// }
// d = q.count ? pool_queuetowhatprovides(pool, &q) : 0;
// solver_addrule(solv, r->p, d);
// queue_push(&solv->weakruleq, solv->nrules - 1);
// solv->choicerules_ref[solv->nrules - 1 - solv->choicerules] = rid;
// #if 0
// printf("OLD ");
// solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid);
// printf("WEAK CHOICE ");
// solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + solv->nrules - 1);
// #endif
// }
// queue_free(&q);
// queue_free(&qi);
// map_free(&m);
// map_free(&mneg);
// solv->choicerules_end = solv->nrules;
// }
}
/***********************************************************************
***
*** Policy rule disabling/reenabling
***
*** Disable all policy rules that conflict with our jobs. If a job
*** gets disabled later on, reenable the involved policy rules again.
***
*** /
#define DISABLE_UPDATE 1
#define DISABLE_INFARCH 2
#define DISABLE_DUP 3
*/
protected function jobToDisableQueue(array $job, array $disableQueue)
{
switch ($job['cmd']) {
case 'install':
foreach ($job['packages'] as $package) {
if (isset($this->installedMap[$package->getId()])) {
$disableQueue[] = array('type' => 'update', 'package' => $package);
}
/* all job packages obsolete * /
qstart = q->count;
pass = 0;
memset(&omap, 0, sizeof(omap));
FOR_JOB_SELECT(p, pp, select, what)
{
Id p2, pp2;
if (pass == 1)
map_grow(&omap, installed->end - installed->start);
s = pool->solvables + p;
if (s->obsoletes)
{
Id obs, *obsp;
obsp = s->repo->idarraydata + s->obsoletes;
while ((obs = *obsp++) != 0)
FOR_PROVIDES(p2, pp2, obs)
{
Solvable *ps = pool->solvables + p2;
if (ps->repo != installed)
continue;
if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
continue;
if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
continue;
if (pass)
MAPSET(&omap, p2 - installed->start);
else
queue_push2(q, DISABLE_UPDATE, p2);
}
}
FOR_PROVIDES(p2, pp2, s->name)
{
Solvable *ps = pool->solvables + p2;
if (ps->repo != installed)
continue;
if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
continue;
if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
continue;
if (pass)
MAPSET(&omap, p2 - installed->start);
else
queue_push2(q, DISABLE_UPDATE, p2);
}
if (pass)
{
for (i = j = qstart; i < q->count; i += 2)
{
if (MAPTST(&omap, q->elements[i + 1] - installed->start))
{
MAPCLR(&omap, q->elements[i + 1] - installed->start);
q->elements[j + 1] = q->elements[i + 1];
j += 2;
}
}
queue_truncate(q, j);
}
if (q->count == qstart)
break;
pass++;
}
if (omap.size)
map_free(&omap);
if (qstart == q->count)
return; /* nothing to prune * /
if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR))
return; /* all is set */
/* now that we know which installed packages are obsoleted check each of them * /
for (i = j = qstart; i < q->count; i += 2)
{
Solvable *is = pool->solvables + q->elements[i + 1];
FOR_JOB_SELECT(p, pp, select, what)
{
int illegal = 0;
s = pool->solvables + p;
if ((set & SOLVER_SETEVR) != 0)
illegal |= POLICY_ILLEGAL_DOWNGRADE; /* ignore * /
if ((set & SOLVER_SETARCH) != 0)
illegal |= POLICY_ILLEGAL_ARCHCHANGE; /* ignore * /
if ((set & SOLVER_SETVENDOR) != 0)
illegal |= POLICY_ILLEGAL_VENDORCHANGE; /* ignore * /
illegal = policy_is_illegal(solv, is, s, illegal);
if (illegal && illegal == POLICY_ILLEGAL_DOWNGRADE && (set & SOLVER_SETEV) != 0)
{
/* it's ok if the EV is different * /
if (evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE_EVONLY) != 0)
illegal = 0;
}
if (illegal)
break;
}
if (!p)
{
/* no package conflicts with the update rule * /
/* thus keep the DISABLE_UPDATE * /
q->elements[j + 1] = q->elements[i + 1];
j += 2;
}
}
queue_truncate(q, j);
return;*/
}
break;
case 'remove':
foreach ($job['packages'] as $package) {
if (isset($this->installedMap[$package->getId()])) {
$disableQueue[] = array('type' => 'update', 'package' => $package);
}
}
break;
}
return $disableQueue;
}
protected function disableUpdateRule($package)
{
if (isset($this->packageToFeatureRule[$package->getId()])) {
$this->packageToFeatureRule[$package->getId()]->disable();
}
}
/**
* Disables all policy rules that conflict with jobs
*/
protected function disablePolicyRules()
protected function setupInstalledMap()
{
$lastJob = null;
$allQueue = array();
$iterator = $this->rules->getIteratorFor(RuleSet::TYPE_JOB);
foreach ($iterator as $rule) {
if ($rule->isDisabled()) {
continue;
}
$job = $this->ruleToJob[$rule->getId()];
if ($job === $lastJob) {
continue;
}
$lastJob = $job;
$allQueue = $this->jobToDisableQueue($job, $allQueue);
}
foreach ($allQueue as $disable) {
switch ($disable['type']) {
case 'update':
$this->disableUpdateRule($disable['package']);
break;
default:
throw new \RuntimeException("Unsupported disable type: " . $disable['type']);
}
$this->installedMap = array();
foreach ($this->installed->getPackages() as $package) {
$this->installedMap[$package->getId()] = $package;
}
}
public function solve(Request $request)
{
$this->jobs = $request->getJobs();
$installedPackages = $this->installed->getPackages();
$this->installedMap = array();
foreach ($installedPackages as $package) {
$this->installedMap[$package->getId()] = $package;
}
$this->setupInstalledMap();
if (version_compare(PHP_VERSION, '5.3.4', '>=')) {
$this->decisionMap = new \SplFixedArray($this->pool->getMaxId() + 1);
@ -876,18 +541,18 @@ class Solver
switch ($job['cmd']) {
case 'update-all':
foreach ($installedPackages as $package) {
foreach ($this->installedMap as $package) {
$this->updateMap[$package->getId()] = true;
}
break;
}
}
foreach ($installedPackages as $package) {
foreach ($this->installedMap as $package) {
$this->addRulesForPackage($package);
}
foreach ($installedPackages as $package) {
foreach ($this->installedMap as $package) {
$this->addRulesForUpdatePackages($package);
}
@ -903,15 +568,11 @@ class Solver
}
}
// solver_addrpmrulesforweak(solv, &addedmap);
foreach ($installedPackages as $package) {
foreach ($this->installedMap as $package) {
$updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installedMap, $package);
$rule = $this->createUpdateRule($package, $updates, Rule::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
$rule->setWeak(true);
$this->addRule(RuleSet::TYPE_FEATURE, $rule);
$this->packageToFeatureRule[$package->getId()] = $rule;
$this->packageToUpdateRule[$package->getId()] = $rule;
}
foreach ($this->jobs as $job) {
@ -952,23 +613,15 @@ class Solver
}
}
$this->addChoiceRules();
foreach ($this->rules as $rule) {
$this->addWatchesToRule($rule);
}
/* disable update rules that conflict with our job */
$this->disablePolicyRules();
/* make decisions based on job/update assertions */
$this->makeAssertionRuleDecisions();
$installRecommended = 0;
$this->runSat(true, $installRecommended);
//$this->printDecisionMap();
//findrecommendedsuggested(solv);
//solver_prepare_solutions(solv);
if ($this->problems) {
throw new SolverProblemsException($this->problems);
@ -989,8 +642,8 @@ class Solver
if (!$literal->isWanted() && isset($this->installedMap[$package->getId()])) {
$literals = array();
if (isset($this->packageToFeatureRule[$package->getId()])) {
$literals = array_merge($literals, $this->packageToFeatureRule[$package->getId()]->getLiterals());
if (isset($this->packageToUpdateRule[$package->getId()])) {
$literals = array_merge($literals, $this->packageToUpdateRule[$package->getId()]->getLiterals());
}
foreach ($literals as $updateLiteral) {
@ -1010,7 +663,15 @@ class Solver
}
if ($literal->isWanted()) {
if ($package instanceof AliasPackage) {
$transaction[] = new Operation\MarkAliasInstalledOperation(
$package, $this->decisionQueueWhy[$i]
);
continue;
}
if (isset($installMeansUpdateMap[$literal->getPackageId()])) {
$source = $installMeansUpdateMap[$literal->getPackageId()];
$transaction[] = new Operation\UpdateOperation(
@ -1026,24 +687,75 @@ class Solver
);
}
} else if (!isset($ignoreRemove[$package->getId()])) {
$transaction[] = new Operation\UninstallOperation(
$package, $this->decisionQueueWhy[$i]
);
if ($package instanceof AliasPackage) {
$transaction[] = new Operation\MarkAliasInstalledOperation(
$package, $this->decisionQueueWhy[$i]
);
} else {
$transaction[] = new Operation\UninstallOperation(
$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) {
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;
}
}
return array_reverse($transaction);
}
protected $decisionQueue = array();
protected $decisionQueueWhy = array();
protected $decisionQueueFree = array();
protected $propagateIndex;
protected $branches = array();
protected $problems = array();
protected $learnedPool = array();
protected $recommendsIndex;
protected function literalFromId($id)
{
$package = $this->pool->packageById(abs($id));
@ -1094,7 +806,7 @@ class Solver
protected function decisionsSatisfy(Literal $l)
{
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)
@ -1237,11 +949,6 @@ class Solver
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;
array_pop($this->decisionQueue);
array_pop($this->decisionQueueWhy);
@ -1421,14 +1128,19 @@ class Solver
$l1num++;
$l1retry = true;
}
$rule = $this->decisionQueueWhy[$decisionId];
}
$rule = $this->decisionQueueWhy[$decisionId];
}
$why = count($this->learnedPool) - 1;
assert($learnedLiterals[0] !== null);
$newRule = new Rule($learnedLiterals, Rule::RULE_LEARNED, $why);
if (count($learnedLiterals) === 1 && $learnedLiterals[0] === null) {
$newRule = new Rule(array(), Rule::RULE_LEARNED, $why);
} else {
assert($learnedLiterals[0] !== null);
$newRule = new Rule($learnedLiterals, Rule::RULE_LEARNED, $why);
}
return array($learnedLiterals[0], $ruleLevel, $newRule, $why);
}
@ -1536,20 +1248,10 @@ class Solver
$why = $lastWeakWhy;
}
if ($lastWeakWhy->getType() == RuleSet::TYPE_CHOICE) {
$this->disableChoiceRules($lastWeakWhy);
}
$this->disableProblem($why);
/**
@TODO what does v < 0 mean here? ($why == v)
if (v < 0)
solver_reenablepolicyrules(solv, -(v + 1));
*/
$this->resetSolver();
return true;
return 1;
}
if ($disableRules) {
@ -1562,10 +1264,10 @@ class Solver
}
$this->resetSolver();
return true;
return 1;
}
return false;
return 0;
}
private function disableProblem($why)
@ -1637,10 +1339,8 @@ class Solver
// * here's the main loop:
// * 1) propagate new decisions (only needed once)
// * 2) fulfill jobs
// * 3) try to keep installed packages
// * 4) fulfill all unresolved rules
// * 5) install recommended packages
// * 6) minimalize solution if we had choices
// * 3) fulfill all unresolved rules
// * 4) minimalize solution if we had choices
// * if we encounter a problem, we rewind to a safe level and restart
// * with step 1
// */
@ -1654,8 +1354,6 @@ class Solver
$minimizationSteps = 0;
$installedPos = 0;
$this->installedPackages = $this->installed->getPackages();
while (true) {
if (1 === $level) {
@ -1727,125 +1425,6 @@ class Solver
}
}
// handle installed packages
if ($level < $systemLevel) {
// use two passes if any packages are being updated
// -> better user experience
for ($pass = (count($this->updateMap)) ? 0 : 1; $pass < 2; $pass++) {
$passLevel = $level;
for ($i = $installedPos, $n = 0; $n < count($this->installedPackages); $i++, $n++) {
$repeat = false;
if ($i == count($this->installedPackages)) {
$i = 0;
}
$literal = new Literal($this->installedPackages[$i], true);
if ($this->decisionsContain($literal)) {
continue;
}
// only process updates in first pass
/** TODO: && or || ? **/
if (0 === $pass && !isset($this->updateMap[$literal->getPackageId()])) {
continue;
}
$rule = null;
if (isset($this->packageToFeatureRule[$literal->getPackageId()])) {
$rule = $this->packageToFeatureRule[$literal->getPackageId()];
}
if (!$rule || $rule->isDisabled()) {
continue;
}
$updateRuleLiterals = $rule->getLiterals();
$decisionQueue = array();
if (!isset($this->noUpdate[$literal->getPackageId()]) && (
$this->decidedRemove($literal->getPackage()) ||
isset($this->updateMap[$literal->getPackageId()]) ||
!$literal->equals($updateRuleLiterals[0])
)) {
foreach ($updateRuleLiterals as $ruleLiteral) {
if ($this->decidedInstall($ruleLiteral->getPackage())) {
// already fulfilled
$decisionQueue = array();
break;
}
if ($this->undecided($ruleLiteral->getPackage())) {
$decisionQueue[] = $ruleLiteral;
}
}
}
if (sizeof($decisionQueue)) {
$oLevel = $level;
$level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
if (0 === $level) {
return;
}
if ($level <= $oLevel) {
$repeat = true;
}
} else if (!$repeat && $this->undecided($literal->getPackage())) {
// still undecided? keep package.
$oLevel = $level;
if (isset($this->cleanDepsMap[$literal->getPackageId()])) {
// clean deps removes package
$level = $this->setPropagateLearn($level, $literal->invert(), $disableRules, null);
} else {
// ckeeping package
$level = $this->setPropagateLearn($level, $literal, $disableRules, $rule);
}
if (0 === $level) {
return;
}
if ($level <= $oLevel) {
$repeat = true;
}
}
if ($repeat) {
if (1 === $level || $level < $passLevel) {
// trouble
break;
}
if ($level < $oLevel) {
// redo all
$n = 0;
}
// repeat
$i--;
$n--;
continue;
}
}
if ($n < count($this->installedPackages)) {
$installedPos = $i; // retry this problem next time
break;
}
$installedPos = 0;
}
$systemLevel = $level + 1;
if ($pass < 2) {
// had trouble => retry
continue;
}
}
if ($level < $systemLevel) {
$systemLevel = $level;
}
@ -1897,18 +1476,14 @@ class Solver
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
$n = -1;
}
if ($level < $systemLevel) {
continue;
}
// minimization step
if (count($this->branches)) {

@ -64,7 +64,7 @@ class FileDownloader implements DownloaderInterface
$fileName = $this->getFileName($package, $path);
$this->io->write(" - Package <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
$this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
$processUrl = $this->processUrl($url);
@ -101,6 +101,7 @@ class FileDownloader implements DownloaderInterface
*/
public function remove(PackageInterface $package, $path)
{
$this->io->write(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
if (!$this->filesystem->removeDirectory($path)) {
throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
}

@ -50,7 +50,7 @@ abstract class VcsDownloader implements DownloaderInterface
throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information');
}
$this->io->write(" - Package <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
$this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
$this->filesystem->removeDirectory($path);
$this->doDownload($package, $path);
$this->io->write('');
@ -65,7 +65,7 @@ abstract class VcsDownloader implements DownloaderInterface
throw new \InvalidArgumentException('Package '.$target->getPrettyName().' is missing reference information');
}
$this->io->write(" - Package <info>" . $target->getName() . "</info> (<comment>" . $target->getPrettyVersion() . "</comment>)");
$this->io->write(" - Installing <info>" . $target->getName() . "</info> (<comment>" . $target->getPrettyVersion() . "</comment>)");
$this->enforceCleanDirectory($path);
$this->doUpdate($initial, $target, $path);
$this->io->write('');
@ -77,6 +77,7 @@ abstract class VcsDownloader implements DownloaderInterface
public function remove(PackageInterface $package, $path)
{
$this->enforceCleanDirectory($path);
$this->io->write(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
if (!$this->filesystem->removeDirectory($path)) {
throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
}

@ -242,7 +242,7 @@ class Installer
$version = $package->getVersion();
foreach ($aliases as $alias) {
if ($alias['package'] === $package->getName() && $alias['version'] === $package->getVersion()) {
$version = $alias['alias'];
$version = $alias['alias_normalized'];
break;
}
}
@ -259,9 +259,11 @@ class Installer
}
}
// fix the version all installed packages that are not in the current local repo to prevent rogue updates
// fix the version of all installed packages (+ platform) that are not
// in the current local repo to prevent rogue updates (e.g. non-dev
// updating when in dev)
foreach ($installedRepo->getPackages() as $package) {
if ($package->getRepository() === $localRepo || $package->getRepository() instanceof PlatformRepository) {
if ($package->getRepository() === $localRepo) {
continue;
}
@ -324,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
if (!$operations) {
$this->io->write('Nothing to install or update');
@ -354,7 +348,10 @@ class Installer
}
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 ($installFromLock) {
@ -376,17 +373,15 @@ class Installer
}
$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();
}
}
// reload local repository for the dev pass to work ok with aliases since it was anti-aliased above
if (!$devMode) {
$localRepo->reload();
}
return true;
}
@ -410,7 +405,6 @@ class Installer
$package->setAlias($alias['alias_normalized']);
$package->setPrettyAlias($alias['alias']);
$package->getRepository()->addPackage($aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']));
$package->getRepository()->removePackage($package);
$aliasPackage->setRootPackageAlias(true);
}
}

@ -21,6 +21,8 @@ use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Operation\UninstallOperation;
use Composer\DependencyResolver\Operation\MarkAliasInstalledOperation;
use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation;
use Composer\Util\Filesystem;
/**
@ -104,6 +106,10 @@ class InstallationManager
*/
public function isPackageInstalled(InstalledRepositoryInterface $repo, PackageInterface $package)
{
if ($package instanceof AliasPackage) {
return $repo->hasPackage($package);
}
return $this->getInstaller($package->getType())->isInstalled($repo, $package);
}
@ -127,7 +133,7 @@ class InstallationManager
*/
public function install(RepositoryInterface $repo, InstallOperation $operation)
{
$package = $this->antiAlias($operation->getPackage());
$package = $operation->getPackage();
$installer = $this->getInstaller($package->getType());
$installer->install($repo, $package);
$this->notifyInstall($package);
@ -141,8 +147,8 @@ class InstallationManager
*/
public function update(RepositoryInterface $repo, UpdateOperation $operation)
{
$initial = $this->antiAlias($operation->getInitialPackage());
$target = $this->antiAlias($operation->getTargetPackage());
$initial = $operation->getInitialPackage();
$target = $operation->getTargetPackage();
$initialType = $initial->getType();
$targetType = $target->getType();
@ -165,11 +171,41 @@ class InstallationManager
*/
public function uninstall(RepositoryInterface $repo, UninstallOperation $operation)
{
$package = $this->antiAlias($operation->getPackage());
$package = $operation->getPackage();
$installer = $this->getInstaller($package->getType());
$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
*
@ -203,17 +239,4 @@ class InstallationManager
$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;
}
}

@ -127,6 +127,13 @@ class LibraryInstaller implements InstallerInterface
$this->downloadManager->remove($package, $downloadPath);
$this->removeBinaries($package);
$repo->removePackage($package);
if (strpos($package->getName(), '/')) {
$packageVendorDir = dirname($downloadPath);
if (is_dir($packageVendorDir) && !glob($packageVendorDir.'/*')) {
@rmdir($packageVendorDir);
}
}
}
/**

@ -89,7 +89,13 @@ class Locker
$repo = $dev ? $this->repositoryManager->getLocalDevRepository() : $this->repositoryManager->getLocalRepository();
foreach ($lockedPackages as $info) {
$resolvedVersion = !empty($info['alias']) ? $info['alias'] : $info['version'];
// TODO BC remove this after June 10th
if (isset($info['alias']) && empty($warned)) {
$warned = true;
echo 'BC warning: your lock file appears to be of an older format than this composer version, it is recommended to run composer update'.PHP_EOL;
}
$resolvedVersion = !empty($info['alias-version']) ? $info['alias-version'] : $info['version'];
// try to find the package in the local repo (best match)
$package = $repo->findPackage($info['package'], $resolvedVersion);
@ -100,10 +106,10 @@ class Locker
}
// 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']);
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 = $alias;
}
@ -179,7 +185,10 @@ class Locker
$locked = array();
foreach ($packages as $package) {
$alias = null;
if ($package instanceof AliasPackage) {
$alias = $package;
$package = $package->getAliasOf();
}
@ -198,8 +207,9 @@ class Locker
$spec['source-reference'] = $package->getSourceReference();
}
if ($package->getAlias() && $package->isInstalledAsAlias()) {
$spec['alias'] = $package->getAlias();
if ($alias) {
$spec['alias-pretty-version'] = $alias->getPrettyVersion();
$spec['alias-version'] = $alias->getVersion();
}
$locked[] = $spec;

@ -46,7 +46,6 @@ class MemoryPackage extends BasePackage
protected $aliases = array();
protected $alias;
protected $prettyAlias;
protected $installedAsAlias;
protected $dev;
protected $requires = array();
@ -211,24 +210,6 @@ class MemoryPackage extends BasePackage
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}
*/

@ -103,8 +103,8 @@ class ArrayRepository implements RepositoryInterface
$package->setRepository($this);
$this->packages[] = $package;
// create alias package on the fly if needed (installed repos manage aliases themselves)
if ($package->getAlias() && !$this instanceof InstalledRepositoryInterface) {
// create alias package on the fly if needed
if ($package->getAlias()) {
$this->addPackage($this->createAliasPackage($package));
}
}

@ -14,6 +14,7 @@ namespace Composer\Repository;
use Composer\Json\JsonFile;
use Composer\Package\PackageInterface;
use Composer\Package\AliasPackage;
use Composer\Package\Loader\ArrayLoader;
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');
}
$aliases = array();
$loader = new ArrayLoader();
foreach ($packages as $packageData) {
$package = $loader->load($packageData);
// package was installed as alias, so we only add the alias
if ($this instanceof InstalledRepositoryInterface && !empty($packageData['installed-as-alias'])) {
$alias = $packageData['installed-as-alias'];
$package->setAlias($alias);
$package->setPrettyAlias($alias);
$package->setInstalledAsAlias(true);
$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);
// aliases need to be looked up in the end to set up references correctly
if ($this instanceof InstalledRepositoryInterface && !empty($packageData['alias'])) {
$aliases[] = array(
'package' => $package,
'alias' => $packageData['alias'],
'alias_pretty' => $packageData['alias_pretty']
);
}
$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,11 +98,10 @@ class FilesystemRepository extends ArrayRepository implements WritableRepository
$packages = array();
$dumper = new ArrayDumper();
foreach ($this->getPackages() as $package) {
$data = $dumper->dump($package);
if ($this instanceof InstalledRepositoryInterface && $package->isInstalledAsAlias()) {
$data['installed-as-alias'] = $package->getAlias();
if (!$package instanceof AliasPackage) {
$data = $dumper->dump($package);
$packages[] = $data;
}
$packages[] = $data;
}
$this->file->write($packages);

@ -27,7 +27,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase
new Rule(array(), 'job1', null),
new Rule(array(), 'job2', null),
),
RuleSet::TYPE_FEATURE => array(
RuleSet::TYPE_LEARNED => array(
new Rule(array(), 'update1', null),
),
RuleSet::TYPE_PACKAGE => array(),
@ -46,7 +46,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase
$expected = array(
$this->rules[RuleSet::TYPE_JOB][0],
$this->rules[RuleSet::TYPE_JOB][1],
$this->rules[RuleSet::TYPE_FEATURE][0],
$this->rules[RuleSet::TYPE_LEARNED][0],
);
$this->assertEquals($expected, $result);
@ -64,7 +64,7 @@ class ResultSetIteratorTest extends \PHPUnit_Framework_TestCase
$expected = array(
RuleSet::TYPE_JOB,
RuleSet::TYPE_JOB,
RuleSet::TYPE_FEATURE,
RuleSet::TYPE_LEARNED,
);
$this->assertEquals($expected, $result);

@ -27,17 +27,15 @@ class RuleSetTest extends TestCase
new Rule(array(), 'job1', null),
new Rule(array(), 'job2', null),
),
RuleSet::TYPE_FEATURE => array(
RuleSet::TYPE_LEARNED => array(
new Rule(array(), 'update1', null),
),
RuleSet::TYPE_LEARNED => array(),
RuleSet::TYPE_CHOICE => array(),
);
$ruleSet = new RuleSet;
$ruleSet->add($rules[RuleSet::TYPE_JOB][0], RuleSet::TYPE_JOB);
$ruleSet->add($rules[RuleSet::TYPE_FEATURE][0], RuleSet::TYPE_FEATURE);
$ruleSet->add($rules[RuleSet::TYPE_LEARNED][0], RuleSet::TYPE_LEARNED);
$ruleSet->add($rules[RuleSet::TYPE_JOB][1], RuleSet::TYPE_JOB);
$this->assertEquals($rules, $ruleSet->getRules());
@ -80,7 +78,7 @@ class RuleSetTest extends TestCase
$rule1 = new Rule(array(), 'job1', null);
$rule2 = new Rule(array(), 'job1', null);
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
$ruleSet->add($rule2, RuleSet::TYPE_FEATURE);
$ruleSet->add($rule2, RuleSet::TYPE_LEARNED);
$iterator = $ruleSet->getIterator();
@ -96,9 +94,9 @@ class RuleSetTest extends TestCase
$rule2 = new Rule(array(), 'job1', null);
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
$ruleSet->add($rule2, RuleSet::TYPE_FEATURE);
$ruleSet->add($rule2, RuleSet::TYPE_LEARNED);
$iterator = $ruleSet->getIteratorFor(RuleSet::TYPE_FEATURE);
$iterator = $ruleSet->getIteratorFor(RuleSet::TYPE_LEARNED);
$this->assertSame($rule2, $iterator->current());
}
@ -110,7 +108,7 @@ class RuleSetTest extends TestCase
$rule2 = new Rule(array(), 'job1', null);
$ruleSet->add($rule1, RuleSet::TYPE_JOB);
$ruleSet->add($rule2, RuleSet::TYPE_FEATURE);
$ruleSet->add($rule2, RuleSet::TYPE_LEARNED);
$iterator = $ruleSet->getIteratorWithout(RuleSet::TYPE_JOB);
@ -142,7 +140,7 @@ class RuleSetTest extends TestCase
->method('equal')
->will($this->returnValue(false));
$ruleSet->add($rule, RuleSet::TYPE_FEATURE);
$ruleSet->add($rule, RuleSet::TYPE_LEARNED);
$this->assertTrue($ruleSet->containsEqual($rule));
$this->assertFalse($ruleSet->containsEqual($rule2));
@ -155,9 +153,9 @@ class RuleSetTest extends TestCase
$literal = new Literal($this->getPackage('foo', '2.1'), true);
$rule = new Rule(array($literal), 'job1', null);
$ruleSet->add($rule, RuleSet::TYPE_FEATURE);
$ruleSet->add($rule, RuleSet::TYPE_JOB);
$this->assertContains('FEATURE : (+foo-2.1.0.0)', $ruleSet->__toString());
$this->assertContains('JOB : (+foo-2.1.0.0)', $ruleSet->__toString());
}
private function getRuleMock()

@ -54,6 +54,16 @@ class SolverTest extends TestCase
));
}
public function testSolverRemoveIfNotInstalled()
{
$this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->reposComplete();
$this->checkSolverResult(array(
array('job' => 'remove', 'package' => $packageA),
));
}
public function testInstallNonExistingPackageFails()
{
$this->repo->addPackage($this->getPackage('A', '1.0'));
@ -176,6 +186,7 @@ class SolverTest extends TestCase
$this->repo->addPackage($newPackageA = $this->getPackage('A', '1.1'));
$this->reposComplete();
$this->request->install('A');
$this->request->update('A');
$this->checkSolverResult(array(
@ -191,6 +202,7 @@ class SolverTest extends TestCase
$this->repo->addPackage($newPackageB = $this->getPackage('B', '1.1'));
$packageA->setRequires(array(new Link('A', 'B', null, 'requires')));
$newPackageA->setRequires(array(new Link('A', 'B', null, 'requires')));
$this->reposComplete();
@ -209,6 +221,7 @@ class SolverTest extends TestCase
$this->repo->addPackage($this->getPackage('A', '1.0'));
$this->reposComplete();
$this->request->install('A');
$this->request->update('A');
$this->checkSolverResult(array());
@ -223,6 +236,8 @@ class SolverTest extends TestCase
$this->reposComplete();
$this->request->install('A');
$this->request->install('B');
$this->request->update('A');
$this->checkSolverResult(array(
@ -267,7 +282,7 @@ class SolverTest extends TestCase
public function testSolverUpdateFullyConstrainedPrunesInstalledPackages()
{
$this->repoInstalled->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->repoInstalled->addPackage($this->getPackage('B', '1.0'));
$this->repoInstalled->addPackage($packageB = $this->getPackage('B', '1.0'));
$this->repo->addPackage($newPackageA = $this->getPackage('A', '1.2'));
$this->repo->addPackage($this->getPackage('A', '2.0'));
$this->reposComplete();
@ -275,10 +290,15 @@ class SolverTest extends TestCase
$this->request->install('A', $this->getVersionConstraint('<', '2.0.0.0'));
$this->request->update('A', $this->getVersionConstraint('=', '1.0.0.0'));
$this->checkSolverResult(array(array(
'job' => 'update',
'from' => $packageA,
'to' => $newPackageA,
$this->checkSolverResult(array(
array(
'job' => 'remove',
'package' => $packageB,
),
array(
'job' => 'update',
'from' => $packageA,
'to' => $newPackageA,
)));
}
@ -297,6 +317,7 @@ class SolverTest extends TestCase
$this->reposComplete();
$this->request->install('A');
$this->request->install('C');
$this->request->update('C');
$this->request->remove('D');
@ -649,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()
{
$this->pool->addRepository($this->repoInstalled);

@ -14,6 +14,7 @@ namespace Composer\Test;
use Composer\Package\Version\VersionParser;
use Composer\Package\MemoryPackage;
use Composer\Package\AliasPackage;
use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Util\Filesystem;
@ -45,6 +46,12 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
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)
{
$fs = new Filesystem();

Loading…
Cancel
Save