Implement with/without rich dependencies

Unlike the other rich rependencies they work by evaluating package
sets. This means, "Requires: (foo with bar)" is only fulfilled
by a package that provides both foo and bar. In comparison,
"Requires: (foo and bar)" can be fulfilled by two different
packages.

Without implements set subtraction, e.g. "Requires: (foo without bar)"
is only fulfilled by a package that provides foo but not bar.
This commit is contained in:
Michael Schroeder 2017-08-03 14:06:33 +02:00 committed by Florian Festi
parent 87b8b19eba
commit dac1c70256
3 changed files with 128 additions and 17 deletions

View File

@ -17,6 +17,8 @@
#include "lib/rpmfi_internal.h" /* rpmfiles stuff for now */
#include "lib/misc.h"
#include "lib/backend/dbiset.h"
#include "debug.h"
const char * const RPMVERSION = VERSION;
@ -510,7 +512,7 @@ int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset)
}
/* Cached rpmdb provide lookup, returns 0 if satisfied, 1 otherwise */
static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep)
static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep, dbiIndexSet *matches)
{
const char * Name = rpmdsN(dep);
const char * DNEVR = rpmdsDNEVR(dep);
@ -524,7 +526,7 @@ static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep)
unsigned int keyhash = 0;
/* See if we already looked this up */
if (prune) {
if (prune && !matches) {
keyhash = depCacheKeyHash(dcache, DNEVR);
if (depCacheGetHEntry(dcache, DNEVR, keyhash, &cachedrc, NULL, NULL)) {
rc = *cachedrc;
@ -533,6 +535,8 @@ static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep)
}
}
if (matches)
*matches = dbiIndexSetNew(0);
/*
* See if a filename dependency is a real file in some package,
* taking file state into account: replaced, wrong colored and
@ -547,6 +551,10 @@ static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep)
if (instance && instance == rpmdsInstance(dep))
continue;
}
if (matches) {
dbiIndexSetAppendOne(*matches, headerGetInstance(h), 0, 0);
continue;
}
rpmdsNotify(dep, "(db files)", rc);
break;
}
@ -577,6 +585,10 @@ static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep)
match = 0;
}
if (match) {
if (matches) {
dbiIndexSetAppendOne(*matches, headerGetInstance(h), 0, 0);
continue;
}
rpmdsNotify(dep, "(db provides)", rc);
break;
}
@ -585,13 +597,81 @@ static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep)
}
rc = (h != NULL) ? 0 : 1;
if (matches) {
dbiIndexSetUniq(*matches, 0);
rc = dbiIndexSetCount(*matches) ? 0 : 1;
}
/* Cache the relatively expensive rpmdb lookup results */
/* Caching the oddball non-pruned case would mess up other results */
if (prune)
if (prune && !matches)
depCacheAddHEntry(dcache, xstrdup(DNEVR), keyhash, rc);
return rc;
}
static dbiIndexSet unsatisfiedDependSet(rpmts ts, rpmds dep)
{
dbiIndexSet set1 = NULL, set2 = NULL;
tsMembers tsmem = rpmtsMembers(ts);
rpmsenseFlags dsflags = rpmdsFlags(dep);
if (dsflags & RPMSENSE_RPMLIB)
goto exit;
if (rpmdsIsRich(dep)) {
rpmds ds1, ds2;
rpmrichOp op;
char *emsg = 0;
if (rpmdsParseRichDep(dep, &ds1, &ds2, &op, &emsg) != RPMRC_OK) {
rpmdsNotify(dep, emsg ? emsg : "(parse error)", 1);
_free(emsg);
goto exit;
}
/* only a subset of ops is supported in set mode */
if (op != RPMRICHOP_WITH && op != RPMRICHOP_WITHOUT
&& op != RPMRICHOP_OR && op != RPMRICHOP_SINGLE) {
rpmdsNotify(dep, "(unsupported op in set mode)", 1);
goto exit_rich;
}
set1 = unsatisfiedDependSet(ts, ds1);
if (op == RPMRICHOP_SINGLE)
goto exit_rich;
if (op != RPMRICHOP_OR && dbiIndexSetCount(set1) == 0)
goto exit_rich;
set2 = unsatisfiedDependSet(ts, ds2);
if (op == RPMRICHOP_WITH) {
dbiIndexSetFilterSet(set1, set2, 0);
} else if (op == RPMRICHOP_WITHOUT) {
dbiIndexSetPruneSet(set1, set2, 0);
} else if (op == RPMRICHOP_OR) {
dbiIndexSetAppendSet(set1, set2, 0);
}
exit_rich:
ds1 = rpmdsFree(ds1);
ds2 = rpmdsFree(ds2);
goto exit;
}
/* match database entries */
rpmdbProvides(ts, NULL, dep, &set1);
/* Pretrans dependencies can't be satisfied by added packages. */
if (!(dsflags & RPMSENSE_PRETRANS)) {
rpmte *matches = rpmalAllSatisfiesDepend(tsmem->addedPackages, dep);
if (matches) {
for (rpmte *p = matches; *p; p++)
dbiIndexSetAppendOne(set1, rpmalLookupTE(tsmem->addedPackages, *p), 1, 0);
}
_free(matches);
}
exit:
set2 = dbiIndexSetFree(set2);
return set1 ? set1 : dbiIndexSetNew(0);
}
/**
* Check dep for an unsatisfied dependency.
* @param ts transaction set
@ -643,6 +723,16 @@ retry:
_free(emsg);
goto exit;
}
if (op == RPMRICHOP_WITH || op == RPMRICHOP_WITHOUT) {
/* switch to set mode processing */
dbiIndexSet set = unsatisfiedDependSet(ts, dep);
rc = dbiIndexSetCount(set) ? 0 : 1;
dbiIndexSetFree(set);
ds1 = rpmdsFree(ds1);
ds2 = rpmdsFree(ds2);
rpmdsNotify(dep, "(rich)", rc);
goto exit;
}
if (op == RPMRICHOP_IF) {
if (rpmdsIsRich(ds2)) {
/* check if this is a IF...ELSE combination */
@ -683,7 +773,7 @@ retry:
}
/* See if the rpmdb provides it */
if (rpmdbProvides(ts, dcache, dep) == 0)
if (rpmdbProvides(ts, dcache, dep, NULL) == 0)
goto exit;
/* Search for an unsatisfied dependency. */

View File

@ -1377,10 +1377,12 @@ static struct RichOpComp {
const char * token;
rpmrichOp op;
} const RichOps[] = {
{ "and", RPMRICHOP_AND},
{ "or", RPMRICHOP_OR},
{ "if", RPMRICHOP_IF},
{ "else", RPMRICHOP_ELSE},
{ "and", RPMRICHOP_AND},
{ "or", RPMRICHOP_OR},
{ "if", RPMRICHOP_IF},
{ "else", RPMRICHOP_ELSE},
{ "with", RPMRICHOP_WITH},
{ "without", RPMRICHOP_WITHOUT},
{ NULL, 0 },
};
@ -1420,6 +1422,10 @@ const char *rpmrichOpStr(rpmrichOp op)
return "if";
if (op == RPMRICHOP_ELSE)
return "else";
if (op == RPMRICHOP_WITH)
return "with";
if (op == RPMRICHOP_WITHOUT)
return "without";
return NULL;
}
@ -1467,10 +1473,11 @@ static rpmRC parseSimpleDep(const char **dstrp, char **emsg, rpmrichParseFunctio
return RPMRC_OK;
}
rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata)
static rpmRC rpmrichParseInternal(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata, int *nowithp)
{
const char *p = *dstrp, *pe;
rpmrichOp op = RPMRICHOP_SINGLE, chainop = 0;
int nowith = 0;
if (cb(cbdata, RPMRICH_PARSE_ENTER, p, 0, 0, 0, 0, op, emsg) != RPMRC_OK)
return RPMRC_FAIL;
@ -1491,7 +1498,7 @@ rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, voi
return RPMRC_FAIL;
}
if (*p == '(') {
if (rpmrichParse(&p, emsg, cb, cbdata) != RPMRC_OK)
if (rpmrichParseInternal(&p, emsg, cb, cbdata, &nowith) != RPMRC_OK)
return RPMRC_FAIL;
} else {
if (parseSimpleDep(&p, emsg, cb, cbdata) != RPMRC_OK)
@ -1515,15 +1522,23 @@ rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, voi
rasprintf(emsg, _("Cannot chain different ops"));
return RPMRC_FAIL;
}
if (chainop && op != RPMRICHOP_AND && op != RPMRICHOP_OR) {
if (chainop && op != RPMRICHOP_AND && op != RPMRICHOP_OR &&
op != RPMRICHOP_WITH) {
if (emsg)
rasprintf(emsg, _("Can only chain AND and OR ops"));
rasprintf(emsg, _("Can only chain and/or/with ops"));
return RPMRC_FAIL;
}
if (cb(cbdata, RPMRICH_PARSE_OP, p, pe - p, 0, 0, 0, op, emsg) != RPMRC_OK)
return RPMRC_FAIL;
chainop = op;
p = pe;
if (nowithp && op != RPMRICHOP_WITH && op != RPMRICHOP_WITHOUT && op != RPMRICHOP_OR)
*nowithp = 1;
}
if ((op == RPMRICHOP_WITH || op == RPMRICHOP_WITHOUT) && nowith) {
if (emsg)
rasprintf(emsg, _("Illegal ops in with/without"));
return RPMRC_FAIL;
}
p++;
if (cb(cbdata, RPMRICH_PARSE_LEAVE, *dstrp, p - *dstrp , 0, 0, 0, op, emsg) != RPMRC_OK)
@ -1532,6 +1547,10 @@ rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, voi
return RPMRC_OK;
}
rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata)
{
return rpmrichParseInternal(dstrp, emsg, cb, cbdata, NULL);
}
struct rpmdsParseRichDepData {
rpmds dep;

View File

@ -455,11 +455,13 @@ int rpmdsRpmlibPool(rpmstrPool pool, rpmds * dsp, const void * tblp);
typedef enum rpmrichOp_e {
RPMRICHOP_SINGLE = 1,
RPMRICHOP_AND = 2,
RPMRICHOP_OR = 3,
RPMRICHOP_IF = 4,
RPMRICHOP_ELSE = 5
RPMRICHOP_SINGLE = 1,
RPMRICHOP_AND = 2,
RPMRICHOP_OR = 3,
RPMRICHOP_IF = 4,
RPMRICHOP_ELSE = 5,
RPMRICHOP_WITH = 6,
RPMRICHOP_WITHOUT = 7
} rpmrichOp;
typedef enum rpmrichParseType_e {