Solver.php 63 KB

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