Solver.php 70 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\DependencyResolver;
  12. use Composer\Repository\RepositoryInterface;
  13. use Composer\Package\PackageInterface;
  14. /**
  15. * @author Nils Adermann <naderman@naderman.de>
  16. */
  17. class Solver
  18. {
  19. const RULE_INTERNAL_ALLOW_UPDATE = 1;
  20. const RULE_JOB_INSTALL = 2;
  21. const RULE_JOB_REMOVE = 3;
  22. const RULE_JOB_LOCK = 4;
  23. const RULE_NOT_INSTALLABLE = 5;
  24. const RULE_NOTHING_PROVIDES_DEP = 6;
  25. const RULE_PACKAGE_CONFLICT = 7;
  26. const RULE_PACKAGE_NOT_EXIST = 8;
  27. const RULE_PACKAGE_REQUIRES = 9;
  28. const RULE_PACKAGE_OBSOLETES = 10;
  29. const RULE_INSTALLED_PACKAGE_OBSOLETES = 11;
  30. const RULE_PACKAGE_SAME_NAME = 12;
  31. const RULE_PACKAGE_IMPLICIT_OBSOLETES = 13;
  32. const RULE_LEARNED = 14;
  33. protected $policy;
  34. protected $pool;
  35. protected $installed;
  36. protected $rules;
  37. protected $updateAll;
  38. protected $ruleToJob = array();
  39. protected $addedMap = array();
  40. protected $fixMap = array();
  41. protected $updateMap = array();
  42. protected $noObsoletes = array();
  43. protected $watches = array();
  44. protected $removeWatches = array();
  45. protected $decisionMap;
  46. protected $packageToUpdateRule = array();
  47. protected $packageToFeatureRule = array();
  48. public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
  49. {
  50. $this->policy = $policy;
  51. $this->pool = $pool;
  52. $this->installed = $installed;
  53. $this->rules = new RuleSet;
  54. }
  55. /**
  56. * Creates a new rule for the requirements of a package
  57. *
  58. * This rule is of the form (-A|B|C), where B and C are the providers of
  59. * one requirement of the package A.
  60. *
  61. * @param PackageInterface $package The package with a requirement
  62. * @param array $providers The providers of the requirement
  63. * @param int $reason A RULE_* constant describing the
  64. * reason for generating this rule
  65. * @param mixed $reasonData Any data, e.g. the requirement name,
  66. * that goes with the reason
  67. * @return Rule The generated rule or null if tautological
  68. */
  69. public function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null)
  70. {
  71. $literals = array(new Literal($package, false));
  72. foreach ($providers as $provider) {
  73. // self fulfilling rule?
  74. if ($provider === $package) {
  75. return null;
  76. }
  77. $literals[] = new Literal($provider, true);
  78. }
  79. return new Rule($literals, $reason, $reasonData);
  80. }
  81. /**
  82. * Create a new rule for updating a package
  83. *
  84. * If package A1 can be updated to A2 or A3 the rule is (A1|A2|A3).
  85. *
  86. * @param PackageInterface $package The package to be updated
  87. * @param array $updates An array of update candidate packages
  88. * @param int $reason A RULE_* constant describing the
  89. * reason for generating this rule
  90. * @param mixed $reasonData Any data, e.g. the package name, that
  91. * goes with the reason
  92. * @return Rule The generated rule or null if tautology
  93. */
  94. protected function createUpdateRule(PackageInterface $package, array $updates, $reason, $reasonData = null)
  95. {
  96. $literals = array(new Literal($package, true));
  97. foreach ($updates as $update) {
  98. $literals[] = new Literal($update, true);
  99. }
  100. return new Rule($literals, $reason, $reasonData);
  101. }
  102. /**
  103. * Creates a new rule for installing a package
  104. *
  105. * The rule is simply (A) for a package A to be installed.
  106. *
  107. * @param PackageInterface $package The package to be installed
  108. * @param int $reason A RULE_* constant describing the
  109. * reason for generating this rule
  110. * @param mixed $reasonData Any data, e.g. the package name, that
  111. * goes with the reason
  112. * @return Rule The generated rule
  113. */
  114. public function createInstallRule(PackageInterface $package, $reason, $reasonData = null)
  115. {
  116. return new Rule(new Literal($package, true));
  117. }
  118. /**
  119. * Creates a rule to install at least one of a set of packages
  120. *
  121. * The rule is (A|B|C) with A, B and C different packages. If the given
  122. * set of packages is empty an impossible rule is generated.
  123. *
  124. * @param array $packages The set of packages to choose from
  125. * @param int $reason A RULE_* constant describing the reason for
  126. * generating this rule
  127. * @param mixed $reasonData Any data, e.g. the package name, that goes with
  128. * the reason
  129. * @return Rule The generated rule
  130. */
  131. public function createInstallOneOfRule(array $packages, $reason, $reasonData = null)
  132. {
  133. if (empty($packages)) {
  134. return $this->createImpossibleRule($reason, $reasonData);
  135. }
  136. $literals = array();
  137. foreach ($packages as $package) {
  138. $literals[] = new Literal($package, true);
  139. }
  140. return new Rule($literals, $reason, $reasonData);
  141. }
  142. /**
  143. * Creates a rule to remove a package
  144. *
  145. * The rule for a package A is (-A).
  146. *
  147. * @param PackageInterface $package The package to be removed
  148. * @param int $reason A RULE_* constant describing the
  149. * reason for generating this rule
  150. * @param mixed $reasonData Any data, e.g. the package name, that
  151. * goes with the reason
  152. * @return Rule The generated rule
  153. */
  154. public function createRemoveRule(PackageInterface $package, $reason, $reasonData = null)
  155. {
  156. return new Rule(array(new Literal($package, false)), $reason, $reasonData);
  157. }
  158. /**
  159. * Creates a rule for two conflicting packages
  160. *
  161. * The rule for conflicting packages A and B is (-A|-B). A is called the issuer
  162. * and B the provider.
  163. *
  164. * @param PackageInterface $issuer The package declaring the conflict
  165. * @param Package $provider The package causing the conflict
  166. * @param int $reason A RULE_* constant describing the
  167. * reason for generating this rule
  168. * @param mixed $reasonData Any data, e.g. the package name, that
  169. * goes with the reason
  170. * @return Rule The generated rule
  171. */
  172. public function createConflictRule(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null)
  173. {
  174. // ignore self conflict
  175. if ($issuer === $provider) {
  176. return null;
  177. }
  178. return new Rule(array(new Literal($issuer, false), new Literal($provider, false)), $reason, $reasonData);
  179. }
  180. /**
  181. * Intentionally creates a rule impossible to solve
  182. *
  183. * The rule is an empty one so it can never be satisfied.
  184. *
  185. * @param int $reason A RULE_* constant describing the reason for
  186. * generating this rule
  187. * @param mixed $reasonData Any data, e.g. the package name, that goes with
  188. * the reason
  189. * @return Rule An empty rule
  190. */
  191. public function createImpossibleRule($reason, $reasonData = null)
  192. {
  193. return new Rule(array(), $reason, $reasonData);
  194. }
  195. /**
  196. * Adds a rule unless it duplicates an existing one of any type
  197. *
  198. * To be able to directly pass in the result of one of the rule creation
  199. * methods the rule may also be null to indicate that no rule should be
  200. * added.
  201. *
  202. * @param int $type A TYPE_* constant defining the rule type
  203. * @param Rule $newRule The rule about to be added
  204. */
  205. private function addRule($type, Rule $newRule = null) {
  206. if ($newRule) {
  207. foreach ($this->rules->getIterator() as $rule) {
  208. if ($rule->equals($newRule)) {
  209. return;
  210. }
  211. }
  212. $this->rules->add($newRule, $type);
  213. }
  214. }
  215. public function addRulesForPackage(PackageInterface $package)
  216. {
  217. $workQueue = new \SPLQueue;
  218. $workQueue->enqueue($package);
  219. while (!$workQueue->isEmpty()) {
  220. $package = $workQueue->dequeue();
  221. if (isset($this->addedMap[$package->getId()])) {
  222. continue;
  223. }
  224. $this->addedMap[$package->getId()] = true;
  225. $dontFix = 0;
  226. if ($this->installed === $package->getRepository() && !isset($this->fixMap[$package->getId()])) {
  227. $dontFix = 1;
  228. }
  229. if (!$dontFix && !$this->policy->installable($this, $this->pool, $this->installed, $package)) {
  230. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRemoveRule($package, self::RULE_NOT_INSTALLABLE, (string) $package));
  231. continue;
  232. }
  233. foreach ($package->getRequires() as $link) {
  234. $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  235. // the strategy here is to not insist on dependencies
  236. // that are already broken. so if we find one provider
  237. // that was already installed, we know that the
  238. // dependency was not broken before so we enforce it
  239. if ($dontFix) {
  240. $foundInstalled = false;
  241. foreach ($possibleRequires as $require) {
  242. if ($this->installed === $require->getRepository()) {
  243. $foundInstalled = true;
  244. break;
  245. }
  246. }
  247. // no installed provider found: previously broken dependency => don't add rule
  248. if (!$foundInstalled) {
  249. continue;
  250. }
  251. }
  252. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, $possibleRequires, self::RULE_PACKAGE_REQUIRES, (string) $link));
  253. foreach ($possibleRequires as $require) {
  254. $workQueue->enqueue($require);
  255. }
  256. }
  257. foreach ($package->getConflicts() as $link) {
  258. $possibleConflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  259. foreach ($possibleConflicts as $conflict) {
  260. if ($dontFix && $this->installed === $conflict->getRepository()) {
  261. continue;
  262. }
  263. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $conflict, self::RULE_PACKAGE_CONFLICT, (string) $link));
  264. }
  265. }
  266. // check obsoletes and implicit obsoletes of a package
  267. // if ignoreinstalledsobsoletes is not set, we're also checking
  268. // obsoletes of installed packages (like newer rpm versions)
  269. //
  270. /** @TODO: if ($this->noInstalledObsoletes) */
  271. if (true) {
  272. $noObsoletes = isset($this->noObsoletes[$package->getId()]);
  273. $isInstalled = ($this->installed === $package->getRepository());
  274. foreach ($package->getReplaces() as $link) {
  275. $obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  276. foreach ($obsoleteProviders as $provider) {
  277. if ($provider === $package) {
  278. continue;
  279. }
  280. if ($isInstalled && $dontFix && $this->installed === $provider->getRepository()) {
  281. continue; // don't repair installed/installed problems
  282. }
  283. $reason = ($isInstalled) ? self::RULE_INSTALLED_PACKAGE_OBSOLETES : self::RULE_PACKAGE_OBSOLETES;
  284. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link));
  285. }
  286. }
  287. // check implicit obsoletes
  288. // for installed packages we only need to check installed/installed problems (and
  289. // only when dontFix is not set), as the others are picked up when looking at the
  290. // uninstalled package.
  291. if (!$isInstalled || !$dontFix) {
  292. $obsoleteProviders = $this->pool->whatProvides($package->getName(), null);
  293. foreach ($obsoleteProviders as $provider) {
  294. if ($provider === $package) {
  295. continue;
  296. }
  297. if ($isInstalled && $this->installed !== $provider->getRepository()) {
  298. continue;
  299. }
  300. // obsolete same packages even when noObsoletes
  301. if ($noObsoletes && (!$package->equals($provider))) {
  302. continue;
  303. }
  304. $reason = ($package->getName() == $provider->getName()) ? self::RULE_PACKAGE_SAME_NAME : self::RULE_PACKAGE_IMPLICIT_OBSOLETES;
  305. $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, (string) $package));
  306. }
  307. }
  308. }
  309. foreach ($package->getRecommends() as $link) {
  310. foreach ($this->pool->whatProvides($link->getTarget(), $link->getConstraint()) as $recommend) {
  311. $workQueue->enqueue($recommend);
  312. }
  313. }
  314. foreach ($package->getSuggests() as $link) {
  315. foreach ($this->pool->whatProvides($link->getTarget(), $link->getConstraint()) as $suggest) {
  316. $workQueue->enqueue($suggest);
  317. }
  318. }
  319. }
  320. }
  321. /**
  322. * Adds all rules for all update packages of a given package
  323. *
  324. * @param PackageInterface $package Rules for this package's updates are to
  325. * be added
  326. * @param bool $allowAll Whether downgrades are allowed
  327. */
  328. private function addRulesForUpdatePackages(PackageInterface $package, $allowAll)
  329. {
  330. $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, $allowAll);
  331. $this->addRulesForPackage($package);
  332. foreach ($updates as $update) {
  333. $this->addRulesForPackage($update);
  334. }
  335. }
  336. /**
  337. * Alters watch chains for a rule.
  338. *
  339. * Next1/2 always points to the next rule that is watching the same package.
  340. * The watches array contains rules to start from for each package
  341. *
  342. */
  343. private function addWatchesToRule(Rule $rule)
  344. {
  345. // skip simple assertions of the form (A) or (-A)
  346. if ($rule->isAssertion()) {
  347. return;
  348. }
  349. if (!isset($this->watches[$rule->watch1])) {
  350. $this->watches[$rule->watch1] = null;
  351. }
  352. $rule->next1 = $this->watches[$rule->watch1];
  353. $this->watches[$rule->watch1] = $rule;
  354. if (!isset($this->watches[$rule->watch2])) {
  355. $this->watches[$rule->watch2] = null;
  356. }
  357. $rule->next2 = $this->watches[$rule->watch2];
  358. $this->watches[$rule->watch2] = $rule;
  359. }
  360. /**
  361. * Put watch2 on rule's literal with highest level
  362. */
  363. private function watch2OnHighest(Rule $rule)
  364. {
  365. $literals = $rule->getLiterals();
  366. // if there are only 2 elements, both are being watched anyway
  367. if ($literals < 3) {
  368. return;
  369. }
  370. $watchLevel = 0;
  371. foreach ($literals as $literal) {
  372. $level = abs($this->decisionMap[$literal->getPackageId()]);
  373. if ($level > $watchLevel) {
  374. $rule->watch2 = $literal->getId();
  375. $watchLevel = $level;
  376. }
  377. }
  378. }
  379. private function findDecisionRule(PackageInterface $package)
  380. {
  381. foreach ($this->decisionQueue as $i => $literal) {
  382. if ($package === $literal->getPackage()) {
  383. return $this->decisionQueueWhy[$i];
  384. }
  385. }
  386. return null;
  387. }
  388. // aka solver_makeruledecisions
  389. private function makeAssertionRuleDecisions()
  390. {
  391. // do we need to decide a SYSTEMSOLVABLE at level 1?
  392. $decisionStart = count($this->decisionQueue);
  393. for ($ruleIndex = 0; $ruleIndex < count($this->rules); $ruleIndex++) {
  394. $rule = $this->rules->ruleById($ruleIndex);
  395. if ($rule->isWeak() || !$rule->isAssertion() || $rule->isDisabled()) {
  396. continue;
  397. }
  398. $literals = $rule->getLiterals();
  399. $literal = $literals[0];
  400. if (!$this->decided($literal->getPackage())) {
  401. $this->decisionQueue[] = $literal;
  402. $this->decisionQueueWhy[] = $rule;
  403. $this->addDecision($literal, 1);
  404. continue;
  405. }
  406. if ($this->decisionsSatisfy($literal)) {
  407. continue;
  408. }
  409. // found a conflict
  410. if (RuleSet::TYPE_LEARNED === $rule->getType()) {
  411. $rule->disable();
  412. continue;
  413. }
  414. $conflict = $this->findDecisionRule($literal->getPackage());
  415. /** TODO: handle conflict with systemsolvable? */
  416. $this->learnedPool[] = array($rule, $conflict);
  417. if ($conflict && RuleSet::TYPE_PACKAGE === $conflict->getType()) {
  418. if ($rule->getType() == RuleSet::TYPE_JOB) {
  419. $why = $this->ruleToJob[$rule->getId()];
  420. } else {
  421. $why = $rule;
  422. }
  423. $this->problems[] = array($why);
  424. $this->disableProblem($why);
  425. continue;
  426. }
  427. // conflict with another job or update/feature rule
  428. $this->problems[] = array();
  429. // push all of our rules (can only be feature or job rules)
  430. // asserting this literal on the problem stack
  431. foreach ($this->rules->getIteratorFor(array(RuleSet::TYPE_JOB, RuleSet::TYPE_UPDATE, RuleSet::TYPE_FEATURE)) as $assertRule) {
  432. if ($assertRule->isDisabled() || !$assertRule->isAssertion() || $assertRule->isWeak()) {
  433. continue;
  434. }
  435. $assertRuleLiterals = $assertRule->getLiterals();
  436. $assertRuleLiteral = $assertRuleLiterals[0];
  437. if ($literal->getPackageId() !== $assertRuleLiteral->getPackageId()) {
  438. continue;
  439. }
  440. if ($assertRule->getType() === RuleSet::TYPE_JOB) {
  441. $why = $this->ruleToJob[$assertRule->getId()];
  442. } else {
  443. $why = $assertRule;
  444. }
  445. $this->problems[count($this->problems) - 1][] = $why;
  446. $this->disableProblem($why);
  447. }
  448. // start over
  449. while (count($this->decisionQueue) > $decisionStart) {
  450. $decisionLiteral = array_pop($this->decisionQueue);
  451. array_pop($this->decisionQueueWhy);
  452. unset($this->decisionQueueFree[count($this->decisionQueue)]);
  453. $this->decisionMap[$decisionLiteral->getPackageId()] = 0;
  454. }
  455. $ruleIndex = -1;
  456. }
  457. foreach ($this->rules as $rule) {
  458. if (!$rule->isWeak() || !$rule->isAssertion() || $rule->isDisabled()) {
  459. continue;
  460. }
  461. $literals = $rule->getLiterals();
  462. $literal = $literals[0];
  463. if ($this->decisionMap[$literal->getPackageId()] == 0) {
  464. $this->decisionQueue[] = $literal;
  465. $this->decisionQueueWhy[] = $rule;
  466. $this->addDecision($literal, 1);
  467. continue;
  468. }
  469. if ($this->decisionsSatisfy($literals[0])) {
  470. continue;
  471. }
  472. // conflict, but this is a weak rule => disable
  473. if ($rule->getType() == RuleSet::TYPE_JOB) {
  474. $why = $this->ruleToJob[$rule->getId()];
  475. } else {
  476. $why = $rule;
  477. }
  478. $this->disableProblem($why);
  479. /** TODO solver_reenablepolicyrules(solv, -(v + 1)); */
  480. }
  481. }
  482. public function addChoiceRules()
  483. {
  484. // void
  485. // solver_addchoicerules(Solver *solv)
  486. // {
  487. // Pool *pool = solv->pool;
  488. // Map m, mneg;
  489. // Rule *r;
  490. // Queue q, qi;
  491. // int i, j, rid, havechoice;
  492. // Id p, d, *pp;
  493. // Id p2, pp2;
  494. // Solvable *s, *s2;
  495. //
  496. // solv->choicerules = solv->nrules;
  497. // if (!pool->installed)
  498. // {
  499. // solv->choicerules_end = solv->nrules;
  500. // return;
  501. // }
  502. // solv->choicerules_ref = sat_calloc(solv->rpmrules_end, sizeof(Id));
  503. // queue_init(&q);
  504. // queue_init(&qi);
  505. // map_init(&m, pool->nsolvables);
  506. // map_init(&mneg, pool->nsolvables);
  507. // /* set up negative assertion map from infarch and dup rules */
  508. // for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++)
  509. // if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
  510. // MAPSET(&mneg, -r->p);
  511. // for (rid = solv->duprules, r = solv->rules + rid; rid < solv->duprules_end; rid++, r++)
  512. // if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
  513. // MAPSET(&mneg, -r->p);
  514. // for (rid = 1; rid < solv->rpmrules_end ; rid++)
  515. // {
  516. // r = solv->rules + rid;
  517. // if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0))
  518. // continue; /* only look at requires rules */
  519. // // solver_printrule(solv, SAT_DEBUG_RESULT, r);
  520. // queue_empty(&q);
  521. // queue_empty(&qi);
  522. // havechoice = 0;
  523. // FOR_RULELITERALS(p, pp, r)
  524. // {
  525. // if (p < 0)
  526. // continue;
  527. // s = pool->solvables + p;
  528. // if (!s->repo)
  529. // continue;
  530. // if (s->repo == pool->installed)
  531. // {
  532. // queue_push(&q, p);
  533. // continue;
  534. // }
  535. // /* check if this package is "blocked" by a installed package */
  536. // s2 = 0;
  537. // FOR_PROVIDES(p2, pp2, s->name)
  538. // {
  539. // s2 = pool->solvables + p2;
  540. // if (s2->repo != pool->installed)
  541. // continue;
  542. // if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
  543. // continue;
  544. // if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
  545. // continue;
  546. // break;
  547. // }
  548. // if (p2)
  549. // {
  550. // /* found installed package p2 that we can update to p */
  551. // if (MAPTST(&mneg, p))
  552. // continue;
  553. // if (policy_is_illegal(solv, s2, s, 0))
  554. // continue;
  555. // queue_push(&qi, p2);
  556. // queue_push(&q, p);
  557. // continue;
  558. // }
  559. // if (s->obsoletes)
  560. // {
  561. // Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
  562. // s2 = 0;
  563. // while ((obs = *obsp++) != 0)
  564. // {
  565. // FOR_PROVIDES(p2, pp2, obs)
  566. // {
  567. // s2 = pool->solvables + p2;
  568. // if (s2->repo != pool->installed)
  569. // continue;
  570. // if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
  571. // continue;
  572. // if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
  573. // continue;
  574. // break;
  575. // }
  576. // if (p2)
  577. // break;
  578. // }
  579. // if (obs)
  580. // {
  581. // /* found installed package p2 that we can update to p */
  582. // if (MAPTST(&mneg, p))
  583. // continue;
  584. // if (policy_is_illegal(solv, s2, s, 0))
  585. // continue;
  586. // queue_push(&qi, p2);
  587. // queue_push(&q, p);
  588. // continue;
  589. // }
  590. // }
  591. // /* package p is independent of the installed ones */
  592. // havechoice = 1;
  593. // }
  594. // if (!havechoice || !q.count)
  595. // continue; /* no choice */
  596. //
  597. // /* now check the update rules of the installed package.
  598. // * if all packages of the update rules are contained in
  599. // * the dependency rules, there's no need to set up the choice rule */
  600. // map_empty(&m);
  601. // FOR_RULELITERALS(p, pp, r)
  602. // if (p > 0)
  603. // MAPSET(&m, p);
  604. // for (i = 0; i < qi.count; i++)
  605. // {
  606. // if (!qi.elements[i])
  607. // continue;
  608. // Rule *ur = solv->rules + solv->updaterules + (qi.elements[i] - pool->installed->start);
  609. // if (!ur->p)
  610. // ur = solv->rules + solv->featurerules + (qi.elements[i] - pool->installed->start);
  611. // if (!ur->p)
  612. // continue;
  613. // FOR_RULELITERALS(p, pp, ur)
  614. // if (!MAPTST(&m, p))
  615. // break;
  616. // if (p)
  617. // break;
  618. // for (j = i + 1; j < qi.count; j++)
  619. // if (qi.elements[i] == qi.elements[j])
  620. // qi.elements[j] = 0;
  621. // }
  622. // if (i == qi.count)
  623. // {
  624. // #if 0
  625. // printf("skipping choice ");
  626. // solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid);
  627. // #endif
  628. // continue;
  629. // }
  630. // d = q.count ? pool_queuetowhatprovides(pool, &q) : 0;
  631. // solver_addrule(solv, r->p, d);
  632. // queue_push(&solv->weakruleq, solv->nrules - 1);
  633. // solv->choicerules_ref[solv->nrules - 1 - solv->choicerules] = rid;
  634. // #if 0
  635. // printf("OLD ");
  636. // solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid);
  637. // printf("WEAK CHOICE ");
  638. // solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + solv->nrules - 1);
  639. // #endif
  640. // }
  641. // queue_free(&q);
  642. // queue_free(&qi);
  643. // map_free(&m);
  644. // map_free(&mneg);
  645. // solv->choicerules_end = solv->nrules;
  646. // }
  647. }
  648. /***********************************************************************
  649. ***
  650. *** Policy rule disabling/reenabling
  651. ***
  652. *** Disable all policy rules that conflict with our jobs. If a job
  653. *** gets disabled later on, reenable the involved policy rules again.
  654. ***
  655. *** /
  656. #define DISABLE_UPDATE 1
  657. #define DISABLE_INFARCH 2
  658. #define DISABLE_DUP 3
  659. */
  660. protected function jobToDisableQueue(array $job, array $disableQueue)
  661. {
  662. switch ($job['cmd']) {
  663. case 'install':
  664. foreach ($job['packages'] as $package) {
  665. if ($this->installed === $package->getRepository()) {
  666. $disableQueue[] = array('type' => 'update', 'package' => $package);
  667. }
  668. /* all job packages obsolete * /
  669. qstart = q->count;
  670. pass = 0;
  671. memset(&omap, 0, sizeof(omap));
  672. FOR_JOB_SELECT(p, pp, select, what)
  673. {
  674. Id p2, pp2;
  675. if (pass == 1)
  676. map_grow(&omap, installed->end - installed->start);
  677. s = pool->solvables + p;
  678. if (s->obsoletes)
  679. {
  680. Id obs, *obsp;
  681. obsp = s->repo->idarraydata + s->obsoletes;
  682. while ((obs = *obsp++) != 0)
  683. FOR_PROVIDES(p2, pp2, obs)
  684. {
  685. Solvable *ps = pool->solvables + p2;
  686. if (ps->repo != installed)
  687. continue;
  688. if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
  689. continue;
  690. if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
  691. continue;
  692. if (pass)
  693. MAPSET(&omap, p2 - installed->start);
  694. else
  695. queue_push2(q, DISABLE_UPDATE, p2);
  696. }
  697. }
  698. FOR_PROVIDES(p2, pp2, s->name)
  699. {
  700. Solvable *ps = pool->solvables + p2;
  701. if (ps->repo != installed)
  702. continue;
  703. if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
  704. continue;
  705. if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
  706. continue;
  707. if (pass)
  708. MAPSET(&omap, p2 - installed->start);
  709. else
  710. queue_push2(q, DISABLE_UPDATE, p2);
  711. }
  712. if (pass)
  713. {
  714. for (i = j = qstart; i < q->count; i += 2)
  715. {
  716. if (MAPTST(&omap, q->elements[i + 1] - installed->start))
  717. {
  718. MAPCLR(&omap, q->elements[i + 1] - installed->start);
  719. q->elements[j + 1] = q->elements[i + 1];
  720. j += 2;
  721. }
  722. }
  723. queue_truncate(q, j);
  724. }
  725. if (q->count == qstart)
  726. break;
  727. pass++;
  728. }
  729. if (omap.size)
  730. map_free(&omap);
  731. if (qstart == q->count)
  732. return; /* nothing to prune * /
  733. if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR))
  734. return; /* all is set */
  735. /* now that we know which installed packages are obsoleted check each of them * /
  736. for (i = j = qstart; i < q->count; i += 2)
  737. {
  738. Solvable *is = pool->solvables + q->elements[i + 1];
  739. FOR_JOB_SELECT(p, pp, select, what)
  740. {
  741. int illegal = 0;
  742. s = pool->solvables + p;
  743. if ((set & SOLVER_SETEVR) != 0)
  744. illegal |= POLICY_ILLEGAL_DOWNGRADE; /* ignore * /
  745. if ((set & SOLVER_SETARCH) != 0)
  746. illegal |= POLICY_ILLEGAL_ARCHCHANGE; /* ignore * /
  747. if ((set & SOLVER_SETVENDOR) != 0)
  748. illegal |= POLICY_ILLEGAL_VENDORCHANGE; /* ignore * /
  749. illegal = policy_is_illegal(solv, is, s, illegal);
  750. if (illegal && illegal == POLICY_ILLEGAL_DOWNGRADE && (set & SOLVER_SETEV) != 0)
  751. {
  752. /* it's ok if the EV is different * /
  753. if (evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE_EVONLY) != 0)
  754. illegal = 0;
  755. }
  756. if (illegal)
  757. break;
  758. }
  759. if (!p)
  760. {
  761. /* no package conflicts with the update rule * /
  762. /* thus keep the DISABLE_UPDATE * /
  763. q->elements[j + 1] = q->elements[i + 1];
  764. j += 2;
  765. }
  766. }
  767. queue_truncate(q, j);
  768. return;*/
  769. }
  770. break;
  771. case 'remove':
  772. foreach ($job['packages'] as $package) {
  773. if ($this->installed === $package->getRepository()) {
  774. $disableQueue[] = array('type' => 'update', 'package' => $package);
  775. }
  776. }
  777. break;
  778. }
  779. return $disableQueue;
  780. }
  781. protected function disableUpdateRule($package)
  782. {
  783. // find update & feature rule and disable
  784. if (isset($this->packageToUpdateRule[$package->getId()])) {
  785. $this->packageToUpdateRule[$package->getId()]->disable();
  786. }
  787. if (isset($this->packageToFeatureRule[$package->getId()])) {
  788. $this->packageToFeatureRule[$literal->getPackageId()]->disable();
  789. }
  790. }
  791. /**
  792. * Disables all policy rules that conflict with jobs
  793. */
  794. protected function disablePolicyRules()
  795. {
  796. $lastJob = null;
  797. $allQueue = array();
  798. $iterator = $this->rules->getIteratorFor(RuleSet::TYPE_JOB);
  799. foreach ($iterator as $rule) {
  800. if ($rule->isDisabled()) {
  801. continue;
  802. }
  803. $job = $this->ruleToJob[$rule->getId()];
  804. if ($job === $lastJob) {
  805. continue;
  806. }
  807. $lastJob = $job;
  808. $allQueue = $this->jobToDisableQueue($job, $allQueue);
  809. }
  810. foreach ($allQueue as $disable) {
  811. switch ($disable['type']) {
  812. case 'update':
  813. $this->disableUpdateRule($disable['package']);
  814. break;
  815. default:
  816. throw new \RuntimeException("Unsupported disable type: " . $disable['type']);
  817. }
  818. }
  819. }
  820. public function solve(Request $request)
  821. {
  822. $this->jobs = $request->getJobs();
  823. $installedPackages = $this->installed->getPackages();
  824. $this->decisionMap = new \SplFixedArray($this->pool->getMaxId() + 1);
  825. foreach ($this->jobs as $job) {
  826. switch ($job['cmd']) {
  827. case 'update-all':
  828. foreach ($installedPackages as $package) {
  829. $this->updateMap[$package->getId()] = true;
  830. }
  831. break;
  832. case 'fix-all':
  833. foreach ($installedPackages as $package) {
  834. $this->fixMap[$package->getId()] = true;
  835. }
  836. break;
  837. }
  838. foreach ($job['packages'] as $package) {
  839. switch ($job['cmd']) {
  840. case 'fix':
  841. if ($this->installed === $package->getRepository()) {
  842. $this->fixMap[$package->getId()] = true;
  843. }
  844. break;
  845. case 'update':
  846. if ($this->installed === $package->getRepository()) {
  847. $this->updateMap[$package->getId()] = true;
  848. }
  849. break;
  850. }
  851. }
  852. }
  853. foreach ($installedPackages as $package) {
  854. $this->addRulesForPackage($package);
  855. }
  856. foreach ($installedPackages as $package) {
  857. $this->addRulesForUpdatePackages($package, true);
  858. }
  859. foreach ($this->jobs as $job) {
  860. foreach ($job['packages'] as $package) {
  861. switch ($job['cmd']) {
  862. case 'install':
  863. $this->installCandidateMap[$package->getId()] = true;
  864. $this->addRulesForPackage($package);
  865. break;
  866. }
  867. }
  868. }
  869. // solver_addrpmrulesforweak(solv, &addedmap);
  870. foreach ($installedPackages as $package) {
  871. // create a feature rule which allows downgrades
  872. $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, true);
  873. $featureRule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
  874. // create an update rule which does not allow downgrades
  875. $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, false);
  876. $rule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
  877. if ($rule->equals($featureRule)) {
  878. if ($this->policy->allowUninstall()) {
  879. $featureRule->setWeak(true);
  880. $this->addRule(RuleSet::TYPE_FEATURE, $featureRule);
  881. $this->packageToFeatureRule[$package->getId()] = $rule;
  882. } else {
  883. $this->addRule(RuleSet::TYPE_UPDATE, $rule);
  884. $this->packageToUpdateRule[$package->getId()] = $rule;
  885. }
  886. } else if ($this->policy->allowUninstall()) {
  887. $featureRule->setWeak(true);
  888. $rule->setWeak(true);
  889. $this->addRule(RuleSet::TYPE_FEATURE, $featureRule);
  890. $this->addRule(RuleSet::TYPE_UPDATE, $rule);
  891. $this->packageToFeatureRule[$package->getId()] = $rule;
  892. $this->packageToUpdateRule[$package->getId()] = $rule;
  893. }
  894. }
  895. foreach ($this->jobs as $job) {
  896. switch ($job['cmd']) {
  897. case 'install':
  898. $rule = $this->createInstallOneOfRule($job['packages'], self::RULE_JOB_INSTALL, $job['packageName']);
  899. $this->addRule(RuleSet::TYPE_JOB, $rule);
  900. $this->ruleToJob[$rule->getId()] = $job;
  901. break;
  902. case 'remove':
  903. // remove all packages with this name including uninstalled
  904. // ones to make sure none of them are picked as replacements
  905. // todo: cleandeps
  906. foreach ($job['packages'] as $package) {
  907. $rule = $this->createRemoveRule($package, self::RULE_JOB_REMOVE);
  908. $this->addRule(RuleSet::TYPE_JOB, $rule);
  909. $this->ruleToJob[$rule->getId()] = $job;
  910. }
  911. break;
  912. case 'lock':
  913. foreach ($job['packages'] as $package) {
  914. if ($this->installed === $package->getRepository()) {
  915. $rule = $this->createInstallRule($package, self::RULE_JOB_LOCK);
  916. } else {
  917. $rule = $this->createRemoveRule($package, self::RULE_JOB_LOCK);
  918. }
  919. $this->addRule(RuleSet::TYPE_JOB, $rule);
  920. $this->ruleToJob[$rule->getId()] = $job;
  921. }
  922. break;
  923. }
  924. }
  925. $this->addChoiceRules();
  926. foreach ($this->rules as $rule) {
  927. $this->addWatchesToRule($rule);
  928. }
  929. /* disable update rules that conflict with our job */
  930. $this->disablePolicyRules();
  931. /* make decisions based on job/update assertions */
  932. $this->makeAssertionRuleDecisions();
  933. $installRecommended = 0;
  934. $this->runSat(true, $installRecommended);
  935. //$this->printDecisionMap();
  936. //findrecommendedsuggested(solv);
  937. //solver_prepare_solutions(solv);
  938. return $this->createTransaction();
  939. }
  940. protected function createTransaction()
  941. {
  942. $transaction = array();
  943. $installMeansUpdateMap = array();
  944. $ignoreRemoveMap = array();
  945. foreach ($this->decisionQueue as $i => $literal) {
  946. $package = $literal->getPackage();
  947. // !wanted & installed
  948. if (!$literal->isWanted() && $this->installed === $package->getRepository()) {
  949. $updateRule = $this->packageToUpdateRule[$package->getId()];
  950. foreach ($updateRule->getLiterals() as $updateLiteral) {
  951. if (!$updateLiteral->equals($literal)) {
  952. $installMeansUpdateMap[$updateLiteral->getPackageId()] = $package;
  953. }
  954. }
  955. }
  956. }
  957. foreach ($this->decisionQueue as $i => $literal) {
  958. $package = $literal->getPackage();
  959. // wanted & installed || !wanted & !installed
  960. if ($literal->isWanted() == ($this->installed === $package->getRepository())) {
  961. continue;
  962. }
  963. if ($literal->isWanted()) {
  964. if (isset($installMeansUpdateMap[$literal->getPackageId()])) {
  965. $source = $installMeansUpdateMap[$literal->getPackageId()];
  966. $transaction[] = array(
  967. 'job' => 'update',
  968. 'from' => $source,
  969. 'to' => $package,
  970. 'why' => $this->decisionQueueWhy[$i],
  971. );
  972. // avoid updates to one package from multiple origins
  973. unset($installMeansUpdateMap[$literal->getPackageId()]);
  974. $ignoreRemove[$source->getId()] = true;
  975. } else {
  976. $transaction[] = array(
  977. 'job' => 'install',
  978. 'package' => $package,
  979. 'why' => $this->decisionQueueWhy[$i],
  980. );
  981. }
  982. } else if (!isset($ignoreRemove[$package->getId()])) {
  983. $transaction[] = array(
  984. 'job' => 'remove',
  985. 'package' => $package,
  986. 'why' => $this->decisionQueueWhy[$i],
  987. );
  988. }
  989. }
  990. return array_reverse($transaction);
  991. }
  992. protected $decisionQueue = array();
  993. protected $decisionQueueWhy = array();
  994. protected $decisionQueueFree = array();
  995. protected $propagateIndex;
  996. protected $branches = array();
  997. protected $problems = array();
  998. protected $learnedPool = array();
  999. protected $recommendsIndex;
  1000. protected function literalFromId($id)
  1001. {
  1002. $package = $this->pool->packageById(abs($id));
  1003. return new Literal($package, $id > 0);
  1004. }
  1005. protected function addDecision(Literal $l, $level)
  1006. {
  1007. if ($l->isWanted()) {
  1008. $this->decisionMap[$l->getPackageId()] = $level;
  1009. } else {
  1010. $this->decisionMap[$l->getPackageId()] = -$level;
  1011. }
  1012. }
  1013. protected function addDecisionId($literalId, $level)
  1014. {
  1015. $packageId = abs($literalId);
  1016. if ($literalId > 0) {
  1017. $this->decisionMap[$packageId] = $level;
  1018. } else {
  1019. $this->decisionMap[$packageId] = -$level;
  1020. }
  1021. }
  1022. protected function decisionsContain(Literal $l)
  1023. {
  1024. return (
  1025. $this->decisionMap[$l->getPackageId()] > 0 && $l->isWanted() ||
  1026. $this->decisionMap[$l->getPackageId()] < 0 && !$l->isWanted()
  1027. );
  1028. }
  1029. protected function decisionsContainId($literalId)
  1030. {
  1031. $packageId = abs($literalId);
  1032. return (
  1033. $this->decisionMap[$packageId] > 0 && $literalId > 0 ||
  1034. $this->decisionMap[$packageId] < 0 && $literalId < 0
  1035. );
  1036. }
  1037. protected function decisionsSatisfy(Literal $l)
  1038. {
  1039. return ($l->isWanted() && $this->decisionMap[$l->getPackageId()] > 0) ||
  1040. (!$l->isWanted() && $this->decisionMap[$l->getPackageId()] <= 0);
  1041. }
  1042. protected function decisionsConflict(Literal $l)
  1043. {
  1044. return (
  1045. $this->decisionMap[$l->getPackageId()] > 0 && !$l->isWanted() ||
  1046. $this->decisionMap[$l->getPackageId()] < 0 && $l->isWanted()
  1047. );
  1048. }
  1049. protected function decisionsConflictId($literalId)
  1050. {
  1051. $packageId = abs($literalId);
  1052. return (
  1053. $this->decisionMap[$packageId] > 0 && !($literalId < 0) ||
  1054. $this->decisionMap[$packageId] < 0 && $literalId > 0
  1055. );
  1056. }
  1057. protected function decided(PackageInterface $p)
  1058. {
  1059. return $this->decisionMap[$p->getId()] != 0;
  1060. }
  1061. protected function undecided(PackageInterface $p)
  1062. {
  1063. return $this->decisionMap[$p->getId()] == 0;
  1064. }
  1065. protected function decidedInstall(PackageInterface $p) {
  1066. return $this->decisionMap[$p->getId()] > 0;
  1067. }
  1068. protected function decidedRemove(PackageInterface $p) {
  1069. return $this->decisionMap[$p->getId()] < 0;
  1070. }
  1071. /**
  1072. * Makes a decision and propagates it to all rules.
  1073. *
  1074. * Evaluates each term affected by the decision (linked through watches)
  1075. * If we find unit rules we make new decisions based on them
  1076. *
  1077. * @return Rule|null A rule on conflict, otherwise null.
  1078. */
  1079. protected function propagate($level)
  1080. {
  1081. while ($this->propagateIndex < count($this->decisionQueue)) {
  1082. // we invert the decided literal here, example:
  1083. // A was decided => (-A|B) now requires B to be true, so we look for
  1084. // rules which are fulfilled by -A, rather than A.
  1085. $literal = $this->decisionQueue[$this->propagateIndex]->inverted();
  1086. $this->propagateIndex++;
  1087. // /* foreach rule where 'pkg' is now FALSE */
  1088. //for (rp = watches + pkg; *rp; rp = next_rp)
  1089. if (!isset($this->watches[$literal->getId()])) {
  1090. continue;
  1091. }
  1092. for ($rule = $this->watches[$literal->getId()]; $rule !== null; $rule = $nextRule) {
  1093. $nextRule = $rule->getNext($literal);
  1094. if ($rule->isDisabled()) {
  1095. continue;
  1096. }
  1097. $otherWatch = $rule->getOtherWatch($literal);
  1098. if ($this->decisionsContainId($otherWatch)) {
  1099. continue;
  1100. }
  1101. $ruleLiterals = $rule->getLiterals();
  1102. if (sizeof($ruleLiterals) > 2) {
  1103. foreach ($ruleLiterals as $ruleLiteral) {
  1104. if ($otherWatch !== $ruleLiteral->getId() &&
  1105. !$this->decisionsConflict($ruleLiteral)) {
  1106. if ($literal->getId() === $rule->watch1) {
  1107. $rule->watch1 = $ruleLiteral->getId();
  1108. $rule->next1 = (isset($this->watches[$ruleLiteral->getId()])) ? $this->watches[$ruleLiteral->getId()] : null ;
  1109. } else {
  1110. $rule->watch2 = $ruleLiteral->getId();
  1111. $rule->next2 = (isset($this->watches[$ruleLiteral->getId()])) ? $this->watches[$ruleLiteral->getId()] : null ;
  1112. }
  1113. $this->watches[$ruleLiteral->getId()] = $rule;
  1114. continue 2;
  1115. }
  1116. }
  1117. }
  1118. // yay, we found a unit clause! try setting it to true
  1119. if ($this->decisionsConflictId($otherWatch)) {
  1120. return $rule;
  1121. }
  1122. $this->addDecisionId($otherWatch, $level);
  1123. $this->decisionQueue[] = $this->literalFromId($otherWatch);
  1124. $this->decisionQueueWhy[] = $rule;
  1125. }
  1126. }
  1127. return null;
  1128. }
  1129. /**
  1130. * Reverts a decision at the given level.
  1131. */
  1132. private function revert($level)
  1133. {
  1134. while (!empty($this->decisionQueue)) {
  1135. $literal = $this->decisionQueue[count($this->decisionQueue) - 1];
  1136. if (!$this->decisionMap[$literal->getPackageId()]) {
  1137. break;
  1138. }
  1139. $decisionLevel = abs($this->decisionMap[$literal->getPackageId()]);
  1140. if ($decisionLevel <= $level) {
  1141. break;
  1142. }
  1143. /** TODO: implement recommendations
  1144. *if (v > 0 && solv->recommendations.count && v == solv->recommendations.elements[solv->recommendations.count - 1])
  1145. * solv->recommendations.count--;
  1146. */
  1147. $this->decisionMap[$literal->getPackageId()] = 0;
  1148. array_pop($this->decisionQueue);
  1149. array_pop($this->decisionQueueWhy);
  1150. $this->propagateIndex = count($this->decisionQueue);
  1151. }
  1152. while (!empty($this->branches)) {
  1153. list($literals, $branchLevel) = $this->branches[count($this->branches) - 1];
  1154. if ($branchLevel >= $level) {
  1155. break;
  1156. }
  1157. array_pop($this->branches);
  1158. }
  1159. $this->recommendsIndex = -1;
  1160. }
  1161. /**-------------------------------------------------------------------
  1162. *
  1163. * setpropagatelearn
  1164. *
  1165. * add free decision (solvable to install) to decisionq
  1166. * increase level and propagate decision
  1167. * return if no conflict.
  1168. *
  1169. * in conflict case, analyze conflict rule, add resulting
  1170. * rule to learnt rule set, make decision from learnt
  1171. * rule (always unit) and re-propagate.
  1172. *
  1173. * returns the new solver level or 0 if unsolvable
  1174. *
  1175. */
  1176. private function setPropagateLearn($level, Literal $literal, $disableRules, Rule $rule)
  1177. {
  1178. assert($rule != null);
  1179. assert($literal != null);
  1180. $level++;
  1181. $this->addDecision($literal, $level);
  1182. $this->decisionQueue[] = $literal;
  1183. $this->decisionQueueWhy[] = $rule;
  1184. $this->decisionQueueFree[count($this->decisionQueueWhy) - 1] = true;
  1185. while (true) {
  1186. $rule = $this->propagate($level);
  1187. if (!$rule) {
  1188. break;
  1189. }
  1190. if ($level == 1) {
  1191. return $this->analyzeUnsolvable($rule, $disableRules);
  1192. }
  1193. // conflict
  1194. list($newLevel, $newRule, $why) = $this->analyze($level, $rule);
  1195. assert($newLevel > 0);
  1196. assert($newLevel < $level);
  1197. $level = $newLevel;
  1198. $this->revert($level);
  1199. assert($newRule != null);
  1200. $this->addRule(RuleSet::TYPE_LEARNED, $newRule);
  1201. $this->learnedWhy[$newRule->getId()] = $why;
  1202. $this->watch2OnHighest($newRule);
  1203. $this->addWatchesToRule($newRule);
  1204. $literals = $newRule->getLiterals();
  1205. $this->addDecision($literals[0], $level);
  1206. $this->decisionQueue[] = $literals[0];
  1207. $this->decisionQueueWhy[] = $newRule;
  1208. }
  1209. return $level;
  1210. }
  1211. private function selectAndInstall($level, array $decisionQueue, $disableRules, Rule $rule)
  1212. {
  1213. // choose best package to install from decisionQueue
  1214. $literals = $this->policy->selectPreferedPackages($this, $this->pool, $this->installed, $decisionQueue);
  1215. $selectedLiteral = array_shift($literals);
  1216. // if there are multiple candidates, then branch
  1217. if (count($literals)) {
  1218. $this->branches[] = array($literals, -$level);
  1219. }
  1220. return $this->setPropagateLearn($level, $selectedLiteral, $disableRules, $rule);
  1221. }
  1222. protected function analyze($level, $rule)
  1223. {
  1224. $ruleLevel = 1;
  1225. $num = 0;
  1226. $l1num = 0;
  1227. $seen = array();
  1228. $learnedLiterals = array(null);
  1229. $decisionId = count($this->decisionQueue);
  1230. $this->learnedPool[] = array();
  1231. while(true) {
  1232. $this->learnedPool[count($this->learnedPool) - 1][] = $rule;
  1233. foreach ($rule->getLiterals() as $literal) {
  1234. // skip the one true literal
  1235. if ($this->decisionsSatisfy($literal)) {
  1236. continue;
  1237. }
  1238. if (isset($seen[$literal->getPackageId()])) {
  1239. continue;
  1240. }
  1241. $seen[$literal->getPackageId()] = true;
  1242. $l = abs($this->decisionMap[$literal->getPackageId()]);
  1243. if (1 === $l) {
  1244. $l1num++;
  1245. } else if ($level === $l) {
  1246. $num++;
  1247. } else {
  1248. // not level1 or conflict level, add to new rule
  1249. $learnedLiterals[] = $literal;
  1250. if ($l > $ruleLevel) {
  1251. $ruleLevel = $l;
  1252. }
  1253. }
  1254. }
  1255. $l1retry = true;
  1256. while ($l1retry) {
  1257. $l1retry = false;
  1258. if (!$num && !$l1num) {
  1259. // all level 1 literals done
  1260. break 2;
  1261. }
  1262. while (true) {
  1263. assert($decisionId > 0);
  1264. $decisionId--;
  1265. $literal = $this->decisionQueue[$decisionId];
  1266. if (isset($seen[$literal->getPackageId()])) {
  1267. break;
  1268. }
  1269. }
  1270. unset($seen[$literal->getPackageId()]);
  1271. if ($num && 0 === --$num) {
  1272. $learnedLiterals[0] = $this->literalFromId(-$literal->getPackageId());
  1273. if (!$l1num) {
  1274. break 2;
  1275. }
  1276. foreach ($this->learnedLiterals as $i => $learnedLiteral) {
  1277. if ($i !== 0) {
  1278. unset($seen[$literal->getPackageId()]);
  1279. }
  1280. }
  1281. // only level 1 marks left
  1282. $l1num++;
  1283. $l1retry = true;
  1284. }
  1285. $rule = $this->decisionQueueWhy[$decisionId];
  1286. }
  1287. }
  1288. $why = count($this->learnedPool) - 1;
  1289. $newRule = new Rule($learnedLiterals, self::RULE_LEARNED, $why);
  1290. return array($ruleLevel, $newRule, $why);
  1291. }
  1292. private function analyzeUnsolvableRule($conflictRule, &$lastWeakWhy)
  1293. {
  1294. $why = $conflictRule->getId();
  1295. if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) {
  1296. $learnedWhy = $this->learnedWhy[$why];
  1297. $problem = $this->learnedPool[$learnedWhy];
  1298. foreach ($problem as $problemRule) {
  1299. $this->analyzeUnsolvableRule($problemRule, $lastWeakWhy);
  1300. }
  1301. return;
  1302. }
  1303. if ($conflictRule->getType() == RuleSet::TYPE_PACKAGE) {
  1304. // package rules cannot be part of a problem
  1305. return;
  1306. }
  1307. if ($conflictRule->isWeak()) {
  1308. /** TODO why > or < lastWeakProblem? */
  1309. if (!$lastWeakWhy || $why > $lastWeakWhy) {
  1310. $lastWeakProblem = $why;
  1311. }
  1312. }
  1313. if ($conflictRule->getType() == RuleSet::TYPE_JOB) {
  1314. $why = $this->ruleToJob[$conflictRule->getId()];
  1315. }
  1316. // if this problem was already found skip it
  1317. if (in_array($why, $this->problems[count($this->problems) - 1], true)) {
  1318. return;
  1319. }
  1320. $this->problems[count($this->problems) - 1][] = $why;
  1321. }
  1322. private function analyzeUnsolvable($conflictRule, $disableRules)
  1323. {
  1324. $lastWeakWhy = null;
  1325. $this->problems[] = array();
  1326. $this->learnedPool[] = array($conflictRule);
  1327. $this->analyzeUnsolvableRule($conflictRule, $lastWeakWhy);
  1328. $seen = array();
  1329. $literals = $conflictRule->getLiterals();
  1330. /* unnecessary because unlike rule.d, watch2 == 2nd literal, unless watch2 changed
  1331. if (sizeof($literals) == 2) {
  1332. $literals[1] = $this->literalFromId($conflictRule->watch2);
  1333. }
  1334. */
  1335. foreach ($literals as $literal) {
  1336. // skip the one true literal
  1337. if ($this->decisionsSatisfy($literal)) {
  1338. continue;
  1339. }
  1340. $seen[$literal->getPackageId()] = true;
  1341. }
  1342. $decisionId = count($this->decisionQueue);
  1343. while ($decisionId > 0) {
  1344. $decisionId--;
  1345. $literal = $this->decisionQueue[$decisionId];
  1346. // skip literals that are not in this rule
  1347. if (!isset($seen[$literal->getPackageId()])) {
  1348. continue;
  1349. }
  1350. $why = $this->decisionQueueWhy[$decisionId];
  1351. $this->learnedPool[count($this->learnedPool) - 1][] = $why;
  1352. $this->analyzeUnsolvableRule($why, $lastWeakWhy);
  1353. $literals = $why->getLiterals();
  1354. /* unnecessary because unlike rule.d, watch2 == 2nd literal, unless watch2 changed
  1355. if (sizeof($literals) == 2) {
  1356. $literals[1] = $this->literalFromId($why->watch2);
  1357. }
  1358. */
  1359. foreach ($literals as $literal) {
  1360. // skip the one true literal
  1361. if ($this->decisionsSatisfy($literal)) {
  1362. continue;
  1363. }
  1364. $seen[$literal->getPackageId()] = true;
  1365. }
  1366. }
  1367. if ($lastWeakWhy) {
  1368. array_pop($this->problems);
  1369. array_pop($this->learnedPool);
  1370. if ($lastWeakWhy->getType() === RuleSet::TYPE_JOB) {
  1371. $why = $this->ruleToJob[$lastWeakWhy];
  1372. } else {
  1373. $why = $lastWeakWhy;
  1374. }
  1375. if ($lastWeakWhy->getType() == RuleSet::TYPE_CHOICE) {
  1376. $this->disableChoiceRules($lastWeakWhy);
  1377. }
  1378. $this->disableProblem($why);
  1379. /**
  1380. @TODO what does v < 0 mean here? ($why == v)
  1381. if (v < 0)
  1382. solver_reenablepolicyrules(solv, -(v + 1));
  1383. */
  1384. $this->resetSolver();
  1385. return true;
  1386. }
  1387. if ($disableRules) {
  1388. foreach ($this->problems[count($this->problems) - 1] as $why) {
  1389. $this->disableProblem($why);
  1390. }
  1391. $this->resetSolver();
  1392. return true;
  1393. }
  1394. return false;
  1395. }
  1396. private function disableProblem($why)
  1397. {
  1398. if ($why instanceof Rule) {
  1399. $why->disable();
  1400. } else if (is_array($why)) {
  1401. // disable all rules of this job
  1402. foreach ($this->ruleToJob as $ruleId => $job) {
  1403. if ($why === $job) {
  1404. $this->rules->ruleById($ruleId)->disable();
  1405. }
  1406. }
  1407. }
  1408. }
  1409. private function resetSolver()
  1410. {
  1411. while ($literal = array_pop($this->decisionQueue)) {
  1412. $this->decisionMap[$literal->getPackageId()] = 0;
  1413. }
  1414. $this->decisionQueueWhy = array();
  1415. $this->decisionQueueFree = array();
  1416. $this->recommendsIndex = -1;
  1417. $this->propagateIndex = 0;
  1418. $this->recommendations = array();
  1419. $this->branches = array();
  1420. $this->enableDisableLearnedRules();
  1421. $this->makeAssertionRuleDecisions();
  1422. }
  1423. /*-------------------------------------------------------------------
  1424. * enable/disable learnt rules
  1425. *
  1426. * we have enabled or disabled some of our rules. We now reenable all
  1427. * of our learnt rules except the ones that were learnt from rules that
  1428. * are now disabled.
  1429. */
  1430. private function enableDisableLearnedRules()
  1431. {
  1432. foreach ($this->rules->getIteratorFor(RuleSet::TYPE_LEARNED) as $rule) {
  1433. $why = $this->learnedWhy[$rule->getId()];
  1434. $problem = $this->learnedPool[$why];
  1435. $foundDisabled = false;
  1436. foreach ($problem as $problemRule) {
  1437. if ($problemRule->disabled()) {
  1438. $foundDisabled = true;
  1439. break;
  1440. }
  1441. }
  1442. if ($foundDisabled && $rule->isEnabled()) {
  1443. $rule->disable();
  1444. } else if (!$foundDisabled && $rule->isDisabled()) {
  1445. $rule->enable();
  1446. }
  1447. }
  1448. }
  1449. private function runSat($disableRules = true, $installRecommended = false)
  1450. {
  1451. $this->propagateIndex = 0;
  1452. // /*
  1453. // * here's the main loop:
  1454. // * 1) propagate new decisions (only needed once)
  1455. // * 2) fulfill jobs
  1456. // * 3) try to keep installed packages
  1457. // * 4) fulfill all unresolved rules
  1458. // * 5) install recommended packages
  1459. // * 6) minimalize solution if we had choices
  1460. // * if we encounter a problem, we rewind to a safe level and restart
  1461. // * with step 1
  1462. // */
  1463. $decisionQueue = array();
  1464. $decisionSupplementQueue = array();
  1465. $disableRules = array();
  1466. $level = 1;
  1467. $systemLevel = $level + 1;
  1468. $minimizationsteps = 0;
  1469. $installedPos = 0;
  1470. $this->installedPackages = $this->installed->getPackages();
  1471. while (true) {
  1472. $conflictRule = $this->propagate($level);
  1473. if ($conflictRule !== null) {
  1474. if ($this->analyzeUnsolvable($conflictRule, $disableRules)) {
  1475. continue;
  1476. } else {
  1477. return;
  1478. }
  1479. }
  1480. // handle job rules
  1481. if ($level < $systemLevel) {
  1482. $iterator = $this->rules->getIteratorFor(RuleSet::TYPE_JOB);
  1483. foreach ($iterator as $rule) {
  1484. if ($rule->isEnabled()) {
  1485. $decisionQueue = array();
  1486. $noneSatisfied = true;
  1487. foreach ($rule->getLiterals() as $literal) {
  1488. if ($this->decisionsSatisfy($literal)) {
  1489. $noneSatisfied = false;
  1490. break;
  1491. }
  1492. $decisionQueue[] = $literal;
  1493. }
  1494. if ($noneSatisfied && count($decisionQueue)) {
  1495. // prune all update packages until installed version
  1496. // except for requested updates
  1497. if (count($this->installed) != count($this->updateMap)) {
  1498. $prunedQueue = array();
  1499. foreach ($decisionQueue as $literal) {
  1500. if ($this->installed === $literal->getPackage()->getRepository()) {
  1501. $prunedQueue[] = $literal;
  1502. if (isset($this->updateMap[$literal->getPackageId()])) {
  1503. $prunedQueue = $decisionQueue;
  1504. break;
  1505. }
  1506. }
  1507. }
  1508. $decisionQueue = $prunedQueue;
  1509. }
  1510. }
  1511. if ($noneSatisfied && count($decisionQueue)) {
  1512. $oLevel = $level;
  1513. $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
  1514. if (0 === $level) {
  1515. return;
  1516. }
  1517. if ($level <= $oLevel) {
  1518. break;
  1519. }
  1520. }
  1521. }
  1522. }
  1523. $systemLevel = $level + 1;
  1524. // jobs left
  1525. $iterator->next();
  1526. if ($iterator->valid()) {
  1527. continue;
  1528. }
  1529. }
  1530. // handle installed packages
  1531. if ($level < $systemLevel) {
  1532. // use two passes if any packages are being updated
  1533. // -> better user experience
  1534. for ($pass = (count($this->updateMap)) ? 0 : 1; $pass < 2; $pass++) {
  1535. $passLevel = $level;
  1536. for ($i = $installedPos, $n = 0; $n < count($this->installedPackages); $i++, $n++) {
  1537. $repeat = false;
  1538. if ($i == count($this->installedPackages)) {
  1539. $i = 0;
  1540. }
  1541. $literal = new Literal($this->installedPackages[$i], true);
  1542. if ($this->decisionsContain($literal)) {
  1543. continue;
  1544. }
  1545. // only process updates in first pass
  1546. /** TODO: && or || ? **/
  1547. if (0 === $pass && !isset($this->updateMap[$literal->getPackageId()])) {
  1548. continue;
  1549. }
  1550. $rule = null;
  1551. if (isset($this->packageToUpdateRule[$literal->getPackageId()])) {
  1552. $rule = $this->packageToUpdateRule[$literal->getPackageId()];
  1553. }
  1554. if ((!$rule || $rule->isDisabled()) && isset($this->packageToFeatureRule[$literal->getPackageId()])) {
  1555. $rule = $this->packageToFeatureRule[$literal->getPackageId()];
  1556. }
  1557. if (!$rule || $rule->isDisabled()) {
  1558. continue;
  1559. }
  1560. $updateRuleLiterals = $rule->getLiterals();
  1561. $decisionQueue = array();
  1562. if (!isset($this->noUpdate[$literal->getPackageId()]) && (
  1563. $this->decidedRemove($literal->getPackage()) ||
  1564. isset($this->updateMap[$literal->getPackageId()]) ||
  1565. !$literal->equals($updateRuleLiterals[0])
  1566. )) {
  1567. foreach ($updateRuleLiterals as $ruleLiteral) {
  1568. if ($this->decidedInstall($ruleLiteral->getPackage())) {
  1569. // already fulfilled
  1570. $decisionQueue = array();
  1571. break;
  1572. }
  1573. if ($this->undecided($ruleLiteral->getPackage())) {
  1574. $decisionQueue[] = $ruleLiteral;
  1575. }
  1576. }
  1577. }
  1578. if (sizeof($decisionQueue)) {
  1579. $oLevel = $level;
  1580. $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
  1581. if (0 === $level) {
  1582. return;
  1583. }
  1584. if ($level <= $oLevel) {
  1585. $repeat = true;
  1586. }
  1587. } else if (!$repeat && $this->undecided($literal->getPackage())) {
  1588. // still undecided? keep package.
  1589. $oLevel = $level;
  1590. if (isset($this->cleanDepsMap[$literal->getPackageId()])) {
  1591. // clean deps removes package
  1592. $level = $this->setPropagateLearn($level, $literal->invert(), $disableRules, null);
  1593. } else {
  1594. // ckeeping package
  1595. $level = $this->setPropagateLearn($level, $literal, $disableRules, $rule);
  1596. }
  1597. if (0 === $level) {
  1598. return;
  1599. }
  1600. if ($level <= $oLevel) {
  1601. $repeat = true;
  1602. }
  1603. }
  1604. if ($repeat) {
  1605. if (1 === $level || $level < $passLevel) {
  1606. // trouble
  1607. break;
  1608. }
  1609. if ($level < $oLevel) {
  1610. // redo all
  1611. $n = 0;
  1612. }
  1613. // repeat
  1614. $i--;
  1615. $n--;
  1616. continue;
  1617. }
  1618. }
  1619. if ($n < count($this->installedPackages)) {
  1620. $installedPos = $i; // retry this problem next time
  1621. break;
  1622. }
  1623. $installedPos = 0;
  1624. }
  1625. $systemlevel = $level + 1;
  1626. if ($pass < 2) {
  1627. // had trouble => retry
  1628. continue;
  1629. }
  1630. }
  1631. if ($level < $systemLevel) {
  1632. $systemLevel = $level;
  1633. }
  1634. for ($i = 0, $n = 0; $n < count($this->rules); $i++, $n++) {
  1635. if ($i == count($this->rules)) {
  1636. $i = 0;
  1637. }
  1638. $rule = $this->rules->ruleById($i);
  1639. $literals = $rule->getLiterals();
  1640. if ($rule->isDisabled()) {
  1641. continue;
  1642. }
  1643. $decisionQueue = array();
  1644. // make sure that
  1645. // * all negative literals are installed
  1646. // * no positive literal is installed
  1647. // i.e. the rule is not fulfilled and we
  1648. // just need to decide on the positive literals
  1649. //
  1650. foreach ($literals as $literal) {
  1651. if (!$literal->isWanted()) {
  1652. if (!$this->decidedInstall($literal->getPackage())) {
  1653. continue 2; // next rule
  1654. }
  1655. } else {
  1656. if ($this->decidedInstall($literal->getPackage())) {
  1657. continue 2; // next rule
  1658. }
  1659. if ($this->undecided($literal->getPackage())) {
  1660. $decisionQueue[] = $literal;
  1661. }
  1662. }
  1663. }
  1664. // need to have at least 2 item to pick from
  1665. if (count($decisionQueue) < 2) {
  1666. continue;
  1667. }
  1668. $oLevel = $level;
  1669. $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
  1670. if (0 === $level) {
  1671. return;
  1672. }
  1673. if ($level < $systemLevel || $level == 1) {
  1674. break; // trouble
  1675. }
  1676. // something changed, so look at all rules again
  1677. $n = -1;
  1678. }
  1679. // $this->printDecisionMap();
  1680. // $this->printDecisionQueue();
  1681. // minimization step
  1682. if (count($this->branches)) {
  1683. $lastLiteral = null;
  1684. $lastLevel = null;
  1685. $lastBranchIndex = 0;
  1686. $lastBranchOffset = 0;
  1687. for ($i = count($this->branches) - 1; $i >= 0; $i--) {
  1688. list($literals, $level) = $this->branches[$i];
  1689. foreach ($literals as $offset => $literal) {
  1690. if ($literal && $literal->isWanted() && $this->decisionMap[$literal->getPackageId()] > $level + 1) {
  1691. $lastLiteral = $literal;
  1692. $lastBranchIndex = $i;
  1693. $lastBranchOffset = $offset;
  1694. $lastLevel = $level;
  1695. }
  1696. }
  1697. }
  1698. if ($lastLiteral) {
  1699. $this->branches[$lastBranchIndex][$lastBranchOffset] = null;
  1700. $minimizationSteps++;
  1701. $level = $lastLevel;
  1702. $this->revert($level);
  1703. $why = $this->decisionQueueWhy[count($this->decisionQueueWhy)];
  1704. $oLevel = $level;
  1705. $level = $this->setPropagateLearn($level, $lastLiteral, $disableRules, $why);
  1706. if ($level == 0) {
  1707. return;
  1708. }
  1709. continue;
  1710. }
  1711. }
  1712. break;
  1713. }
  1714. }
  1715. public function printDecisionMap()
  1716. {
  1717. echo "DecisionMap: \n";
  1718. foreach ($this->decisionMap as $packageId => $level) {
  1719. if ($level > 0) {
  1720. echo ' +' . $this->pool->packageById($packageId) . "\n";
  1721. } else {
  1722. echo ' -' . $this->pool->packageById($packageId) . "\n";
  1723. }
  1724. }
  1725. echo "\n";
  1726. }
  1727. public function printDecisionQueue()
  1728. {
  1729. echo "DecisionQueue: \n";
  1730. foreach ($this->decisionQueue as $i => $literal) {
  1731. echo ' ' . $literal . ' ' . $this->decisionQueueWhy[$i] . "\n";
  1732. }
  1733. echo "\n";
  1734. }
  1735. }