diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 9ff6b0764..4359f3102 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -1035,6 +1035,10 @@ class Solver //findrecommendedsuggested(solv); //solver_prepare_solutions(solv); + if ($this->problems) { + throw new SolverProblemsException($this->problems, $this->learnedPool); + } + return $this->createTransaction(); } @@ -1113,6 +1117,8 @@ class Solver protected function addDecision(Literal $l, $level) { + assert($this->decisionMap[$l->getPackageId()] == 0); + if ($l->isWanted()) { $this->decisionMap[$l->getPackageId()] = $level; } else { @@ -1123,6 +1129,9 @@ class Solver protected function addDecisionId($literalId, $level) { $packageId = abs($literalId); + + assert($this->decisionMap[$packageId] == 0); + if ($literalId > 0) { $this->decisionMap[$packageId] = $level; } else { @@ -1165,8 +1174,8 @@ class Solver { $packageId = abs($literalId); return ( - $this->decisionMap[$packageId] > 0 && !($literalId < 0) || - $this->decisionMap[$packageId] < 0 && $literalId > 0 + ($this->decisionMap[$packageId] > 0 && $literalId < 0) || + ($this->decisionMap[$packageId] < 0 && $literalId > 0) ); } diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php new file mode 100644 index 000000000..cbc4fd571 --- /dev/null +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -0,0 +1,65 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver; + +/** + * @author Nils Adermann + */ +class SolverProblemsException extends \RuntimeException +{ + protected $problems; + + public function __construct(array $problems, array $learnedPool) + { + $message = ''; + foreach ($problems as $i => $problem) { + $message .= '['; + foreach ($problem as $why) { + + if (is_int($why) && isset($learnedPool[$why])) { + $rules = $learnedPool[$why]; + } else { + $rules = $why; + } + + if (isset($rules['packages'])) { + $message .= $this->jobToText($rules); + } else { + $message .= '('; + foreach ($rules as $rule) { + if ($rule instanceof Rule) { + if ($rule->getType() == RuleSet::TYPE_LEARNED) { + $message .= 'learned: '; + } + $message .= $rule . ', '; + } else { + $message .= 'String(' . $rule . '), '; + } + } + $message .= ')'; + } + $message .= ', '; + } + $message .= "]\n"; + } + + parent::__construct($message); + } + + public function jobToText($job) + { + //$output = serialize($job); + $output = 'Job(cmd='.$job['cmd'].', target='.$job['packageName'].', packages=['.implode(', ', $job['packages']).'])'; + return $output; + } +} diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index a71feafce..18cb9f4a1 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -19,6 +19,7 @@ use Composer\DependencyResolver\DefaultPolicy; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Solver; +use Composer\DependencyResolver\SolverProblemsException; use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Test\TestCase; @@ -484,6 +485,49 @@ class SolverTest extends TestCase )); } + public function testConflictResultEmpty() + { + $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); + $this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));; + + $packageA->setConflicts(array( + new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'conflicts'), + )); + + $this->reposComplete(); + + $this->request->install('A'); + $this->request->install('B'); + + try { + $transaction = $this->solver->solve($this->request); + $this->fail('Unsolvable conflict did not resolve in exception.'); + } catch (SolverProblemsException $e) { + // @todo: assert problem properties + } + } + + public function testUnsatisfiableRequires() + { + $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); + $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); + + $packageA->setRequires(array( + new Link('A', 'B', new VersionConstraint('>=', '2.0'), 'requires'), + )); + + $this->reposComplete(); + + $this->request->install('A'); + + try { + $transaction = $this->solver->solve($this->request); + $this->fail('Unsolvable conflict did not resolve in exception.'); + } catch (SolverProblemsException $e) { + // @todo: assert problem properties + } + } + protected function reposComplete() { $this->pool->addRepository($this->repoInstalled); @@ -513,5 +557,4 @@ class SolverTest extends TestCase $this->assertEquals($expected, $result); } - }