LIBSOLV-POOL(3)

πŸ’– LIBSOLV-POOL(3) πŸ’–

(from the LIBSOLV manual, bestie β€” yes, we're in the LIBSOLV chapter and it's giving iconic documentation energy 🫢)


🌸 NAME 🌸

libsolv-pool β€” Libsolv's pool object ✨


πŸŽ€ PUBLIC ATTRIBUTES πŸŽ€

Okay so these are the public fields, the little group-chat members of the pool. Some you can read freely, some you should only change through the proper function. Periodt. πŸ’…

  • void *appdata β€” a no-purpose pointer that's free for you, the library user, to use. When the pool is freed, it just discards the pointer. No cap.
  • Stringpool ss β€” the pool of unified strings. 🧡
  • Reldep *rels β€” the pool of unified relation dependencies.
  • int nrels β€” number of allocated relation dependencies.
  • Repo **repos β€” the array of repository pointers, indexed by repository Id.
  • int nrepos β€” number of allocated repository array elements, i.e. the size of the repos array.
  • int urepos β€” number of used (i.e. non-zero) repository array elements.
  • Repo *installed β€” pointer to the repo holding the installed packages. You can read this attribute all you want, but if you want to change it, use pool_set_installed(). πŸ‘‘
  • Solvable *solvables β€” the array of Solvable objects.
  • int nsolvables β€” number of Solvable objects, i.e. the size of the solvables array. Heads up: the array may contain freed solvables, and in that case the repo pointer of the solvable will be zero. πŸ’€
  • int disttype β€” the distribution type of your system, e.g. DISTTYPE_DEB. You're free to read this attribute, but you should use pool_setdisttype() if you want to change it.
  • Id *whatprovidesdata β€” multi-purpose Id storage holding zero terminated arrays of Ids. pool_whatprovides() returns an offset into this data. πŸ”₯
  • Map *considered β€” optional bitmap that can make the library ignore solvables. If a bitmap is set, only solvables that have a set bit in the bitmap at their Id are considered usable.
  • int debugmask β€” a mask that defines which debug events should be reported. pool_setdebuglevel() sets this mask. πŸ› οΈ
  • Datapos pos β€” an object storing some position in the repository data. Functions like dataiterator_set_pos() set this object, and accessing data with a pseudo solvable Id of SOLVID_POS uses it.
  • Queue pooljobs β€” a queue where fixed solver jobs can be stored. These jobs are automatically added when solver_solve() is called; they're useful to store configuration data like which packages should be multiversion installed. 🫢

✨ CREATION AND DESTRUCTION ✨

  • Pool *pool_create(); β€” create a new instance of a pool. Slay. 🌈
  • void pool_free(Pool *pool); β€” free a pool and all of the data it contains, e.g. the solvables, repositories, strings. It's giving cleanup, and also it's giving finality. πŸ’…

πŸ› οΈ DEBUGGING AND ERROR REPORTING πŸ› οΈ

🧸 Constants 🧸

  • SOLV_FATAL β€” report the error and call exit(1) afterwards. You cannot mask this level. Reports to stderr instead of stdout. 😭
  • SOLV_ERROR β€” used to report errors. Reports to stderr instead of stdout.
  • SOLV_WARN β€” used to report warnings.
  • SOLV_DEBUG_STATS β€” used to report statistical data.
  • SOLV_DEBUG_RULE_CREATION β€” used to report information about the solver's creation of rules.
  • SOLV_DEBUG_PROPAGATE β€” used to report information about the solver's unit rule propagation process.
  • SOLV_DEBUG_ANALYZE β€” used to report information about the solver's learnt rule generation mechanism.
  • SOLV_DEBUG_UNSOLVABLE β€” used to report information about the solver dealing with conflicting rules.
  • SOLV_DEBUG_SOLUTIONS β€” used to report information about the solver creating solutions to solve problems.
  • SOLV_DEBUG_POLICY β€” used to report information about the solver searching for an optimal solution.
  • SOLV_DEBUG_RESULT β€” used by the debug functions to output results.
  • SOLV_DEBUG_JOB β€” used to report information about the job rule generation process.
  • SOLV_DEBUG_SOLVER β€” used to report information about what the solver is currently doing.
  • SOLV_DEBUG_TRANSACTION β€” used to report information about the transaction generation and ordering process.
  • SOLV_DEBUG_TO_STDERR β€” write debug messages to stderr instead of stdout.

