| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120 |
- <?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;
- use Composer\Repository\RepositoryInterface;
- use Composer\Package\PackageInterface;
- /**
- * @author Nils Adermann <naderman@naderman.de>
- */
- class Solver
- {
- const RULE_INTERNAL_ALLOW_UPDATE = 1;
- const RULE_JOB_INSTALL = 2;
- const RULE_JOB_REMOVE = 3;
- const RULE_JOB_LOCK = 4;
- const RULE_NOT_INSTALLABLE = 5;
- const RULE_NOTHING_PROVIDES_DEP = 6;
- const RULE_PACKAGE_CONFLICT = 7;
- const RULE_PACKAGE_NOT_EXIST = 8;
- const RULE_PACKAGE_REQUIRES = 9;
- const TYPE_PACKAGE = 0;
- const TYPE_FEATURE = 1;
- const TYPE_UPDATE = 2;
- const TYPE_JOB = 3;
- const TYPE_WEAK = 4;
- const TYPE_LEARNED = 5;
- protected static $types = array(
- -1 => 'UNKNOWN',
- self::TYPE_PACKAGE => 'PACKAGE',
- self::TYPE_FEATURE => 'FEATURE',
- self::TYPE_UPDATE => 'UPDATE',
- self::TYPE_JOB => 'JOB',
- self::TYPE_WEAK => 'WEAK',
- self::TYPE_LEARNED => 'LEARNED',
- );
- protected $policy;
- protected $pool;
- protected $installed;
- protected $rules;
- protected $updateAll;
- protected $addedMap = array();
- protected $fixMap = array();
- protected $updateMap = array();
- protected $watches = array();
- protected $removeWatches = array();
- public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
- {
- $this->policy = $policy;
- $this->pool = $pool;
- $this->installed = $installed;
- $this->rules = array(
- // order matters here! further down => higher priority
- self::TYPE_LEARNED => array(),
- self::TYPE_WEAK => array(),
- self::TYPE_FEATURE => array(),
- self::TYPE_UPDATE => array(),
- self::TYPE_JOB => array(),
- self::TYPE_PACKAGE => array(),
- );
- }
- /**
- * Creates a new rule for the requirements of a package
- *
- * This rule is of the form (-A|B|C), where B and C are the providers of
- * one requirement of the package A.
- *
- * @param PackageInterface $package The package with a requirement
- * @param array $providers The providers of the requirement
- * @param int $reason A RULE_* constant describing the
- * reason for generating this rule
- * @param mixed $reasonData Any data, e.g. the requirement name,
- * that goes with the reason
- * @return Rule The generated rule or null if tautological
- */
- public function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null)
- {
- $literals = array(new Literal($package, false));
- foreach ($providers as $provider) {
- // self fulfilling rule?
- if ($provider === $package) {
- return null;
- }
- $literals[] = new Literal($provider, true);
- }
- return new Rule($literals, $reason, $reasonData);
- }
- /**
- * Create a new rule for updating a package
- *
- * If package A1 can be updated to A2 or A3 the rule is (A1|A2|A3).
- *
- * @param PackageInterface $package The package to be updated
- * @param array $updates An array of update candidate packages
- * @param int $reason A RULE_* constant describing the
- * reason for generating this rule
- * @param mixed $reasonData Any data, e.g. the package name, that
- * goes with the reason
- * @return Rule The generated rule or null if tautology
- */
- protected function createUpdateRule(PackageInterface $package, array $updates, $reason, $reasonData = null)
- {
- $literals = array(new Literal($package, true));
- foreach ($updates as $update) {
- $literals[] = new Literal($update, true);
- }
- return new Rule($literals, $reason, $reasonData);
- }
- /**
- * Creates a new rule for installing a package
- *
- * The rule is simply (A) for a package A to be installed.
- *
- * @param PackageInterface $package The package to be installed
- * @param int $reason A RULE_* constant describing the
- * reason for generating this rule
- * @param mixed $reasonData Any data, e.g. the package name, that
- * goes with the reason
- * @return Rule The generated rule
- */
- public function createInstallRule(PackageInterface $package, $reason, $reasonData = null)
- {
- return new Rule(new Literal($package, true));
- }
- /**
- * Creates a rule to install at least one of a set of packages
- *
- * The rule is (A|B|C) with A, B and C different packages. If the given
- * set of packages is empty an impossible rule is generated.
- *
- * @param array $packages The set of packages to choose from
- * @param int $reason A RULE_* constant describing the reason for
- * generating this rule
- * @param mixed $reasonData Any data, e.g. the package name, that goes with
- * the reason
- * @return Rule The generated rule
- */
- public function createInstallOneOfRule(array $packages, $reason, $reasonData = null)
- {
- if (empty($packages)) {
- return $this->createImpossibleRule($reason, $reasonData);
- }
- $literals = array();
- foreach ($packages as $package) {
- $literals[] = new Literal($package, true);
- }
- return new Rule($literals, $reason, $reasonData);
- }
- /**
- * Creates a rule to remove a package
- *
- * The rule for a package A is (-A).
- *
- * @param PackageInterface $package The package to be removed
- * @param int $reason A RULE_* constant describing the
- * reason for generating this rule
- * @param mixed $reasonData Any data, e.g. the package name, that
- * goes with the reason
- * @return Rule The generated rule
- */
- public function createRemoveRule(PackageInterface $package, $reason, $reasonData = null)
- {
- return new Rule(array(new Literal($package, false)), $reason, $reasonData);
- }
- /**
- * Creates a rule for two conflicting packages
- *
- * The rule for conflicting packages A and B is (-A|-B). A is called the issuer
- * and B the provider.
- *
- * @param PackageInterface $issuer The package declaring the conflict
- * @param Package $provider The package causing the conflict
- * @param int $reason A RULE_* constant describing the
- * reason for generating this rule
- * @param mixed $reasonData Any data, e.g. the package name, that
- * goes with the reason
- * @return Rule The generated rule
- */
- public function createConflictRule(PackageInterface $issuer, Package $provider, $reason, $reasonData = null)
- {
- // ignore self conflict
- if ($issuer === $provider) {
- return null;
- }
- return new Rule(array(new Literal($issuer, false), new Literal($provider, false)), $reason, $reasonData);
- }
- /**
- * Intentionally creates a rule impossible to solve
- *
- * The rule is an empty one so it can never be satisfied.
- *
- * @param int $reason A RULE_* constant describing the reason for
- * generating this rule
- * @param mixed $reasonData Any data, e.g. the package name, that goes with
- * the reason
- * @return Rule An empty rule
- */
- public function createImpossibleRule($reason, $reasonData = null)
- {
- return new Rule(array(), $reason, $reasonData);
- }
- /**
- * Adds a rule unless it duplicates an existing one of any type
- *
- * To be able to directly pass in the result of one of the rule creation
- * methods the rule may also be null to indicate that no rule should be
- * added.
- *
- * @param int $type A TYPE_* constant defining the rule type
- * @param Rule $newRule The rule about to be added
- */
- private function addRule($type, Rule $newRule = null) {
- if ($newRule) {
- foreach ($this->rules as $rules) {
- foreach ($rules as $rule) {
- if ($rule->equals($newRule)) {
- return;
- }
- }
- }
- $newRule->setType($type);
- $this->rules[$type][] = $newRule;
- }
- }
- public function addRulesForPackage(PackageInterface $package)
- {
- $workQueue = new \SPLQueue;
- $workQueue->enqueue($package);
- while (!$workQueue->isEmpty()) {
- $package = $workQueue->dequeue();
- if (isset($this->addedMap[$package->getId()])) {
- continue;
- }
- $this->addedMap[$package->getId()] = true;
- $dontFix = 0;
- if ($this->installed === $package->getRepository() && !isset($this->fixMap[$package->getId()])) {
- $dontFix = 1;
- }
- if (!$dontFix && !$this->policy->installable($this, $this->pool, $this->installed, $package)) {
- $this->addRule(self::TYPE_PACKAGE, $this->createRemoveRule($package, self::RULE_NOT_INSTALLABLE, (string) $package));
- continue;
- }
- foreach ($package->getRequires() as $link) {
- $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
- // the strategy here is to not insist on dependencies
- // that are already broken. so if we find one provider
- // that was already installed, we know that the
- // dependency was not broken before so we enforce it
- if ($dontFix) {
- $foundInstalled = false;
- foreach ($possibleRequires as $require) {
- if ($this->installed === $require->getRepository()) {
- $foundInstalled = true;
- break;
- }
- }
- // no installed provider found: previously broken dependency => don't add rule
- if (!$foundInstalled) {
- continue;
- }
- }
- $this->addRule(self::TYPE_PACKAGE, $this->createRequireRule($package, $possibleRequires, self::RULE_PACKAGE_REQUIRES, (string) $link));
- foreach ($possibleRequires as $require) {
- $workQueue->enqueue($require);
- }
- }
- foreach ($package->getConflicts() as $link) {
- $possibleConflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
- foreach ($possibleConflicts as $conflict) {
- if ($dontfix && $this->installed === $conflict->getRepository()) {
- continue;
- }
- $this->addRule(self::TYPE_PACKAGE, $this->createConflictRule($package, $conflict, self::RULE_PACKAGE_CONFLICT, (string) $link));
- }
- }
- foreach ($package->getRecommends() as $link) {
- foreach ($this->pool->whatProvides($link->getTarget(), $link->getConstraint()) as $recommend) {
- $workQueue->enqueue($recommend);
- }
- }
- foreach ($package->getSuggests() as $link) {
- foreach ($this->pool->whatProvides($link->getTarget(), $link->getConstraint()) as $suggest) {
- $workQueue->enqueue($suggest);
- }
- }
- }
- }
- /**
- * Adds all rules for all update packages of a given package
- *
- * @param PackageInterface $package Rules for this package's updates are to
- * be added
- * @param bool $allowAll Whether downgrades are allowed
- */
- private function addRulesForUpdatePackages(PackageInterface $package, $allowAll)
- {
- $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, $allowAll);
- $this->addRulesForPackage($package);
- foreach ($updates as $update) {
- $this->addRulesForPackage($update);
- }
- }
- /**
- * Sets up watch chains for all rules.
- *
- * Next1/2 always points to the next rule that is watching the same package.
- * The watches array contains rules to start from for each package
- *
- */
- private function makeWatches()
- {
- foreach ($this->rules as $type => $rules) {
- foreach ($rules as $i => $rule) {
- // skip simple assertions of the form (A) or (-A)
- if ($rule->isAssertion()) {
- continue;
- }
- if (!isset($this->watches[$rule->watch1])) {
- $this->watches[$rule->watch1] = null;
- }
- $rule->next1 = $this->watches[$rule->watch1];
- $this->watches[$rule->watch1] = $rule;
- if (!isset($this->watches[$rule->watch2])) {
- $this->watches[$rule->watch2] = null;
- }
- $rule->next2 = $this->watches[$rule->watch2];
- $this->watches[$rule->watch2] = $rule;
- }
- }
- }
- private function findDecisionRule(PackageInterface $package)
- {
- foreach ($this->decisionQueue as $i => $literal) {
- if ($package === $literal->getPackage()) {
- return $this->decisionQueueWhy[$i];
- }
- }
- return null;
- }
- private function makeAssertionRuleDecisions()
- {
- // do we need to decide a SYSTEMSOLVABLE at level 1?
- foreach ($this->rules as $type => $rules) {
- if (self::TYPE_WEAK === $type) {
- continue;
- }
- foreach ($rules as $rule) {
- if (!$rule->isAssertion() || $rule->isDisabled()) {
- continue;
- }
- $literals = $rule->getLiterals();
- $literal = $literals[0];
- if (!$this->decided($literal->getPackage())) {
- $this->decisionQueue[] = $literal;
- $this->decisionQueueWhy[] = $rule;
- $this->addDecision($literal, 1);
- continue;
- }
- if ($this->decisionsSatisfy($literal)) {
- continue;
- }
- // found a conflict
- if (self::TYPE_LEARNED === $type) {
- $rule->disable();
- }
- $conflict = $this->findDecisionRule($literal->getPackage());
- // todo: handle conflict with systemsolvable?
- if ($conflict && self::TYPE_PACKAGE === $conflict->getType()) {
- }
- }
- }
- foreach ($this->rules[self::TYPE_WEAK] as $rule) {
- if (!$rule->isAssertion() || $rule->isDisabled()) {
- continue;
- }
- if ($this->decisionsSatisfy($literals[0])) {
- continue;
- }
- // conflict, but this is a weak rule => disable
- $rule->disable();
- }
- }
- public function solve(Request $request)
- {
- $this->jobs = $request->getJobs();
- $installedPackages = $this->installed->getPackages();
- foreach ($this->jobs as $job) {
- switch ($job['cmd']) {
- case 'update-all':
- foreach ($installedPackages as $package) {
- $this->updateMap[$package->getId()] = true;
- }
- break;
- case 'fix-all':
- foreach ($installedPackages as $package) {
- $this->fixMap[$package->getId()] = true;
- }
- break;
- }
- foreach ($job['packages'] as $package) {
- switch ($job['cmd']) {
- case 'fix':
- if ($this->installed === $package->getRepository()) {
- $this->fixMap[$package->getId()] = true;
- }
- break;
- case 'update':
- if ($this->installed === $package->getRepository()) {
- $this->updateMap[$package->getId()] = true;
- }
- break;
- }
- }
- }
- foreach ($installedPackages as $package) {
- $this->addRulesForPackage($package);
- }
- foreach ($installedPackages as $package) {
- $this->addRulesForUpdatePackages($package, true);
- }
- foreach ($this->jobs as $job) {
- foreach ($job['packages'] as $package) {
- switch ($job['cmd']) {
- case 'install':
- $this->installCandidateMap[$package->getId()] = true;
- $this->addRulesForPackage($package);
- break;
- }
- }
- }
- // solver_addrpmrulesforweak(solv, &addedmap);
- foreach ($installedPackages as $package) {
- // create a feature rule which allows downgrades
- $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, true);
- $featureRule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
- // create an update rule which does not allow downgrades
- $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, false);
- $rule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
- if ($rule->equals($featureRule)) {
- if ($this->policy->allowUninstall()) {
- $this->addRule(self::TYPE_WEAK, $featureRule);
- } else {
- $this->addRule(self::TYPE_UPDATE, $rule);
- }
- } else if ($this->policy->allowUninstall()) {
- $this->addRule(self::TYPE_WEAK, $featureRule);
- $this->addRule(self::TYPE_WEAK, $rule);
- }
- }
- foreach ($this->jobs as $job) {
- switch ($job['cmd']) {
- case 'install':
- $rule = $this->createInstallOneOfRule($job['packages'], self::RULE_JOB_INSTALL, $job['packageName']);
- $this->addRule(self::TYPE_JOB, $rule);
- //$this->ruleToJob[$rule] = $job;
- break;
- case 'remove':
- // remove all packages with this name including uninstalled
- // ones to make sure none of them are picked as replacements
- // todo: cleandeps
- foreach ($job['packages'] as $package) {
- $rule = $this->createRemoveRule($package, self::RULE_JOB_REMOVE);
- $this->addRule(self::TYPE_JOB, $rule);
- //$this->ruleToJob[$rule] = $job;
- }
- break;
- case 'lock':
- foreach ($job['packages'] as $package) {
- if ($this->installed === $package->getRepository()) {
- $rule = $this->createInstallRule($package, self::RULE_JOB_LOCK);
- } else {
- $rule = $this->createRemoveRule($package, self::RULE_JOB_LOCK);
- }
- $this->addRule(self::TYPE_JOB, $rule);
- //$this->ruleToJob[$rule] = $job;
- }
- break;
- }
- }
- // solver_addchoicerules(solv);
- $this->makeWatches();
- /* disable update rules that conflict with our job */
- //solver_disablepolicyrules(solv);
- /* make decisions based on job/update assertions */
- $this->makeAssertionRuleDecisions();
- $installRecommended = 0;
- $this->runSat(true, $installRecommended);
- //findrecommendedsuggested(solv);
- //solver_prepare_solutions(solv);
- $transaction = array();
- foreach ($this->decisionQueue as $literal) {
- $package = $literal->getPackage();
- // wanted & installed || !wanted & !installed
- if ($literal->isWanted() == ($this->installed == $package->getRepository())) {
- continue;
- }
- $transaction[] = array(
- 'job' => ($literal->isWanted()) ? 'install' : 'remove',
- 'package' => $package,
- );
- }
- return $transaction;
- }
- public function printRules()
- {
- print "\n";
- foreach ($this->rules as $type => $rules) {
- print str_pad(self::$types[$type], 8, ' ') . ": ";
- foreach ($rules as $rule) {
- print $rule;
- }
- print "\n";
- }
- }
- protected $decisionQueue = array();
- protected $propagateIndex;
- protected $decisionMap = array();
- protected $branches = array();
- protected function literalFromId($id)
- {
- $package = $this->pool->packageById($id);
- return new Literal($package, $id > 0);
- }
- protected function addDecision(Literal $l, $level)
- {
- if ($l->isWanted()) {
- $this->decisionMap[$l->getPackageId()] = $level;
- } else {
- $this->decisionMap[$l->getPackageId()] = -$level;
- }
- }
- protected function addDecisionId($literalId, $level)
- {
- $packageId = abs($literalId);
- if ($literalId > 0) {
- $this->decisionMap[$packageId] = $level;
- } else {
- $this->decisionMap[$packageId] = -$level;
- }
- }
- protected function decisionsContain(Literal $l)
- {
- return (isset($this->decisionMap[$l->getPackageId()]) && (
- $this->decisionMap[$l->getPackageId()] > 0 && $l->isWanted() ||
- $this->decisionMap[$l->getPackageId()] < 0 && !$l->isWanted()
- ));
- }
- protected function decisionsContainId($literalId)
- {
- $packageId = abs($literalId);
- return (isset($this->decisionMap[$packageId]) && (
- $this->decisionMap[$packageId] > 0 && $literalId > 0 ||
- $this->decisionMap[$packageId] < 0 && $literalId < 0
- ));
- }
- protected function decisionsSatisfy(Literal $l)
- {
- return ($l->isWanted() && isset($this->decisionMap[$l->getPackageId()]) && $this->decisionMap[$l->getPackageId()] > 0) ||
- (!$l->isWanted() && (!isset($this->decisionMap[$l->getPackageId()]) || $this->decisionMap[$l->getPackageId()] < 0));
- }
- protected function decisionsConflict(Literal $l)
- {
- return (isset($this->decisionMap[$l->getPackageId()]) && (
- $this->decisionMap[$l->getPackageId()] > 0 && !$l->isWanted() ||
- $this->decisionMap[$l->getPackageId()] < 0 && $l->isWanted()
- ));
- }
- protected function decisionsConflictId($literalId)
- {
- $packageId = abs($literalId);
- return (isset($this->decisionMap[$packageId]) && (
- $this->decisionMap[$packageId] > 0 && !$literalId < 0 ||
- $this->decisionMap[$packageId] < 0 && $literalId > 0
- ));
- }
- protected function decided(PackageInterface $p)
- {
- return isset($this->decisionMap[$p->getId()]);
- }
- protected function undecided(PackageInterface $p)
- {
- return !isset($this->decisionMap[$p->getId()]);
- }
- protected function decidedInstall(PackageInterface $p) {
- return isset($this->decisionMap[$p->getId()]) && $this->decisionMap[$p->getId()] > 0;
- }
- protected function decidedRemove(PackageInterface $p) {
- return isset($this->decisionMap[$p->getId()]) && $this->decisionMap[$p->getId()] < 0;
- }
- /**
- * Makes a decision and propagates it to all rules.
- *
- * Evaluates each term affected by the decision (linked through watches)
- * If we find unit rules we make new decisions based on them
- *
- * @return Rule|null A rule on conflict, otherwise null.
- */
- protected function propagate($level)
- {
- while ($this->propagateIndex < count($this->decisionQueue)) {
- // we invert the decided literal here, example:
- // A was decided => (-A|B) now requires B to be true, so we look for
- // rules which are fulfilled by -A, rather than A.
- $literal = $this->decisionQueue[$this->propagateIndex]->inverted();
- $this->propagateIndex++;
- // /* foreach rule where 'pkg' is now FALSE */
- //for (rp = watches + pkg; *rp; rp = next_rp)
- for ($rule = $this->watches[$literal->getId()]; $rule !== null; $rule = $nextRule) {
- $nextRule = $rule->getNext($literal);
- if ($rule->isDisabled()) {
- continue;
- }
- $otherWatch = $rule->getOtherWatch($literal);
- if ($this->decisionsContainId($otherWatch)) {
- continue;
- }
- $ruleLiterals = $rule->getLiterals();
- if (sizeof($ruleLiterals) > 2) {
- foreach ($ruleLiterals as $ruleLiteral) {
- if (!$otherWatch->equals($ruleLiteral) &&
- !$this->decisionsConflict($ruleLiteral)) {
- if ($literal->equals($rule->getWatch1())) {
- $rule->setWatch1($ruleLiteral);
- $rule->setNext1($rule);
- } else {
- $rule->setWatch2($ruleLiteral);
- $rule->setNext2($rule);
- }
- $this->watches[$ruleLiteral->getId()] = $rule;
- continue 2;
- }
- }
- }
- // yay, we found a unit clause! try setting it to true
- if ($this->decisionsConflictId($otherWatch)) {
- return $rule;
- }
- $this->addDecisionId($otherWatch, $level);
- $this->decisionQueue[] = $this->literalFromId($otherWatch);
- $this->decisionQueueWhy[] = $rule;
- }
- }
- return null;
- }
- private function setPropagateLearn($level, Literal $literal, $disableRules, Rule $rule)
- {
- return 0;
- }
- private function selectAndInstall($level, array $decisionQueue, $disableRules, Rule $rule)
- {
- // choose best package to install from decisionQueue
- $literals = $this->policy->selectPreferedPackages($decisionQueue);
- // if there are multiple candidates, then branch
- if (count($literals) > 1) {
- foreach ($literals as $i => $literal) {
- if (0 !== $i) {
- $this->branches[] = array($literal, $level);
- }
- }
- }
- return $this->setPropagateLearn($level, $literals[0], $disableRules, $rule);
- }
- private function analyzeUnsolvable($conflictRule, $disableRules)
- {
- return false;
- }
- private function runSat($disableRules = true, $installRecommended = false)
- {
- $this->propagateIndex = 0;
- // /*
- // * 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
- // */
- $decisionQueue = array();
- $decisionSupplementQueue = array();
- $disableRules = array();
- $level = 1;
- $systemLevel = $level + 1;
- $minimizationsteps = 0;
- $installedPos = 0;
- $this->installedPackages = $this->installed->getPackages();
- while (true) {
- $conflictRule = $this->propagate($level);
- if ($conflictRule !== null) {
- // if (analyze_unsolvable(solv, r, disablerules))
- if ($this->analyzeUnsolvable($conflictRule, $disableRules)) {
- continue;
- } else {
- return;
- }
- }
- // handle job rules
- if ($level < $systemLevel) {
- $ruleIndex = 0;
- foreach ($this->rules[self::TYPE_JOB] as $ruleIndex => $rule) {
- if ($rule->isEnabled()) {
- $decisionQueue = array();
- $noneSatisfied = true;
- foreach ($rule->getLiterals() as $literal) {
- if ($this->decisionsSatisfy($literal)) {
- $noneSatisfied = false;
- break;
- }
- $decisionQueue[] = $literal;
- }
- if ($noneSatisfied && count($decisionQueue)) {
- // prune all update packages until installed version
- // except for requested updates
- if (count($this->installed) != count($this->updateMap)) {
- $prunedQueue = array();
- foreach ($decisionQueue as $literal) {
- if ($this->installed === $literal->getPackage()->getRepository()) {
- $prunedQueue[] = $literal;
- if (isset($this->updateMap[$literal->getPackageId()])) {
- $prunedQueue = $decisionQueue;
- break;
- }
- }
- }
- $decisionQueue = $prunedQueue;
- }
- }
- if ($noneSatisfied && count($decisionQueue)) {
- $oLevel = $level;
- $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
- if (0 === $level) {
- return;
- }
- if ($level <= $oLevel) {
- break;
- }
- }
- }
- }
- $systemLevel = $level + 1;
- // jobs left
- if ($ruleIndex + 1 < count($this->rules[Solver::TYPE_JOB])) {
- continue;
- }
- }
- // 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->rules[Solver::TYPE_UPDATE][$literal->getPackageId()])) {
- $rule = $this->rules[Solver::TYPE_UPDATE][$literal->getPackageId()];
- }
- if ((!$rule || $rule->isDisabled()) && isset($this->rules[Solver::TYPE_FEATURE][$literal->getPackageId()])) {
- $rule = $this->rules[Solver::TYPE_FEATURE][$literal->getPackageId()];
- }
- if (!$rule || $rule->isDisabled()) {
- continue;
- }
- $decisionQueue = array();
- if (!isset($this->noUpdate[$literal->getPackageId()]) && (
- $this->decidedRemove($literal->getPackage()) ||
- isset($this->updateMap[$literal->getPackageId()]) ||
- !$literal->equals($rule->getFirstLiteral())
- )) {
- foreach ($rule->getLiterals() as $ruleLiteral) {
- if ($this->decidedInstall($ruleLiteral->getPackage())) {
- // already fulfilled
- 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;
- }
- }
- // still undecided? keep package.
- if (!$repeat && $this->undecided($literal->getPackage())) {
- $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;
- }
- foreach ($this->rules as $ruleType => $rules) {
- foreach ($rules as $rule) {
- if ($rule->isEnabled()) {
- $decisionQueue = array();
- }
- }
- }
- //
- // /*
- // * decide
- // */
- // POOL_DEBUG(SAT_DEBUG_POLICY, "deciding unresolved rules\n");
- // for (i = 1, n = 1; n < solv->nrules; i++, n++)
- // {
- // if (i == solv->nrules)
- // i = 1;
- // r = solv->rules + i;
- // if (r->d < 0) /* ignore disabled rules */
- // continue;
- // queue_empty(&dq);
- // if (r->d == 0)
- // {
- // /* binary or unary rule */
- // /* need two positive undecided literals */
- // if (r->p < 0 || r->w2 <= 0)
- // continue;
- // if (solv->decisionmap[r->p] || solv->decisionmap[r->w2])
- // continue;
- // queue_push(&dq, r->p);
- // queue_push(&dq, r->w2);
- // }
- // else
- // {
- // /* make sure that
- // * all negative literals are installed
- // * no positive literal is installed
- // * i.e. the rule is not fulfilled and we
- // * just need to decide on the positive literals
- // */
- // if (r->p < 0)
- // {
- // if (solv->decisionmap[-r->p] <= 0)
- // continue;
- // }
- // else
- // {
- // if (solv->decisionmap[r->p] > 0)
- // continue;
- // if (solv->decisionmap[r->p] == 0)
- // queue_push(&dq, r->p);
- // }
- // dp = pool->whatprovidesdata + r->d;
- // while ((p = *dp++) != 0)
- // {
- // if (p < 0)
- // {
- // if (solv->decisionmap[-p] <= 0)
- // break;
- // }
- // else
- // {
- // if (solv->decisionmap[p] > 0)
- // break;
- // if (solv->decisionmap[p] == 0)
- // queue_push(&dq, p);
- // }
- // }
- // if (p)
- // continue;
- // }
- // IF_POOLDEBUG (SAT_DEBUG_PROPAGATE)
- // {
- // POOL_DEBUG(SAT_DEBUG_PROPAGATE, "unfulfilled ");
- // solver_printruleclass(solv, SAT_DEBUG_PROPAGATE, r);
- // }
- // /* dq.count < 2 cannot happen as this means that
- // * the rule is unit */
- // assert(dq.count > 1);
- //
- // olevel = level;
- // level = selectandinstall(solv, level, &dq, disablerules, r - solv->rules);
- // if (level == 0)
- // {
- // queue_free(&dq);
- // queue_free(&dqs);
- // return;
- // }
- // if (level < systemlevel || level == 1)
- // break; /* trouble */
- // /* something changed, so look at all rules again */
- // n = 0;
- // }
- //
- // if (n != solv->nrules) /* ran into trouble, restart */
- // continue;
- //
- // /* at this point we have a consistent system. now do the extras... */
- //
- }
- }
- }
|