However, a (RPM) package has many more properties. Like summary, description, license, package group, etc. All theses properties are not needed for dependency resolution and hence are stored outside of Solvables.
Sat-solver provides the 'Attribute Store' for these properties, internally called 'repodata'. See src/repodata.h
/* create a pool */ Pool *pool = pool_create(); /* create a repo */ Repo *repo = repo_create(pool, "demo"); /* create a solvable */ Id s = repo_add_solvable(repo);
Repodata *data = repo_add_repodata(repo, 0);
The second parameter indicates a 'local pool' for storing strings. By default, strings are stored in the Pool. Setting the second parameter to non-zero will store attribute string values separate from the Pool.
[Q: Whats the technical reason for the local pool?]
First you compute the datanum:
Solvable *solvable = pool_id2solvable(pool, s); Id datanum = (solvable - pool->solvables) - repo->start;
datanum is the reference from the attribute store back to the solvable.
As you can see, the datanum is basically the offset of the solvable within the repo. Remember that the attributes are per-repository (as is the solvable. When calling 'repodata_*' function, use the datanum to represent the solvable.
Extending the store is done with repodata_extend():
repodata_extend(data, solvable - pool->solvables);
[Q: The offset passed to repodata_extend is per-pool. All other repodata functions are per-repo. Why ?]
See src/repodata.h for a complete list of all the repodata_set_*() functions.
Attributes are named and in good sat-solver tradition, everything is an Id.
See knownid.h for predefined attribute names.
repodata_set_void(data, datanum, attr_name);
unsigned int value = 123456; repodata_set_num(data, datanum, attr_name, value);
const char *s = "This is a string"; repodata_set_str(data, datanum, attr_name, s);
If the same string is used multiple times as an attribute value, its more efficient to store it once and use its Id.
Id id = str2id(pool, "value_of_attribute", 1); repodata_set_id(data, datanum, attr_name, id);
A constant can either be of numeric (unsigned int) type
repodata_set_constant(data, datanum, attr_name, 12345);
or an Id:
Id id = str2id(pool, "constant string", 1); repodata_set_constant(data, datanum, attr_name, id);
The attribute store knows about two types of arrays. Arrays of Ids and arrays of strings.
You fill an array by multiple calls to (array of Ids)
repodata_add_idarray(data, datanum, attr_name, id)
or
repodata_add_poolstr_array(data, datanum, attr_name, "value")
Every call will add the value to the end of the array.
[Q: can I mix Id and char* in one array ?]
repodata_internalize(data);
Thats black MM magic. Don't ask.
Hence the read functions are called 'repodata_lookup_*()' and only exist for some attribute types, namely boolean (void), numeric, string, Id and bin_checksum.
The preparation steps are similar to writing, you need a pool, a repository and the solvable reference.
/* create a pool */ Pool *pool = pool_create(); /* create a repo */ Repo *repo = repo_create(pool, "demo"); Solvable *s; /* now populate the repo either from a .solv file: repo_add_solv(Repo *, FILE *); or from the RPM database: repo_add_rpmdb(Repo *, NULL, "/"); and retrieve a Solvable s */
/* get the summary attribute */ const char *summary = repo_lookup_str(s, SOLVABLE_SUMMARY); /* get the buildtime attribute */ unsigned int buildtime = repo_lookup_num(s, SOLVABLE_BUILDTIME);
Both lookup functions return 0 if the attribute isn't set.
The downside of these simple access functions it that you have to know the type of the attribute you're going to retrieve.
repo_lookup then tries to retrieve the attribute value and returns non-zero on success.
You can also use predefined Ids from knownid.h Pay attention to the last parameter to the str2id call. It was 1 when setting the attribute buts its 0 when reading.
This is just a short-path to check for existance of an attribute (name). Passing 0 to str2id means 'do not create if not existing before'. So if attr_name is assigned 0 (ID_NULL) in this call, this attribute is not defined within the pool.
if(repo_lookup(s, attr_name, callback_function, (void *)callback_data) { /* attribute found */ } If the solvable s has the attribute attr_name defined, the callback_function is called passing callback_data. This function is defined as int callback_function( void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv )
with
cbdata: the callback_data from the call to repo_lookup s: the solvable from the call to repo_lookup data: the attribute store associated with the repo key: the attribute name (key->name, an Id) and type (key->type, a REPOKEY_TYPE_*) kv: the attribute value (depending on the key->type)
within the callback_function, you usually look at the key->type and access the value according to the type:
switch(key->type) { case REPOKEY_TYPE_VOID: /* just existance */ ... break; case REPOKEY_TYPE_NUM: /* value is in kv->num */ ... break; case REPOKEY_TYPE_STR: /* value is in kv->str */ ... break; /* ... */ } return 1;
Returning non-zero from the callback_function will end the lookup, signalling you've found what you were looking for.