πŸͺ„ Functions πŸͺ„

  • void pool_debug(Pool *pool, int type, const char *format, ...); β€” report a message of the type type. You can filter debug messages by setting a debug mask.
  • void pool_setdebuglevel(Pool *pool, int level); β€” set a predefined debug mask. A higher level generally means more bits in the mask are set, thus more messages are printed. 🎚️
  • void pool_setdebugmask(Pool *pool, int mask); β€” set the debug mask to filter debug messages.
  • int pool_error(Pool *pool, int ret, const char *format, ...); β€” set the pool's error string. The ret value is simply used as a return value of the function so that you can write code like return pool_error(...);. If the debug mask contains the SOLV_ERROR bit, pool_debug() is also called with the message and type SOLV_ERROR. 🚨
  • extern char *pool_errstr(Pool *pool); β€” return the current error string stored in the pool. Like with the libc's errno value, the string is only meaningful after a function returned an error.
  • void pool_setdebugcallback(Pool *pool, void (*debugcallback)(Pool *, void *data, int type, const char *str), void *debugcallbackdata); β€” set a custom debug callback function. Instead of writing to stdout or stderr, the callback function will be called. πŸͺ„

✨ POOL CONFIGURATION ✨

Okay bestie, this is where you configure the whole pool vibe. It's giving setup era, and we're doing it right. πŸ’…

🌈 Constants 🌈

These are the constants you can use, no cap:

  • DISTTYPE_RPM β€” For systems where rpm is the low-level package manager. rpm girlies, this one's for you. πŸŽ€
  • DISTTYPE_DEB β€” For systems where dpkg is the low-level package manager. Debian besties rise. πŸ¦‹
  • DISTTYPE_ARCH β€” For systems that use the arch linux package manager. Arch btw. ⭐
  • DISTTYPE_HAIKU β€” For systems that use haiku packages. Haiku, you're iconic. 🌸
  • POOL_FLAG_PROMOTEEPOCH β€” Promote the epoch of the providing dependency to the requesting dependency if it does not contain an epoch. This was used at some time in old rpm versions, and modern systems should never need it. Lowkey ancient history. πŸ’€
  • POOL_FLAG_FORBIDSELFCONFLICTS β€” Disallow the installation of packages that conflict with themselves. Debian always allows self-conflicting packages, rpm used to forbid them but switched to also allowing them recently. So it's a whole saga. 😭
  • POOL_FLAG_OBSOLETEUSESPROVIDES β€” Make obsolete type dependency match against provides instead of just the name and version of packages. Very old versions of rpm used the name/version, then it got switched to provides and later switched back again to just name/version. History lesson fr fr. πŸ“š
  • POOL_FLAG_IMPLICITOBSOLETEUSESPROVIDES β€” An implicit obsoletes is the internal mechanism to remove the old package on an update. The default is to remove all packages with the same name; rpm-5 switched to also removing packages providing the same name. The way rpm-5 did its own thing. πŸ’…
  • POOL_FLAG_OBSOLETEUSESCOLORS β€” Rpm's multilib implementation (used in RedHat and Fedora) distinguishes between 32bit and 64bit packages (the terminology is that they have a different color). If obsoleteusescolors is set, packages with different colors will not obsolete each other. It's giving 32-bit vs 64-bit drama. 🎨
  • POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS β€” Same as POOL_FLAG_OBSOLETEUSESCOLORS, but used to find out if packages of the same name can be installed in parallel. For current Fedora systems, POOL_FLAG_OBSOLETEUSESCOLORS should be false and POOL_FLAG_IMPLICITOBSOLETEUSESCOLORS should be true (this is the default if FEDORA is defined when libsolv is compiled). Fedora girlies check this. βœ…
  • POOL_FLAG_NOINSTALLEDOBSOLETES β€” New versions of rpm consider the obsoletes of installed packages when checking for dependency, thus you may not install a package that is obsoleted by some other installed package, unless you also erase the other package. So don't try it unless you're ready to erase. 🚫
  • POOL_FLAG_HAVEDISTEPOCH β€” Mandriva added a new field called distepoch that gets checked in version comparison if the epoch/version/release of two packages are the same. Mandriva really said "one more field." ✨
  • POOL_FLAG_NOOBSOLETESMULTIVERSION β€” If a package is installed in multiversionmode, rpm used to ignore both the implicit obsoletes and the obsolete dependency of a package. This was changed to ignoring just the implicit obsoletes, thus you may install multiple versions of the same name, but obsoleted packages still get removed. So you can have multiples, but obsoleted ones still get yeeted. 🧹
  • POOL_FLAG_ADDFILEPROVIDESFILTERED β€” Make the addfileprovides method only add files from the standard locations (i.e. the bin and etc directories). This is useful if you have only few packages that use non-standard file dependencies, but you still want the fast speed that addfileprovides() generates. Filtered but fast, we love. ⚑
  • POOL_FLAG_NOWHATPROVIDESSHRINK β€” Do not unify identical provider lists when the whatprovides index is created. This makes creating the index faster, at the price of using more memory. Speed vs memory, classic tradeoff. 🧠

