Solver.php 71 KB

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