diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index 1f444c788..9e8ecf29b 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -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', ); diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index aa014035c..afa8f6996 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -30,13 +30,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,50 +245,38 @@ 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()])); - $reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES; - $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link)); - } - } + foreach ($package->getReplaces() as $link) { + $obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint()); - // 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; + } - foreach ($obsoleteProviders as $provider) { - if ($provider === $package) { - continue; - } + $reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES; + $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link)); + } + } - if ($isInstalled && !isset($this->installedMap[$provider->getId()])) { - 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); - // obsolete same packages even when noObsoletes - if ($noObsoletes && (!$package->equals($provider))) { - continue; - } + foreach ($obsoleteProviders as $provider) { + if ($provider === $package) { + continue; + } - $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)); + if ($isInstalled && !isset($this->installedMap[$provider->getId()])) { + continue; } + + $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)); } } } @@ -431,7 +427,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 +488,22 @@ class Solver } $this->disableProblem($why); - /** TODO solver_reenablepolicyrules(solv, -(v + 1)); */ } } - protected function addChoiceRules() + protected function setupInstalledMap() { - -// 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() - { - $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 +524,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 +551,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 +596,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 +625,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) { @@ -1032,18 +668,21 @@ class Solver } } + foreach ($this->decisionMap as $packageId => $decision) { + if ($packageId === 0) { + continue; + } + + if (0 == $decision && isset($this->installedMap[$packageId])) { + $transaction[] = new Operation\UninstallOperation( + $this->pool->packageById($packageId), null + ); + } + } + 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)); @@ -1536,17 +1175,7 @@ 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; @@ -1637,9 +1266,7 @@ 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 // * if we encounter a problem, we rewind to a safe level and restart // * with step 1 @@ -1654,8 +1281,6 @@ class Solver $minimizationSteps = 0; $installedPos = 0; - $this->installedPackages = $this->installed->getPackages(); - while (true) { if (1 === $level) { @@ -1727,125 +1352,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; } diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 356934514..3e15acf80 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -64,7 +64,7 @@ class FileDownloader implements DownloaderInterface $fileName = $this->getFileName($package, $path); - $this->io->write(" - Package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); + $this->io->write(" - Installing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); $processUrl = $this->processUrl($url); @@ -101,6 +101,7 @@ class FileDownloader implements DownloaderInterface */ public function remove(PackageInterface $package, $path) { + $this->io->write(" - Removing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); if (!$this->filesystem->removeDirectory($path)) { throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); } diff --git a/src/Composer/Downloader/VcsDownloader.php b/src/Composer/Downloader/VcsDownloader.php index 2d19bba40..cacc74fc4 100644 --- a/src/Composer/Downloader/VcsDownloader.php +++ b/src/Composer/Downloader/VcsDownloader.php @@ -50,7 +50,7 @@ abstract class VcsDownloader implements DownloaderInterface throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information'); } - $this->io->write(" - Package " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); + $this->io->write(" - Installing " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); $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 " . $target->getName() . " (" . $target->getPrettyVersion() . ")"); + $this->io->write(" - Installing " . $target->getName() . " (" . $target->getPrettyVersion() . ")"); $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 " . $package->getName() . " (" . $package->getPrettyVersion() . ")"); if (!$this->filesystem->removeDirectory($path)) { throw new \RuntimeException('Could not completely delete '.$path.', aborting.'); } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index e4219c860..d7f51badb 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -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; } diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 0da923654..9be2e1d1d 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/src/Composer/Installer/LibraryInstaller.php @@ -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); + } + } } /** diff --git a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php index 28db18131..56084f32a 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php @@ -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); diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php index 54ad88a58..f319651ae 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php @@ -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() diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 49355a5a4..2bc5a928a 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -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');