πŸ› οΈ Functions πŸ› οΈ

  • int pool_setdisttype(Pool *pool, int disttype); β€” Set the package type of your system. The disttype is used for example to define package comparison semantics. Libsolv's default disttype should match the package manager of your system, so you only need to use this function if you want to use the library to solve packaging problems for different systems. This function returns the old disttype on success, and -1 if the new disttype is not supported.
⚠️ Note that any pool_setarch and pool_setarchpolicy calls need to come after the pool_setdisttype call, as they make use of the noarch/any/all architecture id.
  • int pool_set_flag(Pool *pool, int flag, int value); β€” Set a flag to a new value. Returns the old value of the flag. πŸ’–
  • int pool_get_flag(Pool *pool, int flag); β€” Get the value of a pool flag. See the constants section about the meaning of the flags. πŸ‘‘
  • void pool_set_rootdir(Pool *pool, const char *rootdir); β€” Set a specific root directory. Some library functions support a flag that tells the function to prepend the rootdir to file and directory names. 🏠
  • const char *pool_get_rootdir(Pool *pool); β€” Return the current value of the root directory. 🌈
  • char *pool_prepend_rootdir(Pool *pool, const char *dir); β€” Prepend the root directory to the dir argument string. The returned string has been newly allocated and needs to be freed after use. Don't forget to free, bestie. 🫢
  • char *pool_prepend_rootdir_tmp(Pool *pool, const char *dir); β€” Same as pool_prepend_rootdir, but uses the pool's temporary space for allocation. Temp space, no cleanup required. ✨
  • void pool_set_installed(Pool *pool, Repo *repo); β€” Set which repository should be treated as the "installed" repository, i.e. the one that holds information about the installed packages. This is the one that knows what's already on your system. πŸ’…
  • void pool_set_languages(Pool *pool, const char **languages, int nlanguages); β€” Set the language of your system. The library provides lookup functions that return localized strings, for example for package descriptions. You can set an array of languages to provide a fallback mechanism if one language is not available. Multilingual queen behavior. 🌍
  • void pool_setarch(Pool *pool, const char *arch); β€” Set the architecture of your system. The architecture is used to determine which packages are installable and which packages cannot be installed. The arch argument is normally the "machine" value of the "uname" system call. πŸ–₯️
  • void pool_setarchpolicy(Pool *, const char *); β€” Set the architecture policy for your system. This is the general version of pool_setarch (in fact pool_setarch calls pool_setarchpolicy internally). See the section about architecture policies for more information. πŸ—οΈ
  • void pool_addvendorclass(Pool *pool, const char **vendorclass); β€” Add a new vendor equivalence class to the system. A vendor equivalence class defines if an installed package of one vendor can be replaced by a package coming from a different vendor. The vendorclass argument must be a NULL terminated array of strings. See the section about vendor policies for more information. Vendor drama, but make it official. 🀝
  • void pool_setvendorclasses(Pool *pool, const char **vendorclasses); β€” Set all allowed vendor equivalences. The vendorclasses argument must be an NULL terminated array consisting of all allowed classes concatenated. Each class itself must be NULL terminated, thus the last class ends with two NULL elements, one to finish the class and one to finish the list of classes. So many NULLs, but that's the assignment. 😭
  • void pool_set_custom_vendorcheck(Pool *pool, int (*vendorcheck)(Pool *, Solvable *, Solvable *)); β€” Define a custom vendor check mechanism. You can use this if libsolv's internal vendor equivalence class mechanism does not match your needs. Custom means custom, periodt. πŸ’…
  • void pool_setloadcallback(Pool *pool, int (*cb)(Pool *, Repodata *, void *), void *loadcbdata); β€” Define a callback function that gets called when repository metadata needs to be loaded on demand. See the section about on demand loading in the libsolv-repodata manual. On-demand loading is highkey useful. ⚑
  • void pool_setnamespacecallback(Pool *pool, Id (*cb)(Pool *, void *, Id, Id), void *nscbdata); β€” Define a callback function to implement custom namespace support. See the section about namespace dependencies. 🫢

πŸ’– ID POOL MANAGEMENT πŸ’–

Okay bestie, this is where the pool keeps all its little identity cards. Every string, every relation β€” they all get an Id. Let's meet them πŸ’…

✨ Constants ✨

  • ID_EMPTY β€” the Id of the empty string, and it is always Id 1. Like, forever. No cap.
  • REL_LT β€” represents a < relation.
  • REL_EQ β€” represents a = relation.
  • REL_GT β€” represents a > relation. And the cute part? You can or-together combos of REL_GT, REL_EQ, and REL_LT to build literally any relation you want. mix and match, it's giving build-a-bear 🧸
  • REL_AND β€” a boolean AND operation. The name and evr parts of the relation can be two sub-dependencies, and packages must match both parts. both of them. obsessed.
  • REL_OR β€” a boolean OR operation. The name and evr parts of the relation can be two sub-dependencies, and packages can match any part. pick whichever, it's fine ✨
  • REL_WITH β€” like REL_AND, but packages must match both dependencies simultaneously. There's a whole section about boolean dependencies if you want the deep lore.
  • REL_NAMESPACE β€” a special namespace relation. See the namespace dependencies section for the tea.
  • REL_ARCH β€” an architecture filter dependency. The name part is a sub-dependency, and the evr part is the Id of an architecture that the matching packages must have. heads up: this is an exact match ignoring architecture policies.
  • REL_FILECONFLICT β€” an internal file conflict dependency, used to represent file conflicts. Check out the pool_add_fileconflicts_deps() function if you're curious.
  • REL_COND β€” a conditional dependency. The name sub-dependency is only considered if the evr sub-dependency is fulfilled. Boolean dependencies section has more.
  • REL_UNLESS β€” a conditional dependency, but flipped: the name sub-dependency is only considered if the evr sub-dependency is not fulfilled.
  • REL_COMPAT β€” a compat dependency used in Haiku to represent version ranges. The name part is the actual version, and the evr part is the backwards compatibility version.
  • REL_KIND β€” a pseudo dependency that limits the solvables to a specific kind. The kind is expected to be a prefix of the solvable name, so like patch:foo would be of kind patch. fyi REL_KIND is only supported in the selection functions.
  • REL_MULTIARCH β€” a debian multiarch annotation. The most common value for the evr part is any.
  • REL_ELSE β€” the else part of a REL_COND or REL_UNLESS dependency. boolean dependencies section, go look πŸ‘€
  • REL_ERROR β€” an illegal dependency. sounds bad and it is, but it's genuinely useful for encoding dependency parse errors.

🫢 Functions 🫢

Id pool_str2id(Pool *pool, const char *str, int create);

Add a string to the pool of unified strings and get the Id of that string back. If create is zero, new strings won't be added to the pool β€” instead you get Id 0. sorry bestie, no new friends.

Id pool_strn2id(Pool *pool, const char *str, unsigned int len, int create);

Same as pool_str2id, but only len characters of the string are used. perfect for adding substrings to the pool, slay βœ‚οΈ

Id pool_rel2id(Pool *pool, Id name, Id evr, int flags, int create);

Create a relational dependency from two other dependencies, name and evr, plus a flag. Peep the REL_ constants for the supported flags. And just like with pool_str2id, create decides whether new dependencies get added or you get Id zero back instead.

Id pool_id2langid(Pool *pool, Id id, const char *lang, int create);

Attach a language suffix to a string Id. Use this to make language keyname Ids out of keynames β€” it's functionally equivalent to converting the id argument to a string, adding a : character and the lang argument to that string, then converting the whole result back into an Id. same energy, fewer steps πŸ’«

const char *pool_id2str(const Pool *pool, Id id);

Convert an Id back into a string. If the Id is a relational Id, the name part gets converted instead.

const char *pool_id2rel(const Pool *pool, Id id);

Return the relation string of a relational Id. Returns an empty string if the Id you passed isn't a relation. no relation, no string 😭

const char *pool_id2evr(const Pool *pool, Id id);

Return the evr part of a relational Id as a string. Again, empty string if the passed Id isn't a relation.

const char *pool_dep2str(Pool *pool, Id id);

Convert an Id back into a string. If the passed Id belongs to a relation, you get a string representing the relation. ⚠️ and note: in that case the string is allocated on the pool's temporary space.

void pool_freeidhashes(Pool *pool);

Free the hashes used to unify strings and relations. Use this to save memory when you know for a fact you'll never create new strings and relations again. goodbye hashes, it was real πŸ₯²

void pool_get_idcounts(Pool *pool, unsigned int *nstringsp, unsigned int *nrelsp);

Return the number of string Ids and relation Ids currently chillin' in the pool.

void pool_presize_idhashes(Pool *pool, unsigned int nstrings, unsigned int nrels);

Presize the hashes so that nstrings/nrels new strings/relations can be added without the hashes needing to grow. planning ahead is highkey iconic.


πŸ¦‹ SOLVABLE FUNCTIONS πŸ¦‹

Solvable *pool_id2solvable(const Pool *pool, Id p);

Convert a solvable Id into a pointer to the solvable data. ⚠️ heads up though: that pointer may become invalid if new solvables are created or old ones get deleted, because the array storing all solvables might get reallocated. don't hold onto it like it's your emotional support pointer, bestie.

Id pool_solvable2id(const Pool *pool, Solvable *s);

Convert a pointer to the solvable data back into a solvable Id. the round trip ✨

const char *pool_solvid2str(Pool *pool, Id p);

Return a string representing the solvable with the Id p. The string will be some canonical representation of the solvable β€” usually a combo of the name, the version, and the architecture. it's giving package business card πŸ’Œ

const char *pool_solvable2str(Pool *pool, Solvable *s);

Same as pool_solvid2str, except instead of the Id you pass a pointer to the solvable. same string, different entry point 🫢


πŸ’œ DEPENDENCY MATCHING πŸ’œ

✨ Constants ✨

  • EVRCMP_COMPARE β€” compare all parts of the version, and treat missing parts as empty strings.
  • EVRCMP_MATCH_RELEASE β€” a special mode for rpm version string matching. If a version misses a release part, it matches all releases. In that case the special values -2 and 2 get returned, depending on which of the two versions didn't have a release part. yes, those are real numbers, no cap.
  • EVRCMP_MATCH β€” a generic match where missing parts always match. very forgiving, very demure.
  • EVRCMP_COMPARE_EVONLY β€” only compare the epoch and the version parts, ignore the release part entirely. not me ignoring the release πŸ™ˆ

🌸 Functions 🌸

int pool_evrcmp(const Pool *pool, Id evr1id, Id evr2id, int mode);

Compare two version Ids. Returns -1 if the first version is less than the second, 0 if they're identical, and 1 if the first is bigger than the second. classic three-way answer, we love a decisive function πŸ’…

int pool_evrcmp_str(const Pool *pool, const char *evr1, const char *evr2, int mode);

Same as pool_evrcmp(), but with strings instead of Ids. string girlies rise 🌈

int pool_evrmatch(const Pool *pool, Id evrid, const char *epoch, const char *version, const char *release);

Match a version Id against an epoch, a version, and a release string. Passing NULL means that part should match everything. wildcard behaviour, periodt.

int pool_match_dep(Pool *pool, Id d1, Id d2);

Returns 1 if the dependency d1 (the provider) is matched by the dependency d2, otherwise 0. For two dependencies to match, both the name parts must match and the version range described by the evr parts must overlap. it's a two-step vibe check and both steps matter πŸ”₯

int pool_match_nevr(Pool *pool, Solvable *s, Id d);

Like pool_match_dep, but the provider is the "self-provides" dependency of the Solvable s, i.e. the dependency sβ†’name = sβ†’evr. self-love as a matching strategy, honestly iconic πŸ‘‘

int pool_satisfieddep_map(Pool *pool, const Map *map, Id dep);

Returns 1 if the dependency dep is satisfied by the packages specified in map, otherwise 0. the map said yes or the map said no, there is no maybe ✨

✨ WHATPROVIDES INDEX ✨

Okay bestie, imagine every single dependency in your pool asking "okay but WHO actually provides me tho?" β€” this is the index that answers that. It maps dependency Ids β†’ the set of packages that provide that dependency. Iconic.

void pool_createwhatprovides(Pool *pool);

Builds that index. Mapping dep Ids β†’ sets of packages providing them. Slay.

void pool_freewhatprovides(Pool *pool);

Yeets the whatprovides index to save memory. Free your RAM, bestie, it's self-care. πŸŽ€

Id pool_whatprovides(Pool *pool, Id d);

Returns an offset into the Pool's whatprovidesdata array. The solvables whose Ids are stored starting at that offset are the ones that provide dependency d. The solvable list is zero terminated β€” so you know where it stops, no guessing. πŸ’…

Id *pool_whatprovides_ptr(Pool *pool, Id d);

Instead of handing you the offset, it hands you the pointer to the Ids sitting at that offset. BUT β€” huge but β€” this pointer has a very limited validity time. Any call that adds new values to the whatprovidesdata area may reallocate the array, and then your pointer is just... a ghost. πŸ‘» Not me warning you twice.

Id pool_queuetowhatprovides(Pool *pool, Queue *q);

Takes the contents of Queue q and drops them at the END of the whatprovidesdata array, then gives you back the offset into the array. Off you go. ✨

void pool_addfileprovides(Pool *pool);

Some package managers (looking at you, rpm πŸ‘€) let dependencies point at files contained in other packages. To let libsolv handle those efficiently, you call this method after creating and reading all repositories β€” not before, bestie, timing matters. What it does: scans all dependencies for file names, then scans all packages for matching files. If a filename gets matched up, it's added to the provides list of the corresponding package. πŸ¦‹

void pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst);

Same energy as pool_addfileprovides, but the added Ids get returned in two Queues instead: idq for every repository except the one containing the "installed" packages, and idqinst for that latter one. Why tho? Because you can stash this info in the meta section of the repositories to speed up the next time the repository is loaded and addfileprovides gets called. Efficiency? Ate. πŸ”₯

void pool_set_whatprovides(pool, Id id, Id offset);

Manually set an entry in the whatprovides index. And no β€” you'll never do this for package dependencies, because those entries are created by calling pool_createwhatprovides(). That's not your job. But this function? Super useful for namespace provides if you don't wanna use a namespace callback to lazily set the provides. The offset argument is an offset in the whatprovides array, which means you can flex "1" as a false value and "2" as a true value. Cute little trick. πŸ’œ

void pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr);

Clears the cache of the providers for namespace dependencies matching namespace ns. If the evr argument is non-zero, only the namespace dependency for exactly that dependency gets cleared β€” otherwise all matching namespace dependencies get cleared. See the section about Namespace dependencies for the full tea. πŸ“

void pool_add_fileconflicts_deps(Pool *pool, Queue *conflicts);

So here's the drama: some package managers like rpm report conflicts when a package installation overwrites a file of another installed package with different content. 😭 And because file content info is not stored in the repository metadata, those conflicts can only be detected after the packages are downloaded. Libsolv provides a function to check for such conflicts: pool_findfileconflicts(). If conflicts are found, they can be added as special REL_FILECONFLICT provides dependencies, so that the solver will know about the conflict when it is re-run. Problem detected, problem handled. πŸ‘‘


🧰 UTILITY FUNCTIONS 🧰

char *pool_alloctmpspace(Pool *pool, int len);

Allocates space on the pool's temporary space area. Careful bestie: this space has a limited lifetime β€” it gets automatically freed after a fixed amount (currently 16) of other pool_alloctmpspace() calls are done. So don't hold onto it like it's forever, because it is not. πŸ’€

void pool_freetmpspace(Pool *pool, const char *space);

Gives the space allocated with pool_alloctmpspace back to the system. You do not have to use this function, since the space is automatically reclaimed anyway. But it can be useful if you wanna extend the lifetime of other pointers to the pool's temporary space area. Handy. 🫢

const char *pool_bin2hex(Pool *pool, const unsigned char *buf, int len);

Converts some binary data into hexadecimal, returning a string allocated in the pool's temporary space area. Hex girl summer. 🌈

char *pool_tmpjoin(Pool *pool, const char *str1, const char *str2, const char *str3);

Joins three strings and returns the result in the pool's temporary space area. And yes, you can pass NULL arguments if you just wanna join fewer strings. Flexible queen. ✨

char *pool_tmpappend(Pool *pool, const char *str1, const char *str2, const char *str3);

Like pool_tmpjoin(), but if the first argument is the last allocated space in the pool's temporary space area, it will be replaced with the result of the join and no new temporary space slot gets used. So you can join more than three strings with a combo of one pool_tmpjoin() and multiple pool_tmpappend() calls. Note: the str1 pointer is no longer usable after the call. It's gone, bestie, say bye. πŸ™ˆ


πŸ” DATA LOOKUP πŸ”

πŸŽ€ Constants πŸŽ€

  • SOLVID_POS β€” Use the data position stored in the pool for the lookup instead of looking up the data of a solvable.
  • SOLVID_META β€” Use the data stored in the meta section of a repository (or repodata area) instead of looking up the data of a solvable. Heads up: this constant does not work for the pool's lookup functions β€” use it for the repo's or repodata's lookup functions instead. It's just listed here for completeness. πŸ’…

πŸ’– Functions πŸ’–

const char *pool_lookup_str(Pool *pool, Id solvid, Id keyname);

Returns the string value stored under the attribute keyname in solvable solvid. String delivered. 🎁

unsigned long long pool_lookup_num(Pool *pool, Id solvid, Id keyname, unsigned long long notfound);

Returns the 64bit unsigned number stored under the attribute keyname in solvable solvid. And if no such number exists? You get the value of the notfound argument instead. So you always get something, no empty-handed moments. ✨

Id pool_lookup_id(Pool *pool, Id solvid, Id keyname);

Returns the Id stored under the attribute keyname in solvable solvid. Short, sweet, an Id. πŸ’œ

int pool_lookup_idarray(Pool *pool, Id solvid, Id keyname, Queue *q);

Fills queue q with the contents of the Id array stored under the attribute keyname in solvable solvid. Returns "1" if an array was found β€” otherwise the queue will be empty and "0" gets returned. Binary outcome, no in-between. πŸ¦‹

