]>
Commit | Line | Data |
---|---|---|
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" | |
21 | ||
22 | // --------------------------------------------------------------------------- | |
23 | // Utility functions for mapping between Operators and Relation Ids | |
24 | // --------------------------------------------------------------------------- | |
25 | ||
26 | static Id | |
27 | Operator2RelId(PackageSpecification::_operators op) | |
28 | { | |
29 | switch (op) | |
30 | { | |
31 | case PackageSpecification::Equals: | |
32 | return REL_EQ; | |
33 | case PackageSpecification::LessThan: | |
34 | return REL_LT; | |
35 | case PackageSpecification::MoreThan: | |
36 | return REL_GT; | |
37 | case PackageSpecification::LessThanEquals: | |
38 | return REL_LT | REL_EQ; | |
39 | case PackageSpecification::MoreThanEquals: | |
40 | return REL_GT | REL_EQ; | |
41 | } | |
42 | ||
43 | return 0; | |
44 | } | |
45 | ||
46 | static PackageSpecification::_operators | |
47 | RelId2Operator(Id id) | |
48 | { | |
49 | switch (id) | |
50 | { | |
51 | case REL_EQ: | |
52 | return PackageSpecification::Equals; | |
53 | case REL_LT: | |
54 | return PackageSpecification::LessThan; | |
55 | case REL_GT: | |
56 | return PackageSpecification::MoreThan; | |
57 | case REL_LT | REL_EQ: | |
58 | return PackageSpecification::LessThanEquals; | |
59 | case REL_GT | REL_EQ: | |
60 | return PackageSpecification::MoreThanEquals; | |
61 | } | |
62 | ||
63 | return PackageSpecification::Equals; | |
64 | } | |
65 | ||
66 | // --------------------------------------------------------------------------- | |
67 | // implements class SolvableVersion | |
68 | // | |
69 | // a wrapper around a libsolv Solvable | |
70 | // --------------------------------------------------------------------------- | |
71 | ||
72 | const std::string | |
73 | SolvableVersion::Name () const | |
74 | { | |
75 | Solvable *solvable = pool_id2solvable(pool, id); | |
76 | return std::string(pool_id2str(pool, solvable->name)); | |
77 | } | |
78 | ||
79 | const std::string | |
80 | SolvableVersion::Canonical_version() const | |
81 | { | |
82 | Solvable *solvable = pool_id2solvable(pool, id); | |
83 | return std::string(pool_id2str(pool, solvable->evr)); | |
84 | } | |
85 | ||
86 | package_type_t | |
87 | SolvableVersion::Type () const | |
88 | { | |
89 | Solvable *solvable = pool_id2solvable(pool, id); | |
90 | if (solvable->arch == ARCH_SRC) | |
91 | return package_source; | |
92 | else | |
93 | return package_binary; | |
94 | } | |
95 | ||
96 | const PackageDepends | |
97 | SolvableVersion::depends() const | |
98 | { | |
99 | Solvable *solvable = pool_id2solvable(pool, id); | |
100 | ||
101 | Queue q; | |
102 | queue_init(&q); | |
103 | ||
104 | if (repo_lookup_idarray(solvable->repo, id, SOLVABLE_REQUIRES, &q)) | |
105 | { | |
106 | // convert | |
107 | PackageDepends dep; | |
108 | ||
109 | for (int i = 0; i < q.count; i++) | |
110 | { | |
111 | #ifdef DEBUG | |
112 | Log (LOG_PLAIN) << "dep " << std::hex << q.elements[i] << ": " << pool_dep2str(pool, q.elements[i]) << endLog; | |
113 | #endif | |
114 | ||
115 | const char *name = pool_id2str(pool, q.elements[i]); | |
116 | PackageSpecification *spec = new PackageSpecification (name); | |
117 | ||
118 | if (ISRELDEP(id)) | |
119 | { | |
120 | Reldep *rd = GETRELDEP(pool, id); | |
121 | spec->setOperator(RelId2Operator(rd->flags)); | |
122 | spec->setVersion(pool_id2str(pool, rd->evr)); | |
123 | } | |
124 | ||
125 | dep.push_back (spec); | |
126 | } | |
127 | ||
128 | queue_empty(&q); | |
129 | ||
130 | return dep; | |
131 | } | |
132 | ||
133 | // otherwise, return an empty depends list | |
134 | static PackageDepends empty_package; | |
135 | return empty_package; | |
136 | } | |
137 | ||
138 | const std::string | |
139 | SolvableVersion::SDesc () const | |
140 | { | |
141 | Solvable *solvable = pool_id2solvable(pool, id); | |
142 | const char *sdesc = repo_lookup_str(solvable->repo, id, SOLVABLE_SUMMARY); | |
143 | return sdesc; | |
144 | } | |
145 | ||
146 | SolvableVersion | |
147 | SolvableVersion::sourcePackage () const | |
148 | { | |
149 | if (!id) | |
150 | return SolvableVersion(); | |
151 | ||
152 | // extract source package id | |
153 | Solvable *solvable = pool_id2solvable(pool, id); | |
154 | Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1); | |
155 | Id spkg_id = repo_lookup_id(solvable->repo, id, spkg_attr); | |
156 | ||
157 | // has no such attribute | |
158 | if (!spkg_id) | |
159 | return SolvableVersion(); | |
160 | ||
161 | return SolvableVersion(spkg_id, pool); | |
162 | } | |
163 | ||
164 | packagesource * | |
165 | SolvableVersion::source() const | |
166 | { | |
167 | if (!id) { | |
168 | static packagesource empty_source = packagesource(); | |
169 | return &empty_source; | |
170 | } | |
171 | ||
172 | Solvable *solvable = pool_id2solvable(pool, id); | |
173 | Id psrc_attr = pool_str2id(pool, "solvable:packagesource", 1); | |
174 | return (packagesource *)repo_lookup_num(solvable->repo, id, psrc_attr, 0); | |
175 | } | |
176 | ||
177 | bool | |
178 | SolvableVersion::accessible () const | |
179 | { | |
180 | // XXX: accessible if archive is locally available, or we know a mirror | |
181 | // | |
182 | // (This seems utterly pointless. Packages which aren't locally available are | |
183 | // removed from the package list. Packages we don't know a mirror for don't | |
184 | // appear in the packagelist.) | |
185 | return TRUE; | |
186 | } | |
187 | ||
188 | package_stability_t | |
189 | SolvableVersion::Stability () const | |
190 | { | |
191 | Solvable *solvable = pool_id2solvable(pool, id); | |
192 | Id stability_attr = pool_str2id(pool, "solvable:stability", 1); | |
193 | return (package_stability_t)repo_lookup_num(solvable->repo, id, stability_attr, TRUST_UNKNOWN); | |
194 | } | |
195 | ||
196 | bool | |
197 | SolvableVersion::operator <(SolvableVersion const &rhs) const | |
198 | { | |
199 | return (compareVersions(*this, rhs) < 0); | |
200 | } | |
201 | ||
202 | bool | |
203 | SolvableVersion::operator ==(SolvableVersion const &rhs) const | |
204 | { | |
205 | return (compareVersions(*this, rhs) == 0); | |
206 | } | |
207 | ||
208 | bool | |
209 | SolvableVersion::operator !=(SolvableVersion const &rhs) const | |
210 | { | |
211 | return (compareVersions(*this, rhs) != 0); | |
212 | } | |
213 | ||
214 | int | |
215 | SolvableVersion::compareVersions(const SolvableVersion &a, | |
216 | const SolvableVersion &b) | |
217 | { | |
218 | if (a.id == b.id) | |
219 | return 0; | |
220 | ||
221 | // if a and b are different, at least one of them has a pool | |
222 | Pool *pool = a.pool ? a.pool : b.pool; | |
223 | ||
224 | Solvable *sa = a.id ? pool_id2solvable(a.pool, a.id) : NULL; | |
225 | Solvable *sb = b.id ? pool_id2solvable(b.pool, b.id) : NULL; | |
226 | ||
227 | // empty versions compare as if their version is the empty string | |
228 | Id evra = sa ? sa->evr : pool_str2id(pool, "", 1); | |
229 | Id evrb = sb ? sb->evr : pool_str2id(pool, "", 1); | |
230 | ||
231 | return pool_evrcmp(pool, evra, evrb, EVRCMP_COMPARE); | |
232 | } | |
233 | ||
234 | // --------------------------------------------------------------------------- | |
235 | // implements class SolverPool | |
236 | // | |
237 | // a simplified wrapper for libsolv | |
238 | // --------------------------------------------------------------------------- | |
239 | ||
240 | static | |
241 | void debug_callback(Pool *pool, void *data, int type, const char *str) | |
242 | { | |
243 | if (type & (SOLV_FATAL|SOLV_ERROR)) | |
244 | LogPlainPrintf("libsolv: %s", str); | |
245 | else | |
246 | LogBabblePrintf("libsolv: %s", str); | |
247 | } | |
248 | ||
249 | SolverPool::SolverPool() | |
250 | { | |
251 | /* create a pool */ | |
252 | pool = pool_create(); | |
253 | ||
254 | pool_setdebugcallback(pool, debug_callback, NULL); | |
255 | ||
256 | int level = 1; | |
257 | #if DEBUG | |
258 | level = 3; | |
259 | #endif | |
260 | pool_setdebuglevel(pool, level); | |
261 | ||
262 | /* create the repo to hold installed packages */ | |
263 | SolvRepo *installed = getRepo("_installed"); | |
264 | pool_set_installed(pool, installed->repo); | |
265 | } | |
266 | ||
267 | SolvRepo * | |
268 | SolverPool::getRepo(const std::string &name, bool test) | |
269 | { | |
270 | RepoList::iterator i = repos.find(name); | |
271 | if (i != repos.end()) | |
272 | return i->second; | |
273 | ||
274 | /* create repo if not found */ | |
275 | SolvRepo *r = new(SolvRepo); | |
276 | r->repo = repo_create(pool, name.c_str()); | |
277 | ||
278 | /* create attribute store, with no local pool */ | |
279 | r->data = repo_add_repodata(r->repo, 0); | |
280 | ||
281 | /* remember if this is a test stability repo */ | |
282 | r->test = test; | |
283 | ||
284 | repos[name] = r; | |
285 | ||
286 | return r; | |
287 | } | |
288 | ||
289 | /* | |
290 | Helper function to convert a PackageDepends list to libsolv dependencies. | |
291 | */ | |
292 | Id | |
293 | SolverPool::makedeps(Repo *repo, PackageDepends *requires) | |
294 | { | |
295 | Id deps = 0; | |
296 | ||
297 | for (PackageDepends::iterator i = requires->begin(); | |
298 | i != requires->end(); | |
299 | i++) | |
300 | { | |
301 | Id name = pool_str2id(pool, (*i)->packageName().c_str(), 1); | |
302 | ||
303 | if ((*i)->version().size() == 0) | |
304 | { | |
305 | // no relation, so dependency is just on package name | |
306 | deps = repo_addid_dep(repo, deps, name, 0); | |
307 | } | |
308 | else | |
309 | { | |
310 | // otherwise, dependency is on package name with a version condition | |
311 | Id evr = pool_str2id(pool, (*i)->version().c_str(), 1); | |
312 | int rel = pool_rel2id(pool, name, evr, Operator2RelId((*i)->op()), 1); | |
313 | ||
314 | deps = repo_addid_dep(repo, deps, rel, 0); | |
315 | } | |
316 | } | |
317 | ||
318 | return deps; | |
319 | } | |
320 | ||
321 | SolvableVersion | |
322 | SolverPool::addPackage(const std::string& pkgname, const addPackageData &pkgdata) | |
323 | { | |
324 | std::string repoName = pkgdata.reponame; | |
325 | bool test = false; | |
326 | ||
327 | /* It's simplest to place test packages into a separate repo, and then | |
328 | arrange for that repo to be disabled, if we don't want to consider | |
329 | those packages */ | |
330 | ||
331 | if (pkgdata.stability == TRUST_TEST) | |
332 | { | |
333 | repoName = pkgdata.reponame + "_test_"; | |
334 | test = true; | |
335 | } | |
336 | ||
337 | SolvRepo *r = getRepo(repoName, test); | |
338 | Repo *repo = r->repo; | |
339 | ||
340 | /* create a solvable */ | |
341 | Id s = repo_add_solvable(repo); | |
342 | Solvable *solvable = pool_id2solvable(pool, s); | |
343 | ||
344 | /* initialize solvable for this packageo/version/etc. */ | |
345 | solvable->name = pool_str2id(pool, pkgname.c_str(), 1); | |
346 | solvable->arch = (pkgdata.type == package_binary) ? ARCH_ANY : ARCH_SRC; | |
347 | solvable->evr = pool_str2id(repo->pool, pkgdata.version.c_str(), 1); | |
348 | solvable->vendor = pool_str2id(repo->pool, pkgdata.vendor.c_str(), 1); | |
349 | solvable->provides = repo_addid_dep(repo, solvable->provides, pool_rel2id(pool, solvable->name, solvable->evr, REL_EQ, 1), 0); | |
350 | if (pkgdata.requires) | |
351 | solvable->requires = makedeps(repo, pkgdata.requires); | |
352 | ||
353 | /* a solvable can also store arbitrary attributes not needed for dependency | |
354 | resolution, if we need them */ | |
355 | ||
356 | Repodata *data = r->data; | |
357 | Id handle = s; | |
358 | #if DEBUG | |
359 | Log (LOG_PLAIN) << "solvable " << s << " name " << pkgname << endLog; | |
360 | #endif | |
361 | ||
362 | /* store short description attribute */ | |
363 | repodata_set_str(data, handle, SOLVABLE_SUMMARY, pkgdata.sdesc.c_str()); | |
364 | /* store long description attribute */ | |
365 | repodata_set_str(data, handle, SOLVABLE_DESCRIPTION, pkgdata.ldesc.c_str()); | |
366 | ||
367 | /* store source-package attribute */ | |
368 | const std::string sname = pkgdata.spkg.packageName(); | |
369 | if (!sname.empty()) | |
370 | repodata_set_id(data, handle, SOLVABLE_SOURCENAME, pool_str2id(pool, sname.c_str(), 1)); | |
371 | else | |
372 | repodata_set_void(data, handle, SOLVABLE_SOURCENAME); | |
373 | /* solvable:sourceevr may also be available from spkg but assumed to be same | |
374 | as evr for the moment */ | |
375 | ||
376 | /* store source-package id */ | |
377 | /* XXX: this assumes we create install package after source package and so can | |
378 | know that id */ | |
379 | Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1); | |
380 | repodata_set_id(data, handle, spkg_attr, pkgdata.spkg_id.id); | |
381 | ||
382 | /* we could store packagesource information as attributes ... | |
383 | ||
384 | e.g. | |
385 | size SOLVABLE_DOWNLOADSIZE | |
386 | pathname SOLVABLE_MEDIAFILE | |
387 | site SOLVABLE_MEDIABASE | |
388 | checksum SOLVABLE_CHECKSUM | |
389 | ||
390 | ... but for the moment, we just store a pointer to a packagesource object | |
391 | */ | |
392 | Id psrc_attr = pool_str2id(pool, "solvable:packagesource", 1); | |
393 | packagesource *psrc = new packagesource(pkgdata.archive); | |
394 | repodata_set_num(data, handle, psrc_attr, (intptr_t)psrc); | |
395 | ||
396 | /* store stability level attribute */ | |
397 | Id stability_attr = pool_str2id(pool, "solvable:stability", 1); | |
398 | repodata_set_num(data, handle, stability_attr, pkgdata.stability); | |
399 | ||
400 | #if 0 | |
401 | repodata_internalize(data); | |
402 | ||
403 | /* debug: verify the attributes we've just set get retrieved correctly */ | |
404 | SolvableVersion sv = SolvableVersion(s, pool); | |
405 | const std::string check_sdesc = sv.SDesc(); | |
406 | if (pkgdata.sdesc.compare(check_sdesc) != 0) { | |
407 | Log (LOG_PLAIN) << pkgname << " has sdesc mismatch: '" << pkgdata.sdesc << "' and '" | |
408 | << check_sdesc << "'" << endLog; | |
409 | } | |
410 | if (!sname.empty()) { | |
411 | SolvableVersion check_spkg = sv.sourcePackage(); | |
412 | Solvable *check_spkg_solvable = pool_id2solvable(pool, check_spkg.id); | |
413 | std::string check_sname = pool_id2str(pool, check_spkg_solvable->name); | |
414 | if (sname.compare(check_sname) != 0) { | |
415 | Log (LOG_PLAIN) << pkgname << " has spkg mismatch: '" << pkgdata.spkg.packageName() | |
416 | << "' and '" << check_sname << "'" << endLog; | |
417 | } | |
418 | } | |
419 | packagesource *check_archive = sv.source(); | |
420 | if (check_archive != psrc) | |
421 | Log (LOG_PLAIN) << pkgname << " has archive mismatch: " << psrc | |
422 | << " and " << check_archive << endLog; | |
423 | package_stability_t check_stability = sv.Stability(); | |
424 | if (check_stability != pkgdata.stability) { | |
425 | Log (LOG_PLAIN) << pkgname << " has stability mismatch: " << pkgdata.stability | |
426 | << " and " << check_stability << endLog; | |
427 | } | |
428 | #endif | |
429 | ||
430 | return SolvableVersion(s, pool); | |
431 | } | |
432 | ||
433 | void | |
434 | SolverPool::internalize() | |
435 | { | |
436 | /* Make attribute data available to queries */ | |
437 | for (RepoList::iterator i = repos.begin(); | |
438 | i != repos.end(); | |
439 | i++) | |
440 | { | |
441 | repodata_internalize(i->second->data); | |
442 | } | |
443 | } |