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