int pool_lookup_void(Pool *pool, Id solvid, Id keyname);

Returns "1" if a void value is stored under the attribute keyname in solvable solvid, otherwise "0". Void check, queen. 🌸

const char *pool_lookup_checksum(Pool *pool, Id solvid, Id keyname, Id *typep);

Returns the checksum stored under the attribute keyname in solvable solvid. The type of the checksum gets returned through the typep pointer. If no such checksum is found, NULL comes back and the type is set to zero. Also ngl, the result is stored in the Pool's temporary space area β€” so remember the lifetime thing from earlier, okay? πŸ’…

const unsigned char *pool_lookup_bin_checksum(Pool *pool, Id solvid, Id keyname, Id *typep);

Returns the checksum stored under the attribute keyname in solvable solvid, but as binary data. You can use the returned type to calculate the length of the checksum. And the best part: no temporary space area is needed. Periodt. πŸ”₯

const char *pool_lookup_deltalocation(Pool *pool, Id solvid, unsigned int *medianrp);

This is a utility lookup function that returns the delta location for a delta rpm. Here's the catch: solvables cannot store deltas, so you have to use SOLVID_POS as argument and set the Pool's datapos pointer to point at valid delta rpm data. No shortcuts, bestie. πŸ‘‘

void pool_search(Pool *pool, Id solvid, Id keyname, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata);

Performs a search over all data stored in the pool. You can limit the search area using the solvid and keyname arguments. The values can optionally be matched against the match argument β€” pass NULL if you do not want that matching to happen. See the Dataiterator manpage for the possible match modes and for the flags argument. For all (matching) values, the callback function gets called with the cbdata callback argument plus the data describing the value. Callback girlies, this one's for you. πŸŽ€


πŸ“‹ JOB AND SELECTION FUNCTIONS πŸ“‹

Okay so, a Job consists of two Ids: how and what. The how part describes the action, the job flags, and the selection method β€” while the what part is the input for the selection. A Selection is a queue consisting of multiple jobs (which means the number of elements in the queue must be a multiple of two β€” do the math bestie, it has to line up ✨). See the Solver manpage for more info about jobs.

const char *pool_job2str(Pool *pool, Id how, Id what, Id flagmask);

Converts a job into a string. Useful for debugging purposes. The flagmask can be used to mask the flags of the job: use "0" if you don't wanna see such flags, "-1" to see all flags, or a combination of the flags you want to see. Your view, your choice. πŸ‘‘

void pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what);

Returns a list of solvables that the specified job selects. Drop them in the queue, boom. πŸ’…

int pool_isemptyupdatejob(Pool *pool, Id how, Id what);

Returns "1" if the job is an update job that does not work with any installed package β€” meaning the job is basically a no-op. You can use this to turn no-op update jobs into install jobs (as done by package managers like "zypper"). Sneaky but valid. 😌

const char *pool_selection2str(Pool *pool, Queue *selection, Id flagmask);

Converts a selection into a string. Useful for debugging purposes. See the pool_job2str() function for the flagmask argument. Same flagmask energy, different day. 🌸


🎁 ODDS AND ENDS 🎁

void pool_freeallrepos(Pool *pool, int reuseids);

Frees all repos from the pool (including all solvables). If reuseids is true, all Ids of the solvables are free to be reused the next time solvables are created. Recycling era, we love to see it. ♻️

void pool_clear_pos(Pool *pool);

Clears the data position stored in the pool. Position? Cleared. Gone. πŸ’€


πŸ›οΈ ARCHITECTURE POLICIES πŸ›οΈ

An architecture policy defines a list of architectures that can be installed on the system, plus the relationship between them (i.e. the ordering, the hierarchy, the whole vibe). Architectures can be delimited with three different characters:

  • ':' β€” No relationship between the architectures, like at all. A package of one architecture can not be replaced with one of the other architecture. They're strangers. 🚫
  • '>' β€” The first architecture is better than the second one. An installed package of the second architecture may be replaced with one from the first architecture, and vice versa. The solver will select the better architecture if the versions are the same. Main character architecture energy, honestly. πŸ‘‘
  • '=' β€” The two architectures are freely exchangeable. Used to define aliases for architectures. Twins basically. πŸ¦‹

An example would be 'x86_64:i686=athlon>i586'. Let's decode that tea: it means x86_64 packages can only be replaced by other x86_64 packages; i686 packages can be replaced by i686 and i586 packages (but i686 packages will be preferred ✨); and athlon is another name for the i686 architecture. The way that whole string is just architecture lore? Iconic.

You can turn off the architecture replacement checks with the Solver's SOLVER_FLAG_ALLOW_ARCHCHANGE flag. Your pool, your rules. πŸ’…

