Move rule generation from solver into separate rule set generator
parent
83c499cf29
commit
46e4ae0e6a
|
@ -25,20 +25,6 @@ class Problem
|
||||||
*/
|
*/
|
||||||
protected $reasons;
|
protected $reasons;
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a job as a reason
|
|
||||||
*
|
|
||||||
* @param array $job A job descriptor which is a reason for this problem
|
|
||||||
* @param Rule $rule An optional rule associated with the job
|
|
||||||
*/
|
|
||||||
public function addJobRule($job, Rule $rule = null)
|
|
||||||
{
|
|
||||||
$this->addReason(serialize($job), array(
|
|
||||||
'rule' => $rule,
|
|
||||||
'job' => $job,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a rule as a reason
|
* Add a rule as a reason
|
||||||
*
|
*
|
||||||
|
@ -48,7 +34,7 @@ class Problem
|
||||||
{
|
{
|
||||||
$this->addReason($rule->getId(), array(
|
$this->addReason($rule->getId(), array(
|
||||||
'rule' => $rule,
|
'rule' => $rule,
|
||||||
'job' => null,
|
'job' => $rule->getJob(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,8 @@ class Rule
|
||||||
protected $id;
|
protected $id;
|
||||||
protected $weak;
|
protected $weak;
|
||||||
|
|
||||||
|
protected $job;
|
||||||
|
|
||||||
public $watch1;
|
public $watch1;
|
||||||
public $watch2;
|
public $watch2;
|
||||||
|
|
||||||
|
@ -43,7 +45,7 @@ class Rule
|
||||||
|
|
||||||
public $ruleHash;
|
public $ruleHash;
|
||||||
|
|
||||||
public function __construct(array $literals, $reason, $reasonData)
|
public function __construct(array $literals, $reason, $reasonData, $job = null)
|
||||||
{
|
{
|
||||||
// sort all packages ascending by id
|
// sort all packages ascending by id
|
||||||
usort($literals, array($this, 'compareLiteralsById'));
|
usort($literals, array($this, 'compareLiteralsById'));
|
||||||
|
@ -55,6 +57,8 @@ class Rule
|
||||||
$this->disabled = false;
|
$this->disabled = false;
|
||||||
$this->weak = false;
|
$this->weak = false;
|
||||||
|
|
||||||
|
$this->job = $job;
|
||||||
|
|
||||||
$this->watch1 = (count($this->literals) > 0) ? $literals[0]->getId() : 0;
|
$this->watch1 = (count($this->literals) > 0) ? $literals[0]->getId() : 0;
|
||||||
$this->watch2 = (count($this->literals) > 1) ? $literals[1]->getId() : 0;
|
$this->watch2 = (count($this->literals) > 1) ? $literals[1]->getId() : 0;
|
||||||
|
|
||||||
|
@ -80,6 +84,11 @@ class Rule
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getJob()
|
||||||
|
{
|
||||||
|
return $this->job;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if this rule is equal to another one
|
* Checks if this rule is equal to another one
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,306 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\DependencyResolver;
|
||||||
|
|
||||||
|
use Composer\Repository\RepositoryInterface;
|
||||||
|
use Composer\Package\PackageInterface;
|
||||||
|
use Composer\Package\AliasPackage;
|
||||||
|
use Composer\DependencyResolver\Operation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
*/
|
||||||
|
class RuleSetGenerator
|
||||||
|
{
|
||||||
|
protected $policy;
|
||||||
|
protected $pool;
|
||||||
|
protected $rules;
|
||||||
|
protected $jobs;
|
||||||
|
protected $installedMap;
|
||||||
|
|
||||||
|
public function __construct(PolicyInterface $policy, Pool $pool)
|
||||||
|
{
|
||||||
|
$this->policy = $policy;
|
||||||
|
$this->pool = $pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new rule for the requirements of a package
|
||||||
|
*
|
||||||
|
* This rule is of the form (-A|B|C), where B and C are the providers of
|
||||||
|
* one requirement of the package A.
|
||||||
|
*
|
||||||
|
* @param PackageInterface $package The package with a requirement
|
||||||
|
* @param array $providers The providers of the requirement
|
||||||
|
* @param int $reason A RULE_* constant describing the
|
||||||
|
* reason for generating this rule
|
||||||
|
* @param mixed $reasonData Any data, e.g. the requirement name,
|
||||||
|
* that goes with the reason
|
||||||
|
* @return Rule The generated rule or null if tautological
|
||||||
|
*/
|
||||||
|
protected function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null)
|
||||||
|
{
|
||||||
|
$literals = array(new Literal($package, false));
|
||||||
|
|
||||||
|
foreach ($providers as $provider) {
|
||||||
|
// self fulfilling rule?
|
||||||
|
if ($provider === $package) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$literals[] = new Literal($provider, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Rule($literals, $reason, $reasonData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new rule for installing a package
|
||||||
|
*
|
||||||
|
* The rule is simply (A) for a package A to be installed.
|
||||||
|
*
|
||||||
|
* @param PackageInterface $package The package to be installed
|
||||||
|
* @param int $reason A RULE_* constant describing the
|
||||||
|
* reason for generating this rule
|
||||||
|
* @param mixed $reasonData Any data, e.g. the package name, that
|
||||||
|
* goes with the reason
|
||||||
|
* @return Rule The generated rule
|
||||||
|
*/
|
||||||
|
protected function createInstallRule(PackageInterface $package, $reason, $reasonData = null)
|
||||||
|
{
|
||||||
|
return new Rule(new Literal($package, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a rule to install at least one of a set of packages
|
||||||
|
*
|
||||||
|
* The rule is (A|B|C) with A, B and C different packages. If the given
|
||||||
|
* set of packages is empty an impossible rule is generated.
|
||||||
|
*
|
||||||
|
* @param array $packages The set of packages to choose from
|
||||||
|
* @param int $reason A RULE_* constant describing the reason for
|
||||||
|
* generating this rule
|
||||||
|
* @param array $job The job this rule was created from
|
||||||
|
* @return Rule The generated rule
|
||||||
|
*/
|
||||||
|
protected function createInstallOneOfRule(array $packages, $reason, $job)
|
||||||
|
{
|
||||||
|
$literals = array();
|
||||||
|
foreach ($packages as $package) {
|
||||||
|
$literals[] = new Literal($package, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Rule($literals, $reason, $job['packageName'], $job);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a rule to remove a package
|
||||||
|
*
|
||||||
|
* The rule for a package A is (-A).
|
||||||
|
*
|
||||||
|
* @param PackageInterface $package The package to be removed
|
||||||
|
* @param int $reason A RULE_* constant describing the
|
||||||
|
* reason for generating this rule
|
||||||
|
* @param array $job The job this rule was created from
|
||||||
|
* @return Rule The generated rule
|
||||||
|
*/
|
||||||
|
protected function createRemoveRule(PackageInterface $package, $reason, $job)
|
||||||
|
{
|
||||||
|
return new Rule(array(new Literal($package, false)), $reason, $job['packageName'], $job);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a rule for two conflicting packages
|
||||||
|
*
|
||||||
|
* The rule for conflicting packages A and B is (-A|-B). A is called the issuer
|
||||||
|
* and B the provider.
|
||||||
|
*
|
||||||
|
* @param PackageInterface $issuer The package declaring the conflict
|
||||||
|
* @param Package $provider The package causing the conflict
|
||||||
|
* @param int $reason A RULE_* constant describing the
|
||||||
|
* reason for generating this rule
|
||||||
|
* @param mixed $reasonData Any data, e.g. the package name, that
|
||||||
|
* goes with the reason
|
||||||
|
* @return Rule The generated rule
|
||||||
|
*/
|
||||||
|
protected function createConflictRule(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null)
|
||||||
|
{
|
||||||
|
// ignore self conflict
|
||||||
|
if ($issuer === $provider) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Rule(array(new Literal($issuer, false), new Literal($provider, false)), $reason, $reasonData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a rule unless it duplicates an existing one of any type
|
||||||
|
*
|
||||||
|
* To be able to directly pass in the result of one of the rule creation
|
||||||
|
* methods.
|
||||||
|
*
|
||||||
|
* @param int $type A TYPE_* constant defining the rule type
|
||||||
|
* @param Rule $newRule The rule about to be added
|
||||||
|
*/
|
||||||
|
private function addRule($type, Rule $newRule = null) {
|
||||||
|
if ($this->rules->containsEqual($newRule)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->rules->add($newRule, $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function addRulesForPackage(PackageInterface $package)
|
||||||
|
{
|
||||||
|
$workQueue = new \SplQueue;
|
||||||
|
$workQueue->enqueue($package);
|
||||||
|
|
||||||
|
while (!$workQueue->isEmpty()) {
|
||||||
|
$package = $workQueue->dequeue();
|
||||||
|
if (isset($this->addedMap[$package->getId()])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addedMap[$package->getId()] = true;
|
||||||
|
|
||||||
|
foreach ($package->getRequires() as $link) {
|
||||||
|
$possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
|
||||||
|
|
||||||
|
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, $possibleRequires, Rule::RULE_PACKAGE_REQUIRES, (string) $link));
|
||||||
|
|
||||||
|
foreach ($possibleRequires as $require) {
|
||||||
|
$workQueue->enqueue($require);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($package->getConflicts() as $link) {
|
||||||
|
$possibleConflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
|
||||||
|
|
||||||
|
foreach ($possibleConflicts as $conflict) {
|
||||||
|
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $conflict, Rule::RULE_PACKAGE_CONFLICT, (string) $link));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check obsoletes and implicit obsoletes of a package
|
||||||
|
$isInstalled = (isset($this->installedMap[$package->getId()]));
|
||||||
|
|
||||||
|
foreach ($package->getReplaces() as $link) {
|
||||||
|
$obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
|
||||||
|
|
||||||
|
foreach ($obsoleteProviders as $provider) {
|
||||||
|
if ($provider === $package) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
|
||||||
|
$reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
|
||||||
|
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check implicit obsoletes
|
||||||
|
// for installed packages we only need to check installed/installed problems,
|
||||||
|
// as the others are picked up when looking at the uninstalled package.
|
||||||
|
if (!$isInstalled) {
|
||||||
|
$obsoleteProviders = $this->pool->whatProvides($package->getName(), null);
|
||||||
|
|
||||||
|
foreach ($obsoleteProviders as $provider) {
|
||||||
|
if ($provider === $package) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) {
|
||||||
|
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, (string) $package));
|
||||||
|
} else if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
|
||||||
|
$reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES;
|
||||||
|
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, (string) $package));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function obsoleteImpossibleForAlias($package, $provider)
|
||||||
|
{
|
||||||
|
$packageIsAlias = $package instanceof AliasPackage;
|
||||||
|
$providerIsAlias = $provider instanceof AliasPackage;
|
||||||
|
|
||||||
|
$impossible = (
|
||||||
|
($packageIsAlias && $package->getAliasOf() === $provider) ||
|
||||||
|
($providerIsAlias && $provider->getAliasOf() === $package) ||
|
||||||
|
($packageIsAlias && $providerIsAlias && $provider->getAliasOf() === $package->getAliasOf())
|
||||||
|
);
|
||||||
|
|
||||||
|
return $impossible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all rules for all update packages of a given package
|
||||||
|
*
|
||||||
|
* @param PackageInterface $package Rules for this package's updates are to
|
||||||
|
* be added
|
||||||
|
* @param bool $allowAll Whether downgrades are allowed
|
||||||
|
*/
|
||||||
|
private function addRulesForUpdatePackages(PackageInterface $package)
|
||||||
|
{
|
||||||
|
$updates = $this->policy->findUpdatePackages($this->pool, $this->installedMap, $package);
|
||||||
|
|
||||||
|
foreach ($updates as $update) {
|
||||||
|
$this->addRulesForPackage($update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function addRulesForJobs()
|
||||||
|
{
|
||||||
|
foreach ($this->jobs as $job) {
|
||||||
|
switch ($job['cmd']) {
|
||||||
|
case 'install':
|
||||||
|
if ($job['packages']) {
|
||||||
|
foreach ($job['packages'] as $package) {
|
||||||
|
if (!isset($this->installedMap[$package->getId()])) {
|
||||||
|
$this->addRulesForPackage($package);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rule = $this->createInstallOneOfRule($job['packages'], Rule::RULE_JOB_INSTALL, $job);
|
||||||
|
$this->addRule(RuleSet::TYPE_JOB, $rule);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'remove':
|
||||||
|
// remove all packages with this name including uninstalled
|
||||||
|
// ones to make sure none of them are picked as replacements
|
||||||
|
foreach ($job['packages'] as $package) {
|
||||||
|
$rule = $this->createRemoveRule($package, Rule::RULE_JOB_REMOVE, $job);
|
||||||
|
$this->addRule(RuleSet::TYPE_JOB, $rule);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRulesFor($jobs, $installedMap)
|
||||||
|
{
|
||||||
|
$this->jobs = $jobs;
|
||||||
|
$this->rules = new RuleSet;
|
||||||
|
$this->installedMap = $installedMap;
|
||||||
|
|
||||||
|
foreach ($this->installedMap as $package) {
|
||||||
|
$this->addRulesForPackage($package);
|
||||||
|
$this->addRulesForUpdatePackages($package);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addRulesForJobs();
|
||||||
|
|
||||||
|
return $this->rules;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,9 +26,9 @@ class Solver
|
||||||
protected $pool;
|
protected $pool;
|
||||||
protected $installed;
|
protected $installed;
|
||||||
protected $rules;
|
protected $rules;
|
||||||
|
protected $ruleSetGenerator;
|
||||||
protected $updateAll;
|
protected $updateAll;
|
||||||
|
|
||||||
protected $ruleToJob = array();
|
|
||||||
protected $addedMap = array();
|
protected $addedMap = array();
|
||||||
protected $updateMap = array();
|
protected $updateMap = array();
|
||||||
protected $watches = array();
|
protected $watches = array();
|
||||||
|
@ -48,238 +48,7 @@ class Solver
|
||||||
$this->policy = $policy;
|
$this->policy = $policy;
|
||||||
$this->pool = $pool;
|
$this->pool = $pool;
|
||||||
$this->installed = $installed;
|
$this->installed = $installed;
|
||||||
$this->rules = new RuleSet;
|
$this->ruleSetGenerator = new RuleSetGenerator($policy, $pool);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new rule for the requirements of a package
|
|
||||||
*
|
|
||||||
* This rule is of the form (-A|B|C), where B and C are the providers of
|
|
||||||
* one requirement of the package A.
|
|
||||||
*
|
|
||||||
* @param PackageInterface $package The package with a requirement
|
|
||||||
* @param array $providers The providers of the requirement
|
|
||||||
* @param int $reason A RULE_* constant describing the
|
|
||||||
* reason for generating this rule
|
|
||||||
* @param mixed $reasonData Any data, e.g. the requirement name,
|
|
||||||
* that goes with the reason
|
|
||||||
* @return Rule The generated rule or null if tautological
|
|
||||||
*/
|
|
||||||
protected function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null)
|
|
||||||
{
|
|
||||||
$literals = array(new Literal($package, false));
|
|
||||||
|
|
||||||
foreach ($providers as $provider) {
|
|
||||||
// self fulfilling rule?
|
|
||||||
if ($provider === $package) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$literals[] = new Literal($provider, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Rule($literals, $reason, $reasonData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new rule for installing a package
|
|
||||||
*
|
|
||||||
* The rule is simply (A) for a package A to be installed.
|
|
||||||
*
|
|
||||||
* @param PackageInterface $package The package to be installed
|
|
||||||
* @param int $reason A RULE_* constant describing the
|
|
||||||
* reason for generating this rule
|
|
||||||
* @param mixed $reasonData Any data, e.g. the package name, that
|
|
||||||
* goes with the reason
|
|
||||||
* @return Rule The generated rule
|
|
||||||
*/
|
|
||||||
protected function createInstallRule(PackageInterface $package, $reason, $reasonData = null)
|
|
||||||
{
|
|
||||||
return new Rule(new Literal($package, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a rule to install at least one of a set of packages
|
|
||||||
*
|
|
||||||
* The rule is (A|B|C) with A, B and C different packages. If the given
|
|
||||||
* set of packages is empty an impossible rule is generated.
|
|
||||||
*
|
|
||||||
* @param array $packages The set of packages to choose from
|
|
||||||
* @param int $reason A RULE_* constant describing the reason for
|
|
||||||
* generating this rule
|
|
||||||
* @param mixed $reasonData Any data, e.g. the package name, that goes with
|
|
||||||
* the reason
|
|
||||||
* @return Rule The generated rule
|
|
||||||
*/
|
|
||||||
protected function createInstallOneOfRule(array $packages, $reason, $reasonData = null)
|
|
||||||
{
|
|
||||||
$literals = array();
|
|
||||||
foreach ($packages as $package) {
|
|
||||||
$literals[] = new Literal($package, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Rule($literals, $reason, $reasonData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a rule to remove a package
|
|
||||||
*
|
|
||||||
* The rule for a package A is (-A).
|
|
||||||
*
|
|
||||||
* @param PackageInterface $package The package to be removed
|
|
||||||
* @param int $reason A RULE_* constant describing the
|
|
||||||
* reason for generating this rule
|
|
||||||
* @param mixed $reasonData Any data, e.g. the package name, that
|
|
||||||
* goes with the reason
|
|
||||||
* @return Rule The generated rule
|
|
||||||
*/
|
|
||||||
protected function createRemoveRule(PackageInterface $package, $reason, $reasonData = null)
|
|
||||||
{
|
|
||||||
return new Rule(array(new Literal($package, false)), $reason, $reasonData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a rule for two conflicting packages
|
|
||||||
*
|
|
||||||
* The rule for conflicting packages A and B is (-A|-B). A is called the issuer
|
|
||||||
* and B the provider.
|
|
||||||
*
|
|
||||||
* @param PackageInterface $issuer The package declaring the conflict
|
|
||||||
* @param Package $provider The package causing the conflict
|
|
||||||
* @param int $reason A RULE_* constant describing the
|
|
||||||
* reason for generating this rule
|
|
||||||
* @param mixed $reasonData Any data, e.g. the package name, that
|
|
||||||
* goes with the reason
|
|
||||||
* @return Rule The generated rule
|
|
||||||
*/
|
|
||||||
protected function createConflictRule(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null)
|
|
||||||
{
|
|
||||||
// ignore self conflict
|
|
||||||
if ($issuer === $provider) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Rule(array(new Literal($issuer, false), new Literal($provider, false)), $reason, $reasonData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a rule unless it duplicates an existing one of any type
|
|
||||||
*
|
|
||||||
* To be able to directly pass in the result of one of the rule creation
|
|
||||||
* methods the rule may also be null to indicate that no rule should be
|
|
||||||
* added.
|
|
||||||
*
|
|
||||||
* @param int $type A TYPE_* constant defining the rule type
|
|
||||||
* @param Rule $newRule The rule about to be added
|
|
||||||
*/
|
|
||||||
private function addRule($type, Rule $newRule = null) {
|
|
||||||
if ($newRule) {
|
|
||||||
if ($this->rules->containsEqual($newRule)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->rules->add($newRule, $type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function addRulesForPackage(PackageInterface $package)
|
|
||||||
{
|
|
||||||
$workQueue = new \SplQueue;
|
|
||||||
$workQueue->enqueue($package);
|
|
||||||
|
|
||||||
while (!$workQueue->isEmpty()) {
|
|
||||||
$package = $workQueue->dequeue();
|
|
||||||
if (isset($this->addedMap[$package->getId()])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addedMap[$package->getId()] = true;
|
|
||||||
|
|
||||||
foreach ($package->getRequires() as $link) {
|
|
||||||
$possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
|
|
||||||
|
|
||||||
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, $possibleRequires, Rule::RULE_PACKAGE_REQUIRES, (string) $link));
|
|
||||||
|
|
||||||
foreach ($possibleRequires as $require) {
|
|
||||||
$workQueue->enqueue($require);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($package->getConflicts() as $link) {
|
|
||||||
$possibleConflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
|
|
||||||
|
|
||||||
foreach ($possibleConflicts as $conflict) {
|
|
||||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $conflict, Rule::RULE_PACKAGE_CONFLICT, (string) $link));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check obsoletes and implicit obsoletes of a package
|
|
||||||
$isInstalled = (isset($this->installedMap[$package->getId()]));
|
|
||||||
|
|
||||||
foreach ($package->getReplaces() as $link) {
|
|
||||||
$obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
|
|
||||||
|
|
||||||
foreach ($obsoleteProviders as $provider) {
|
|
||||||
if ($provider === $package) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
|
|
||||||
$reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
|
|
||||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check implicit obsoletes
|
|
||||||
// for installed packages we only need to check installed/installed problems,
|
|
||||||
// as the others are picked up when looking at the uninstalled package.
|
|
||||||
if (!$isInstalled) {
|
|
||||||
$obsoleteProviders = $this->pool->whatProvides($package->getName(), null);
|
|
||||||
|
|
||||||
foreach ($obsoleteProviders as $provider) {
|
|
||||||
if ($provider === $package) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) {
|
|
||||||
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, (string) $package));
|
|
||||||
} else if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
|
|
||||||
$reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES;
|
|
||||||
$this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, (string) $package));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function obsoleteImpossibleForAlias($package, $provider)
|
|
||||||
{
|
|
||||||
$packageIsAlias = $package instanceof AliasPackage;
|
|
||||||
$providerIsAlias = $provider instanceof AliasPackage;
|
|
||||||
|
|
||||||
$impossible = (
|
|
||||||
($packageIsAlias && $package->getAliasOf() === $provider) ||
|
|
||||||
($providerIsAlias && $provider->getAliasOf() === $package) ||
|
|
||||||
($packageIsAlias && $providerIsAlias && $provider->getAliasOf() === $package->getAliasOf())
|
|
||||||
);
|
|
||||||
|
|
||||||
return $impossible;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds all rules for all update packages of a given package
|
|
||||||
*
|
|
||||||
* @param PackageInterface $package Rules for this package's updates are to
|
|
||||||
* be added
|
|
||||||
* @param bool $allowAll Whether downgrades are allowed
|
|
||||||
*/
|
|
||||||
private function addRulesForUpdatePackages(PackageInterface $package)
|
|
||||||
{
|
|
||||||
$updates = $this->policy->findUpdatePackages($this->pool, $this->installedMap, $package);
|
|
||||||
|
|
||||||
foreach ($updates as $update) {
|
|
||||||
$this->addRulesForPackage($update);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -387,17 +156,9 @@ class Solver
|
||||||
|
|
||||||
$problem = new Problem;
|
$problem = new Problem;
|
||||||
|
|
||||||
if ($rule->getType() == RuleSet::TYPE_JOB) {
|
$problem->addRule($rule);
|
||||||
$job = $this->ruleToJob[$rule->getId()];
|
$problem->addRule($conflict);
|
||||||
|
$this->disableProblem($rule);
|
||||||
$problem->addJobRule($job, $rule);
|
|
||||||
$problem->addRule($conflict);
|
|
||||||
$this->disableProblem($job);
|
|
||||||
} else {
|
|
||||||
$problem->addRule($rule);
|
|
||||||
$problem->addRule($conflict);
|
|
||||||
$this->disableProblem($rule);
|
|
||||||
}
|
|
||||||
$this->problems[] = $problem;
|
$this->problems[] = $problem;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -421,15 +182,8 @@ class Solver
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($assertRule->getType() === RuleSet::TYPE_JOB) {
|
$problem->addRule($assertRule);
|
||||||
$job = $this->ruleToJob[$assertRule->getId()];
|
$this->disableProblem($assertRule);
|
||||||
|
|
||||||
$problem->addJobRule($job, $assertRule);
|
|
||||||
$this->disableProblem($job);
|
|
||||||
} else {
|
|
||||||
$problem->addRule($assertRule);
|
|
||||||
$this->disableProblem($assertRule);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$this->problems[] = $problem;
|
$this->problems[] = $problem;
|
||||||
|
|
||||||
|
@ -463,13 +217,7 @@ class Solver
|
||||||
}
|
}
|
||||||
|
|
||||||
// conflict, but this is a weak rule => disable
|
// conflict, but this is a weak rule => disable
|
||||||
if ($rule->getType() == RuleSet::TYPE_JOB) {
|
$this->disableProblem($rule);
|
||||||
$why = $this->ruleToJob[$rule->getId()];
|
|
||||||
} else {
|
|
||||||
$why = $rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->disableProblem($why);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,10 +227,7 @@ class Solver
|
||||||
foreach ($this->installed->getPackages() as $package) {
|
foreach ($this->installed->getPackages() as $package) {
|
||||||
$this->installedMap[$package->getId()] = $package;
|
$this->installedMap[$package->getId()] = $package;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected function addRulesForJobs()
|
|
||||||
{
|
|
||||||
foreach ($this->jobs as $job) {
|
foreach ($this->jobs as $job) {
|
||||||
switch ($job['cmd']) {
|
switch ($job['cmd']) {
|
||||||
case 'update':
|
case 'update':
|
||||||
|
@ -498,30 +243,12 @@ class Solver
|
||||||
$this->updateMap[$package->getId()] = true;
|
$this->updateMap[$package->getId()] = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'install':
|
|
||||||
if (empty($job['packages'])) {
|
|
||||||
$problem = new Problem();
|
|
||||||
$problem->addJobRule($job);
|
|
||||||
$this->problems[] = $problem;
|
|
||||||
} else {
|
|
||||||
foreach ($job['packages'] as $package) {
|
|
||||||
if (!isset($this->installedMap[$package->getId()])) {
|
|
||||||
$this->addRulesForPackage($package);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$rule = $this->createInstallOneOfRule($job['packages'], Rule::RULE_JOB_INSTALL, $job['packageName']);
|
case 'install':
|
||||||
$this->addRule(RuleSet::TYPE_JOB, $rule);
|
if (!$job['packages']) {
|
||||||
$this->ruleToJob[$rule->getId()] = $job;
|
$problem = new Problem();
|
||||||
}
|
$problem->addRule(new Rule(array(), null, null, $job));
|
||||||
break;
|
$this->problems[] = $problem;
|
||||||
case 'remove':
|
|
||||||
// remove all packages with this name including uninstalled
|
|
||||||
// ones to make sure none of them are picked as replacements
|
|
||||||
foreach ($job['packages'] as $package) {
|
|
||||||
$rule = $this->createRemoveRule($package, Rule::RULE_JOB_REMOVE);
|
|
||||||
$this->addRule(RuleSet::TYPE_JOB, $rule);
|
|
||||||
$this->ruleToJob[$rule->getId()] = $job;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -540,12 +267,7 @@ class Solver
|
||||||
$this->decisionMap = array_fill(0, $this->pool->getMaxId() + 1, 0);
|
$this->decisionMap = array_fill(0, $this->pool->getMaxId() + 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->installedMap as $package) {
|
$this->rules = $this->ruleSetGenerator->getRulesFor($this->jobs, $this->installedMap);
|
||||||
$this->addRulesForPackage($package);
|
|
||||||
$this->addRulesForUpdatePackages($package);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addRulesForJobs();
|
|
||||||
|
|
||||||
foreach ($this->rules as $rule) {
|
foreach ($this->rules as $rule) {
|
||||||
$this->addWatchesToRule($rule);
|
$this->addWatchesToRule($rule);
|
||||||
|
@ -827,7 +549,7 @@ class Solver
|
||||||
|
|
||||||
$this->revert($level);
|
$this->revert($level);
|
||||||
|
|
||||||
$this->addRule(RuleSet::TYPE_LEARNED, $newRule);
|
$this->rules->add($newRule, RuleSet::TYPE_LEARNED);
|
||||||
|
|
||||||
$this->learnedWhy[$newRule->getId()] = $why;
|
$this->learnedWhy[$newRule->getId()] = $why;
|
||||||
|
|
||||||
|
@ -987,12 +709,7 @@ class Solver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($conflictRule->getType() == RuleSet::TYPE_JOB) {
|
$problem->addRule($conflictRule);
|
||||||
$job = $this->ruleToJob[$conflictRule->getId()];
|
|
||||||
$problem->addJobRule($job, $conflictRule);
|
|
||||||
} else {
|
|
||||||
$problem->addRule($conflictRule);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function analyzeUnsolvable($conflictRule, $disableRules)
|
private function analyzeUnsolvable($conflictRule, $disableRules)
|
||||||
|
@ -1058,13 +775,7 @@ class Solver
|
||||||
if ($lastWeakWhy) {
|
if ($lastWeakWhy) {
|
||||||
array_pop($this->problems);
|
array_pop($this->problems);
|
||||||
|
|
||||||
if ($lastWeakWhy->getType() === RuleSet::TYPE_JOB) {
|
$this->disableProblem($lastWeakWhy);
|
||||||
$why = $this->ruleToJob[$lastWeakWhy];
|
|
||||||
} else {
|
|
||||||
$why = $lastWeakWhy;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->disableProblem($why);
|
|
||||||
$this->resetSolver();
|
$this->resetSolver();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1072,11 +783,7 @@ class Solver
|
||||||
|
|
||||||
if ($disableRules) {
|
if ($disableRules) {
|
||||||
foreach ($this->problems[count($this->problems) - 1] as $reason) {
|
foreach ($this->problems[count($this->problems) - 1] as $reason) {
|
||||||
if ($reason['job']) {
|
$this->disableProblem($reason['rule']);
|
||||||
$this->disableProblem($reason['job']);
|
|
||||||
} else {
|
|
||||||
$this->disableProblem($reason['rule']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->resetSolver();
|
$this->resetSolver();
|
||||||
|
@ -1088,14 +795,15 @@ class Solver
|
||||||
|
|
||||||
private function disableProblem($why)
|
private function disableProblem($why)
|
||||||
{
|
{
|
||||||
if ($why instanceof Rule) {
|
$job = $why->getJob();
|
||||||
$why->disable();
|
|
||||||
} else if (is_array($why)) {
|
|
||||||
|
|
||||||
|
if (!$job) {
|
||||||
|
$why->disable();
|
||||||
|
} else {
|
||||||
// disable all rules of this job
|
// disable all rules of this job
|
||||||
foreach ($this->ruleToJob as $ruleId => $job) {
|
foreach ($this->rules as $rule) {
|
||||||
if ($why === $job) {
|
if ($job === $rule->getJob()) {
|
||||||
$this->rules->ruleById($ruleId)->disable();
|
$rule->disable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue