From dac1c702562f57bd7ab080172d7e228cda25a819 Mon Sep 17 00:00:00 2001 From: Michael Schroeder Date: Thu, 3 Aug 2017 14:06:33 +0200 Subject: [PATCH] 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. --- lib/depends.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++--- lib/rpmds.c | 35 +++++++++++++----- lib/rpmds.h | 12 ++++--- 3 files changed, 128 insertions(+), 17 deletions(-) diff --git a/lib/depends.c b/lib/depends.c index 78df0a708..6e274a3c5 100644 --- a/lib/depends.c +++ b/lib/depends.c @@ -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. */ diff --git a/lib/rpmds.c b/lib/rpmds.c index 29610e72c..6e8bf2ca0 100644 --- a/lib/rpmds.c +++ b/lib/rpmds.c @@ -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; diff --git a/lib/rpmds.h b/lib/rpmds.h index 07c3f253c..fc314e19a 100644 --- a/lib/rpmds.h +++ b/lib/rpmds.h @@ -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 {