diff --git a/build/pack.c b/build/pack.c index dc5f84cef0..d7adcb0e2b 100644 --- a/build/pack.c +++ b/build/pack.c @@ -228,12 +228,36 @@ static rpmRC processScriptFiles(rpmSpec spec, Package pkg) return rc; } -static int haveTildeDep(Package pkg) +struct charInDepData { + char c; + int present; +}; + +static rpmRC charInDepCb(void *cbdata, rpmrichParseType type, + const char *n, int nl, const char *e, int el, rpmsenseFlags sense, + rpmrichOp op, char **emsg) { + struct charInDepData *data = cbdata; + if (memchr(e, data->c, el)) + data->present = 1; + + return RPMRC_OK; +} + +static int haveCharInDep(Package pkg, char c) { + struct charInDepData data = {c, 0}; for (int i = 0; i < PACKAGE_NUM_DEPS; i++) { rpmds ds = rpmdsInit(pkg->dependencies[i]); while (rpmdsNext(ds) >= 0) { - if (strchr(rpmdsEVR(ds), '~')) + if (rpmdsIsRich(ds)) { + const char *depstr = rpmdsN(ds); + rpmrichParse(&depstr, NULL, charInDepCb, &data); + } else { + const char *evr = rpmdsEVR(ds); + if (strchr(evr, c)) + data.present = 1; + } + if (data.present) return 1; } } @@ -245,7 +269,12 @@ static int haveRichDep(Package pkg) for (int i = 0; i < PACKAGE_NUM_DEPS; i++) { rpmds ds = rpmdsInit(pkg->dependencies[i]); rpmTagVal tagN = rpmdsTagN(ds); - if (tagN != RPMTAG_REQUIRENAME && tagN != RPMTAG_CONFLICTNAME) + if (tagN != RPMTAG_REQUIRENAME && + tagN != RPMTAG_RECOMMENDNAME && + tagN != RPMTAG_SUGGESTNAME && + tagN != RPMTAG_SUPPLEMENTNAME && + tagN != RPMTAG_ENHANCENAME && + tagN != RPMTAG_CONFLICTNAME) continue; while (rpmdsNext(ds) >= 0) { if (rpmdsIsRich(ds)) @@ -322,9 +351,13 @@ static char *getIOFlags(Package pkg) static void finalizeDeps(Package pkg) { /* check if the package has a dependency with a '~' */ - if (haveTildeDep(pkg)) + if (haveCharInDep(pkg, '~')) (void) rpmlibNeedsFeature(pkg, "TildeInVersions", "4.10.0-1"); + /* check if the package has a dependency with a '^' */ + if (haveCharInDep(pkg, '^')) + (void) rpmlibNeedsFeature(pkg, "CaretInVersions", "4.15.0-1"); + /* check if the package has a rich dependency */ if (haveRichDep(pkg)) (void) rpmlibNeedsFeature(pkg, "RichDependencies", "4.12.0-1"); diff --git a/lib/rpmds.c b/lib/rpmds.c index 01aa1022b1..730a58c357 100644 --- a/lib/rpmds.c +++ b/lib/rpmds.c @@ -1240,6 +1240,9 @@ static const struct rpmlibProvides_s rpmlibProvides[] = { { "rpmlib(TildeInVersions)", "4.10.0-1", ( RPMSENSE_EQUAL), N_("dependency comparison supports versions with tilde.") }, + { "rpmlib(CaretInVersions)", "4.15.0-1", + ( RPMSENSE_EQUAL), + N_("dependency comparison supports versions with caret.") }, { "rpmlib(LargeFiles)", "4.12.0-1", ( RPMSENSE_EQUAL), N_("support files larger than 4GB") }, diff --git a/lib/rpmvercmp.c b/lib/rpmvercmp.c index b3d08faa4a..13857e151d 100644 --- a/lib/rpmvercmp.c +++ b/lib/rpmvercmp.c @@ -33,8 +33,8 @@ int rpmvercmp(const char * a, const char * b) /* loop through each version segment of str1 and str2 and compare them */ while (*one || *two) { - while (*one && !risalnum(*one) && *one != '~') one++; - while (*two && !risalnum(*two) && *two != '~') two++; + while (*one && !risalnum(*one) && *one != '~' && *one != '^') one++; + while (*two && !risalnum(*two) && *two != '~' && *two != '^') two++; /* handle the tilde separator, it sorts before everything else */ if (*one == '~' || *two == '~') { @@ -45,6 +45,21 @@ int rpmvercmp(const char * a, const char * b) continue; } + /* + * Handle caret separator. Concept is the same as tilde, + * except that if one of the strings ends (base version), + * the other is considered as higher version. + */ + if (*one == '^' || *two == '^') { + if (!*one) return -1; + if (!*two) return 1; + if (*one != '^') return 1; + if (*two != '^') return -1; + one++; + two++; + continue; + } + /* If we ran to the end of either, we are finished with the loop */ if (!(*one && *two)) break; diff --git a/tests/rpmvercmp.at b/tests/rpmvercmp.at index 8b32209aa5..1e7c960ea2 100644 --- a/tests/rpmvercmp.at +++ b/tests/rpmvercmp.at @@ -102,6 +102,32 @@ RPMVERCMP(1.0~rc1~git123, 1.0~rc1~git123, 0) RPMVERCMP(1.0~rc1~git123, 1.0~rc1, -1) RPMVERCMP(1.0~rc1, 1.0~rc1~git123, 1) +dnl Basic testcases for caret sorting +RPMVERCMP(1.0^, 1.0^, 0) +RPMVERCMP(1.0^, 1.0, 1) +RPMVERCMP(1.0, 1.0^, -1) +RPMVERCMP(1.0^git1, 1.0^git1, 0) +RPMVERCMP(1.0^git1, 1.0, 1) +RPMVERCMP(1.0, 1.0^git1, -1) +RPMVERCMP(1.0^git1, 1.0^git2, -1) +RPMVERCMP(1.0^git2, 1.0^git1, 1) +RPMVERCMP(1.0^git1, 1.01, -1) +RPMVERCMP(1.01, 1.0^git1, 1) +RPMVERCMP(1.0^20160101, 1.0^20160101, 0) +RPMVERCMP(1.0^20160101, 1.0.1, -1) +RPMVERCMP(1.0.1, 1.0^20160101, 1) +RPMVERCMP(1.0^20160101^git1, 1.0^20160101^git1, 0) +RPMVERCMP(1.0^20160102, 1.0^20160101^git1, 1) +RPMVERCMP(1.0^20160101^git1, 1.0^20160102, -1) + +dnl Basic testcases for tilde and caret sorting +RPMVERCMP(1.0~rc1^git1, 1.0~rc1^git1, 0) +RPMVERCMP(1.0~rc1^git1, 1.0~rc1, 1) +RPMVERCMP(1.0~rc1, 1.0~rc1^git1, -1) +RPMVERCMP(1.0^git1~pre, 1.0^git1~pre, 0) +RPMVERCMP(1.0^git1, 1.0^git1~pre, 1) +RPMVERCMP(1.0^git1~pre, 1.0^git1, -1) + dnl These are included here to document current, arguably buggy behaviors dnl for reference purposes and for easy checking against unintended dnl behavior changes.