πŸ’– VENDOR POLICIES πŸ’–

okay so here's the tea bestie: different vendors usually compile their packages with different features, right? so Libsolv is like, no, we're not swapping an installed package from one vendor for a package from a totally different vendor. only same-vendor-to-same-vendor replacements allowed. also β€” and this is the important part β€” while the version of a package is normally set by the upstream project, the release chunk of the version is set by the vendor's own package maintainer. so doing version comparisons across different vendors? literally meaningless, don't even try it. πŸ™ˆ

when we say "vendor" here, we mean the SOLVABLE_VENDOR string stored in each solvable. sometimes a vendor changes its name, or a bunch of vendors form a group and coordinate their package building together β€” so libsolv gives you a way to declare that a whole squad of vendors is compatible. you do that by defining vendor equivalence classes: packages from a vendor in one class can be replaced by packages from any other vendor in that same class. slay. πŸ’…

you can have multiple equivalence classes, and the set of allowed vendor changes for one installed package is computed by building the union of all the equivalence classes that the installed package's vendor belongs to. math but make it fashion. ✨

and if you want to just switch the whole vibe off, you can disable the vendor replacement checks with the Solver's SOLVER_FLAG_ALLOW_VENDORCHANGE flag.


🧚 BOOLEAN DEPENDENCIES 🧚

Boolean Dependencies let you build complex expressions out of simple dependencies. heads up though β€” depending on which package manager you're on, only a subset of these may be useful. for example, debian currently only allows an "OR" expression. noted, no cap.

REL_OR

true if either the first dependency or the second one is true. this is the one you want for package dependencies like "Requires", where you can say that either one of the packages needs to be installed.

REL_AND

true if both dependencies are true. the packages fulfilling them may be totally different ones, i.e. "Supplements: perl REL_AND python" is true if both a package providing perl and a package providing python are installed.

REL_WITH

true if both dependencies are true and they're fulfilled by the same package. so "Supplements: perl REL_WITH python" would only be true if there's a package installed that provides both dependencies β€” like some kind of multi-language interpreter. one package doing the absolute most. πŸ‘‘

REL_COND

true if the first dependency is true or the second dependency is false. "A REL_COND B" is equivalent to "A REL_OR (NOT B)" β€” except that libsolv doesn't expose "NOT". awkward but we move.

REL_UNLESS

true if the first dependency is true and the second dependency is false. "A REL_UNLESS B" is equivalent to "A REL_AND (NOT B)" β€” again, except libsolv doesn't expose "NOT".

REL_ELSE

the "else" part of a "REL_COND" or "REL_UNLESS" dependency. it has to sit directly in the evr part of the condition, e.g. "foo REL_COND (bar REL_ELSE baz)".

  • for "REL_COND" this is equivalent to writing "(foo REL_COND bar) REL_AND (bar REL_OR baz)".
  • for "REL_UNLESS" this is equivalent to writing "(foo REL_UNLESS bar) REL_OR (bar REL_AND baz)".

each sub-dependency of a boolean dependency can itself be a boolean dependency, so you can chain them together to build truly elaborate dependencies. nesting dolls but make it dependency resolution πŸŽ€


🌈 NAMESPACE DEPENDENCIES 🌈

Namespace dependencies are how you implement dependencies on attributes that live outside of libsolv entirely. the classic example: a dependency on the language the user has set. these types of dependencies are usually only used for "Conflicts" or "Supplements" dependencies, since the underlying package manager has no idea how to deal with them. it's giving "not my department". πŸ’€

if the library needs to evaluate a namespace dependency, it calls the namespace callback function set in the pool. that callback can return a set of packages that "provide" the dependency. and if the dependency is provided by the system itself, the returned set should consist of just the system solvable β€” that's Solvable Id 1.

the returned set of packages must be handed back as an offset into the whatprovidesdata array. you can use the pool_queuetowhatprovides function to convert a queue into exactly that kind of offset. to make your life easier when writing the callback, the return values "0" and "1" are not interpreted as an offset:

  • "0" means there's no package in the return set.
  • "1" means just the system solvable is in the set.

the returned set gets cached, so for each namespace dependency the callback is only called once. if you need to flush that cache β€” say, because the user switched to a different language β€” use the pool_flush_namespaceproviders() function. cache cleared, fresh start, we love a reset. πŸ¦‹


✨ AUTHOR ✨

Michael Schroeder <mls@suse.de>


and that's a wrap on the whole page, bestie!! 🌸 you just read the entire LIBSOLV-POOL(3) manual β€” source and version: libsolv, dated 10/21/2026. iconic behavior, honestly. go forth and pool responsibly. πŸ’œπŸ«Ά

πŸ“¦ where this page comes from