]> cygwin.com Git - cygwin-apps/setup.git/blame - libsolv.cc
Add obsoletes: support
[cygwin-apps/setup.git] / libsolv.cc
CommitLineData
1c159e0a
JT
1/*
2 * Copyright (c) 2017 Jon Turney
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 */
13
14#include "libsolv.h"
15
16#include "solv/solver.h"
17#include "solv/solverdebug.h"
18#include "solv/evr.h"
19
20#include "LogSingleton.h"
1d553f34 21#include <iomanip>
1c159e0a
JT
22
23// ---------------------------------------------------------------------------
24// Utility functions for mapping between Operators and Relation Ids
25// ---------------------------------------------------------------------------
26
27static Id
28Operator2RelId(PackageSpecification::_operators op)
29{
30 switch (op)
31 {
32 case PackageSpecification::Equals:
33 return REL_EQ;
34 case PackageSpecification::LessThan:
35 return REL_LT;
36 case PackageSpecification::MoreThan:
37 return REL_GT;
38 case PackageSpecification::LessThanEquals:
39 return REL_LT | REL_EQ;
40 case PackageSpecification::MoreThanEquals:
41 return REL_GT | REL_EQ;
42 }
43
44 return 0;
45}
46
47static PackageSpecification::_operators
48RelId2Operator(Id id)
49{
50 switch (id)
51 {
52 case REL_EQ:
53 return PackageSpecification::Equals;
54 case REL_LT:
55 return PackageSpecification::LessThan;
56 case REL_GT:
57 return PackageSpecification::MoreThan;
58 case REL_LT | REL_EQ:
59 return PackageSpecification::LessThanEquals;
60 case REL_GT | REL_EQ:
61 return PackageSpecification::MoreThanEquals;
62 }
63
64 return PackageSpecification::Equals;
65}
66
67// ---------------------------------------------------------------------------
68// implements class SolvableVersion
69//
70// a wrapper around a libsolv Solvable
71// ---------------------------------------------------------------------------
72
73const std::string
74SolvableVersion::Name () const
75{
76 Solvable *solvable = pool_id2solvable(pool, id);
77 return std::string(pool_id2str(pool, solvable->name));
78}
79
80const std::string
81SolvableVersion::Canonical_version() const
82{
83 Solvable *solvable = pool_id2solvable(pool, id);
84 return std::string(pool_id2str(pool, solvable->evr));
85}
86
87package_type_t
88SolvableVersion::Type () const
89{
90 Solvable *solvable = pool_id2solvable(pool, id);
91 if (solvable->arch == ARCH_SRC)
92 return package_source;
93 else
94 return package_binary;
95}
96
97const PackageDepends
98SolvableVersion::depends() const
99{
100 Solvable *solvable = pool_id2solvable(pool, id);
101
102 Queue q;
103 queue_init(&q);
104
105 if (repo_lookup_idarray(solvable->repo, id, SOLVABLE_REQUIRES, &q))
106 {
107 // convert
108 PackageDepends dep;
109
110 for (int i = 0; i < q.count; i++)
111 {
112#ifdef DEBUG
113 Log (LOG_PLAIN) << "dep " << std::hex << q.elements[i] << ": " << pool_dep2str(pool, q.elements[i]) << endLog;
114#endif
115
116 const char *name = pool_id2str(pool, q.elements[i]);
117 PackageSpecification *spec = new PackageSpecification (name);
118
119 if (ISRELDEP(id))
120 {
121 Reldep *rd = GETRELDEP(pool, id);
122 spec->setOperator(RelId2Operator(rd->flags));
123 spec->setVersion(pool_id2str(pool, rd->evr));
124 }
125
126 dep.push_back (spec);
127 }
128
129 queue_empty(&q);
130
131 return dep;
132 }
133
134 // otherwise, return an empty depends list
135 static PackageDepends empty_package;
136 return empty_package;
137}
138
139const std::string
140SolvableVersion::SDesc () const
141{
142 Solvable *solvable = pool_id2solvable(pool, id);
143 const char *sdesc = repo_lookup_str(solvable->repo, id, SOLVABLE_SUMMARY);
144 return sdesc;
145}
146
147SolvableVersion
148SolvableVersion::sourcePackage () const
149{
150 if (!id)
151 return SolvableVersion();
152
153 // extract source package id
154 Solvable *solvable = pool_id2solvable(pool, id);
155 Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1);
156 Id spkg_id = repo_lookup_id(solvable->repo, id, spkg_attr);
157
158 // has no such attribute
159 if (!spkg_id)
160 return SolvableVersion();
161
162 return SolvableVersion(spkg_id, pool);
163}
164
165packagesource *
166SolvableVersion::source() const
167{
168 if (!id) {
169 static packagesource empty_source = packagesource();
170 return &empty_source;
171 }
172
173 Solvable *solvable = pool_id2solvable(pool, id);
174 Id psrc_attr = pool_str2id(pool, "solvable:packagesource", 1);
175 return (packagesource *)repo_lookup_num(solvable->repo, id, psrc_attr, 0);
176}
177
178bool
179SolvableVersion::accessible () const
180{
181 // XXX: accessible if archive is locally available, or we know a mirror
182 //
183 // (This seems utterly pointless. Packages which aren't locally available are
184 // removed from the package list. Packages we don't know a mirror for don't
185 // appear in the packagelist.)
186 return TRUE;
187}
188
189package_stability_t
190SolvableVersion::Stability () const
191{
192 Solvable *solvable = pool_id2solvable(pool, id);
193 Id stability_attr = pool_str2id(pool, "solvable:stability", 1);
194 return (package_stability_t)repo_lookup_num(solvable->repo, id, stability_attr, TRUST_UNKNOWN);
195}
196
197bool
198SolvableVersion::operator <(SolvableVersion const &rhs) const
199{
200 return (compareVersions(*this, rhs) < 0);
201}
202
203bool
204SolvableVersion::operator ==(SolvableVersion const &rhs) const
205{
206 return (compareVersions(*this, rhs) == 0);
207}
208
209bool
210SolvableVersion::operator !=(SolvableVersion const &rhs) const
211{
212 return (compareVersions(*this, rhs) != 0);
213}
214
215int
216SolvableVersion::compareVersions(const SolvableVersion &a,
217 const SolvableVersion &b)
218{
219 if (a.id == b.id)
220 return 0;
221
222 // if a and b are different, at least one of them has a pool
223 Pool *pool = a.pool ? a.pool : b.pool;
224
225 Solvable *sa = a.id ? pool_id2solvable(a.pool, a.id) : NULL;
226 Solvable *sb = b.id ? pool_id2solvable(b.pool, b.id) : NULL;
227
228 // empty versions compare as if their version is the empty string
229 Id evra = sa ? sa->evr : pool_str2id(pool, "", 1);
230 Id evrb = sb ? sb->evr : pool_str2id(pool, "", 1);
231
232 return pool_evrcmp(pool, evra, evrb, EVRCMP_COMPARE);
233}
234
235// ---------------------------------------------------------------------------
236// implements class SolverPool
237//
238// a simplified wrapper for libsolv
239// ---------------------------------------------------------------------------
240
241static
242void debug_callback(Pool *pool, void *data, int type, const char *str)
243{
244 if (type & (SOLV_FATAL|SOLV_ERROR))
245 LogPlainPrintf("libsolv: %s", str);
246 else
247 LogBabblePrintf("libsolv: %s", str);
248}
249
250SolverPool::SolverPool()
251{
252 /* create a pool */
253 pool = pool_create();
254
255 pool_setdebugcallback(pool, debug_callback, NULL);
256
257 int level = 1;
258#if DEBUG
259 level = 3;
260#endif
261 pool_setdebuglevel(pool, level);
262
263 /* create the repo to hold installed packages */
264 SolvRepo *installed = getRepo("_installed");
265 pool_set_installed(pool, installed->repo);
266}
267
268SolvRepo *
269SolverPool::getRepo(const std::string &name, bool test)
270{
271 RepoList::iterator i = repos.find(name);
272 if (i != repos.end())
273 return i->second;
274
275 /* create repo if not found */
276 SolvRepo *r = new(SolvRepo);
277 r->repo = repo_create(pool, name.c_str());
278
279 /* create attribute store, with no local pool */
280 r->data = repo_add_repodata(r->repo, 0);
281
282 /* remember if this is a test stability repo */
283 r->test = test;
284
285 repos[name] = r;
286
287 return r;
288}
289
290/*
291 Helper function to convert a PackageDepends list to libsolv dependencies.
292*/
293Id
294SolverPool::makedeps(Repo *repo, PackageDepends *requires)
295{
296 Id deps = 0;
297
298 for (PackageDepends::iterator i = requires->begin();
299 i != requires->end();
300 i++)
301 {
302 Id name = pool_str2id(pool, (*i)->packageName().c_str(), 1);
303
304 if ((*i)->version().size() == 0)
305 {
306 // no relation, so dependency is just on package name
307 deps = repo_addid_dep(repo, deps, name, 0);
308 }
309 else
310 {
311 // otherwise, dependency is on package name with a version condition
312 Id evr = pool_str2id(pool, (*i)->version().c_str(), 1);
313 int rel = pool_rel2id(pool, name, evr, Operator2RelId((*i)->op()), 1);
314
315 deps = repo_addid_dep(repo, deps, rel, 0);
316 }
317 }
318
319 return deps;
320}
321
322SolvableVersion
323SolverPool::addPackage(const std::string& pkgname, const addPackageData &pkgdata)
324{
325 std::string repoName = pkgdata.reponame;
326 bool test = false;
327
328 /* It's simplest to place test packages into a separate repo, and then
329 arrange for that repo to be disabled, if we don't want to consider
330 those packages */
331
332 if (pkgdata.stability == TRUST_TEST)
333 {
334 repoName = pkgdata.reponame + "_test_";
335 test = true;
336 }
337
338 SolvRepo *r = getRepo(repoName, test);
339 Repo *repo = r->repo;
340
341 /* create a solvable */
342 Id s = repo_add_solvable(repo);
343 Solvable *solvable = pool_id2solvable(pool, s);
344
345 /* initialize solvable for this packageo/version/etc. */
346 solvable->name = pool_str2id(pool, pkgname.c_str(), 1);
347 solvable->arch = (pkgdata.type == package_binary) ? ARCH_ANY : ARCH_SRC;
348 solvable->evr = pool_str2id(repo->pool, pkgdata.version.c_str(), 1);
349 solvable->vendor = pool_str2id(repo->pool, pkgdata.vendor.c_str(), 1);
350 solvable->provides = repo_addid_dep(repo, solvable->provides, pool_rel2id(pool, solvable->name, solvable->evr, REL_EQ, 1), 0);
351 if (pkgdata.requires)
352 solvable->requires = makedeps(repo, pkgdata.requires);
20b98f20
JT
353 if (pkgdata.obsoletes)
354 solvable->obsoletes = makedeps(repo, pkgdata.obsoletes);
1c159e0a
JT
355
356 /* a solvable can also store arbitrary attributes not needed for dependency
357 resolution, if we need them */
358
359 Repodata *data = r->data;
360 Id handle = s;
361#if DEBUG
362 Log (LOG_PLAIN) << "solvable " << s << " name " << pkgname << endLog;
363#endif
364
365 /* store short description attribute */
366 repodata_set_str(data, handle, SOLVABLE_SUMMARY, pkgdata.sdesc.c_str());
367 /* store long description attribute */
368 repodata_set_str(data, handle, SOLVABLE_DESCRIPTION, pkgdata.ldesc.c_str());
369
370 /* store source-package attribute */
371 const std::string sname = pkgdata.spkg.packageName();
372 if (!sname.empty())
373 repodata_set_id(data, handle, SOLVABLE_SOURCENAME, pool_str2id(pool, sname.c_str(), 1));
374 else
375 repodata_set_void(data, handle, SOLVABLE_SOURCENAME);
376 /* solvable:sourceevr may also be available from spkg but assumed to be same
377 as evr for the moment */
378
379 /* store source-package id */
380 /* XXX: this assumes we create install package after source package and so can
381 know that id */
382 Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1);
383 repodata_set_id(data, handle, spkg_attr, pkgdata.spkg_id.id);
384
385 /* we could store packagesource information as attributes ...
386
387 e.g.
388 size SOLVABLE_DOWNLOADSIZE
389 pathname SOLVABLE_MEDIAFILE
390 site SOLVABLE_MEDIABASE
391 checksum SOLVABLE_CHECKSUM
392
393 ... but for the moment, we just store a pointer to a packagesource object
394 */
395 Id psrc_attr = pool_str2id(pool, "solvable:packagesource", 1);
396 packagesource *psrc = new packagesource(pkgdata.archive);
397 repodata_set_num(data, handle, psrc_attr, (intptr_t)psrc);
398
399 /* store stability level attribute */
400 Id stability_attr = pool_str2id(pool, "solvable:stability", 1);
401 repodata_set_num(data, handle, stability_attr, pkgdata.stability);
402
403#if 0
404 repodata_internalize(data);
405
406 /* debug: verify the attributes we've just set get retrieved correctly */
407 SolvableVersion sv = SolvableVersion(s, pool);
408 const std::string check_sdesc = sv.SDesc();
409 if (pkgdata.sdesc.compare(check_sdesc) != 0) {
410 Log (LOG_PLAIN) << pkgname << " has sdesc mismatch: '" << pkgdata.sdesc << "' and '"
411 << check_sdesc << "'" << endLog;
412 }
413 if (!sname.empty()) {
414 SolvableVersion check_spkg = sv.sourcePackage();
415 Solvable *check_spkg_solvable = pool_id2solvable(pool, check_spkg.id);
416 std::string check_sname = pool_id2str(pool, check_spkg_solvable->name);
417 if (sname.compare(check_sname) != 0) {
418 Log (LOG_PLAIN) << pkgname << " has spkg mismatch: '" << pkgdata.spkg.packageName()
419 << "' and '" << check_sname << "'" << endLog;
420 }
421 }
422 packagesource *check_archive = sv.source();
423 if (check_archive != psrc)
424 Log (LOG_PLAIN) << pkgname << " has archive mismatch: " << psrc
425 << " and " << check_archive << endLog;
426 package_stability_t check_stability = sv.Stability();
427 if (check_stability != pkgdata.stability) {
428 Log (LOG_PLAIN) << pkgname << " has stability mismatch: " << pkgdata.stability
429 << " and " << check_stability << endLog;
430 }
431#endif
432
433 return SolvableVersion(s, pool);
434}
435
436void
437SolverPool::internalize()
438{
439 /* Make attribute data available to queries */
440 for (RepoList::iterator i = repos.begin();
441 i != repos.end();
442 i++)
443 {
444 repodata_internalize(i->second->data);
445 }
446}
1d553f34
JT
447
448void
449SolverPool::use_test_packages(bool use_test_packages)
450{
451 // Only enable repos containing test packages if wanted
452 for (RepoList::iterator i = repos.begin();
453 i != repos.end();
454 i++)
455 {
456 if (i->second->test)
457 {
458 i->second->repo->disabled = !use_test_packages;
459 }
460 }
461}
462
463// ---------------------------------------------------------------------------
464// implements class SolverSolution
465//
466// A wrapper around the libsolv solver
467// ---------------------------------------------------------------------------
468
469SolverSolution::~SolverSolution()
470{
471 if (solv)
472 {
473 solver_free(solv);
474 solv = NULL;
475 }
476}
477
478static
479std::ostream &operator<<(std::ostream &stream,
480 SolverTransaction::transType type)
481{
482 switch (type)
483 {
484 case SolverTransaction::transInstall:
485 stream << "install";
486 break;
487 case SolverTransaction::transErase:
488 stream << "erase";
489 break;
490 default:
491 stream << "unknown";
492 }
493 return stream;
494}
495
496bool
497SolverSolution::update(SolverTasks &tasks, bool update, bool use_test_packages, bool include_source)
498{
499 Log (LOG_PLAIN) << "solving: " << tasks.tasks.size() << " tasks," <<
500 " update: " << (update ? "yes" : "no") << "," <<
501 " use test packages: " << (use_test_packages ? "yes" : "no") << "," <<
502 " include_source: " << (include_source ? "yes" : "no") << endLog;
503
504 pool.use_test_packages(use_test_packages);
505
506 Queue job;
507 queue_init(&job);
508 // solver accepts a queue containing pairs of (cmd, id) tasks
509 // cmd is job and selection flags ORed together
510 for (SolverTasks::taskList::const_iterator i = tasks.tasks.begin();
511 i != tasks.tasks.end();
512 i++)
513 {
514 const SolvableVersion &sv = (*i).first;
515
516 switch ((*i).second)
517 {
518 case SolverTasks::taskInstall:
519 queue_push2(&job, SOLVER_INSTALL | SOLVER_SOLVABLE, sv.id);
520 break;
521 case SolverTasks::taskUninstall:
522 queue_push2(&job, SOLVER_ERASE | SOLVER_SOLVABLE, sv.id);
523 break;
524 case SolverTasks::taskReinstall:
525 // we don't know how to ask solver for this, so we just add the erase
526 // and install later
527 break;
528 default:
529 Log (LOG_PLAIN) << "unknown task " << (*i).second << endLog;
530 }
531 }
532
533 if (update)
534 queue_push2(&job, SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, 0);
535
536 if (!solv)
537 solv = solver_create(pool.pool);
538
539 solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
540 solver_set_flag(solv, SOLVER_FLAG_ALLOW_DOWNGRADE, 0);
541 solver_solve(solv, &job);
542 queue_free(&job);
543
544 int pcnt = solver_problem_count(solv);
545 solver_printdecisions(solv);
546
547 // get transactions for solution
548 Transaction *t = solver_create_transaction(solv);
549 transaction_order(t, 0);
550 transaction_print(t);
551
552 // massage into SolverTransactions form
553 trans.clear();
554
555 for (int i = 0; i < t->steps.count; i++)
556 {
557 Id id = t->steps.elements[i];
558 SolverTransaction::transType tt = type(t, i);
559 trans.push_back(SolverTransaction(SolvableVersion(id, pool.pool), tt));
560 }
561
562 // add install and remove tasks for anything marked as reinstall
563 for (SolverTasks::taskList::const_iterator i = tasks.tasks.begin();
564 i != tasks.tasks.end();
565 i++)
566 {
567 const SolvableVersion &sv = (*i).first;
568
569 if (((*i).second) == SolverTasks::taskReinstall)
570 {
571 trans.push_back(SolverTransaction(SolvableVersion(sv.id, pool.pool),
572 SolverTransaction::transErase));
573 trans.push_back(SolverTransaction(SolvableVersion(sv.id, pool.pool),
574 SolverTransaction::transInstall));
575 }
576 }
577
578 // if include_source mode is on, also install source for everything we are
579 // installing
580 if (include_source)
581 {
582 // (this uses indicies into the vector, as iterators might become
583 // invalidated by doing push_back)
584 size_t n = trans.size();
585 for (size_t i = 0; i < n; i++)
586 {
587 if (trans[i].type == SolverTransaction::transInstall)
588 {
589 SolvableVersion src_version = trans[i].version.sourcePackage();
590 if (src_version)
591 trans.push_back(SolverTransaction(src_version,
592 SolverTransaction::transInstall));
593 }
594 }
595 }
596
597 if (trans.size())
598 {
599 Log (LOG_PLAIN) << "Augmented Transaction List:" << endLog;
600 for (SolverTransactionList::iterator i = trans.begin ();
601 i != trans.end ();
602 ++i)
603 {
604 Log (LOG_PLAIN) << std::setw(4) << std::distance(trans.begin(), i)
605 << std::setw(8) << i->type
606 << std::setw(48) << i->version.Name()
607 << std::setw(20) << i->version.Canonical_version() << endLog;
608 }
609 }
610
611 transaction_free(t);
612
613 return (pcnt == 0);
614}
615
616const SolverTransactionList &
617SolverSolution::transactions() const
618{
619 return trans;
620}
621
622// Construct a string reporting the problems and solutions
623std::string
624SolverSolution::report() const
625{
626 std::string r = "";
627 int pcnt = solver_problem_count(solv);
628 for (Id problem = 1; problem <= pcnt; problem++)
629 {
630 r += "Problem " + std::to_string(problem) + "/" + std::to_string(pcnt);
631 r += "\n";
632
633 Id probr = solver_findproblemrule(solv, problem);
634 Id dep, source, target;
635 SolverRuleinfo type = solver_ruleinfo(solv, probr, &source, &target, &dep);
636 r += solver_problemruleinfo2str(solv, type, source, target, dep);
637 r += "\n";
638
639 int scnt = solver_solution_count(solv, problem);
640 for (Id solution = 1; solution <= scnt; solution++)
641 {
642 r += "Solution " + std::to_string(solution) + "/" + std::to_string(scnt);
643 r += "\n";
644
645 Id p, rp, element;
646 element = 0;
647 while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0)
648 {
649 r += " - ";
650 r += solver_solutionelement2str(solv, p, rp);
651 r += "\n";
652 }
653 }
654 }
655 return r;
656}
657
658// helper function to map transaction type
659SolverTransaction::transType
660SolverSolution::type(Transaction *trans, int pos)
661{
662 Id tt = transaction_type(trans, trans->steps.elements[pos],
663 SOLVER_TRANSACTION_SHOW_ACTIVE);
664
665 // if active side of transaction is nothing, ask again for passive side of
666 // transaction
667 if (tt == SOLVER_TRANSACTION_IGNORE)
668 tt = transaction_type(trans, trans->steps.elements[pos], 0);
669
670 switch(tt)
671 {
672 case SOLVER_TRANSACTION_INSTALL:
673 case SOLVER_TRANSACTION_REINSTALL:
674 case SOLVER_TRANSACTION_UPGRADE:
675 case SOLVER_TRANSACTION_DOWNGRADE:
676 return SolverTransaction::transInstall;
677 case SOLVER_TRANSACTION_ERASE:
678 case SOLVER_TRANSACTION_REINSTALLED:
679 case SOLVER_TRANSACTION_UPGRADED:
680 case SOLVER_TRANSACTION_DOWNGRADED:
681 return SolverTransaction::transErase;
682 default:
683 Log (LOG_PLAIN) << "unknown transaction type " << std::hex << tt << endLog;
684 case SOLVER_TRANSACTION_IGNORE:
685 return SolverTransaction::transIgnore;
686 }
687};
This page took 0.077611 seconds and 5 git commands to generate.