From: Michael Schroeder Date: Thu, 28 Mar 2019 14:08:04 +0000 (+0100) Subject: Add support for REL_CONDA X-Git-Tag: 0.7.4~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bcb3762386ae114787fd48886484555ff1d82ff8;p=thirdparty%2Flibsolv.git Add support for REL_CONDA Still to do: support for chaining versions with ',' or '|'. --- diff --git a/bindings/solv.i b/bindings/solv.i index d1143c3b..00a665b7 100644 --- a/bindings/solv.i +++ b/bindings/solv.i @@ -1008,6 +1008,7 @@ typedef int Id; %constant int REL_ERROR; %constant int REL_WITHOUT; %constant int REL_UNLESS; +%constant int REL_CONDA; typedef struct { Pool* const pool; diff --git a/ext/testcase.c b/ext/testcase.c index 4654bcb9..8edf58ff 100644 --- a/ext/testcase.c +++ b/ext/testcase.c @@ -406,6 +406,7 @@ struct oplist { { REL_ELSE, "" }, { REL_ERROR, "" }, { REL_UNLESS, "" }, + { REL_CONDA, "" }, { REL_LT, "<" }, { 0, 0 } }; diff --git a/src/conda.c b/src/conda.c index fb9533ce..30b0f090 100644 --- a/src/conda.c +++ b/src/conda.c @@ -16,8 +16,12 @@ #include #include #include +#include +#include #include "pool.h" +#include "repo.h" +#include "util.h" #include "conda.h" static const char * @@ -56,13 +60,21 @@ endpart(const char *seg, const char *end) } /* C implementation of the version comparison code in conda/models/version.py */ +/* startswith == 1 : check if s1 starts with s2 */ static int -solv_vercmp_conda(const char *s1, const char *q1, const char *s2, const char *q2) +solv_vercmp_conda(const char *s1, const char *q1, const char *s2, const char *q2, int startswith) { const char *s1p, *s2p; const char *s1e, *s2e; int r, isfirst; + const char *q2end = 0; + if (startswith) + { + for (q2end = q2; q2end > s2; q2end--) + if (q2end[-1] != '.' && q2end[-1] != '-' && q2end[-1] != '_') + break; + } for (;;) { while (s1 < q1 && (*s1 == '.' || *s1 == '-' || *s1 == '_')) @@ -71,14 +83,18 @@ solv_vercmp_conda(const char *s1, const char *q1, const char *s2, const char *q2 s2++; if (s1 == q1 && s2 == q2) return 0; + if (startswith && s2 == q2) + return 0; /* find end of component */ s1e = endseg(s1, q1); s2e = endseg(s2, q2); - + for (isfirst = 1; ; isfirst = 0) { if (s1 == s1e && s2 == s2e) break; + if (s2 == q2end) + return 0; s1p = endpart(s1, s1e); s2p = endpart(s2, s2e); /* prepend 0 if not numeric */ @@ -154,30 +170,27 @@ solv_vercmp_conda(const char *s1, const char *q1, const char *s2, const char *q2 } } -int -pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode) +static int +pool_evrcmp_conda_int(const char *evr1, const char *evr1e, const char *evr2, const char *evr2e, int startswith) { static char zero[2] = {'0', 0}; - int r; const char *s1, *s2; const char *r1, *r2; - - if (evr1 == evr2) - return 0; + int r; /* split and compare epoch */ - for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++) + for (s1 = evr1; s1 < evr1e && *s1 >= '0' && *s1 <= '9'; s1++) ; - for (s2 = evr2; *s2 >= '0' && *s2 <= '9'; s2++) + for (s2 = evr2; s2 < evr2e && *s2 >= '0' && *s2 <= '9'; s2++) ; - if (s1 == evr1 || *s1 != '!') + if (s1 == evr1 || s1 == evr1e || *s1 != '!') s1 = 0; - if (s2 == evr1 || *s2 != '!') + if (s2 == evr1 || s2 == evr2e || *s2 != '!') s2 = 0; if (s1 || s2) { r = solv_vercmp_conda(s1 ? evr1 : zero, s1 ? s1 : zero + 1, - s2 ? evr2 : zero, s2 ? s2 : zero + 1); + s2 ? evr2 : zero, s2 ? s2 : zero + 1, 0); if (r) return r; if (s1) @@ -186,13 +199,13 @@ pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode evr2 = s2 + 1; } /* split into version/localversion */ - for (s1 = evr1, r1 = 0; *s1; s1++) + for (s1 = evr1, r1 = 0; s1 < evr1e; s1++) if (*s1 == '+') r1 = s1; - for (s2 = evr2, r2 = 0; *s2; s2++) + for (s2 = evr2, r2 = 0; s2 < evr2e; s2++) if (*s2 == '+') r2 = s2; - r = solv_vercmp_conda(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2); + r = solv_vercmp_conda(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2, r2 ? 0 : startswith); if (r) return r; if (!r1 && !r2) @@ -201,14 +214,190 @@ pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode return -1; if (r1 && !r2) return 1; - return solv_vercmp_conda(r1 + 1, s1, r2 + 1, s2); + return solv_vercmp_conda(r1 + 1, s1, r2 + 1, s2, startswith); } -#if 0 -/* return true if solvable s matches the spec */ +int +pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode) +{ + if (evr1 == evr2) + return 0; + return pool_evrcmp_conda_int(evr1, evr1 + strlen(evr1), evr2, evr2 + strlen(evr2), 0); +} + +static int +regexmatch(const char *evr, const char *version, size_t versionlen) +{ + regex_t reg; + char *buf = solv_malloc(versionlen + 1); + int r; + + memcpy(buf, version, versionlen); + buf[versionlen] = 0; + if (regcomp(®, buf, REG_EXTENDED | REG_NOSUB)) + return 0; + r = regexec(®, evr, 0, NULL, 0); + regfree(®); + return r == 0; +} + +static int +globmatch(const char *evr, const char *version, size_t versionlen) +{ + regex_t reg; + char *buf = solv_malloc(2 * versionlen + 3); + size_t i, j; + int r; + + buf[0] = '^'; + j = 1; + for (i = 0, j = 1; i < versionlen; i++) + { + if (version[i] == '.' || version[i] == '+' || version[i] == '*') + buf[j++] = version[i] == '*' ? '.' : '\\'; + buf[j++] = version[i]; + } + buf[j++] = '$'; + buf[j] = 0; + if (regcomp(®, buf, REG_EXTENDED | REG_NOSUB)) + return 0; + r = regexec(®, evr, 0, NULL, 0); + regfree(®); + return r == 0; +} + +/* return true if solvable s matches the version */ +/* see conda/models/version.py */ +static int +solvable_conda_matchversion_single(Solvable *s, const char *version, size_t versionlen) +{ + const char *evr; + size_t i; + int r; + + if (versionlen == 0 || (versionlen == 1 && *version == '*')) + return 1; /* matches every version */ + evr = pool_id2str(s->repo->pool, s->evr); + if (versionlen >= 2 && version[0] == '^' && version[versionlen - 1] == '$') + return regexmatch(evr, version, versionlen); + if (version[0] == '=' || version[0] == '<' || version[0] == '>' || version[0] == '!' || version[0] == '~') + { + int flags = 0; + int oplen; + if (version[0] == '=') + flags = version[1] == '=' ? REL_EQ : 8; + else if (version[0] == '!' || version[0] == '~') + { + if (version[1] != '=') + return 0; + flags = version[0] == '!' ? REL_LT | REL_GT : 9; + } + else if (version[0] == '<' || version[0] == '>') + { + flags = version[0] == '<' ? REL_LT : REL_GT; + if (version[1] == '=') + flags |= REL_EQ; + } + else + return 0; + oplen = flags == 8 || flags == REL_LT || flags == REL_GT ? 1 : 2; + if (versionlen < oplen + 1) + return 0; + version += oplen; + versionlen -= oplen; + if (version[0] == '=' || version[0] == '<' || version[0] == '>' || version[0] == '!' || version[0] == '~') + return 0; /* bad chars after op */ + if (versionlen >= 2 && version[versionlen - 2] == '.' && version[versionlen - 1] == '*') + { + if (flags == 8 || flags == (REL_GT | REL_EQ)) + versionlen -= 2; + else if (flags == (REL_LT | REL_GT)) + { + versionlen -= 2; + flags = 10; + } + else + return 0; + } + if (flags < 8) + { + /* we now have an op and a version */ + r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 0); + if (r < 0) + return (flags & REL_LT) ? 1 : 0; + if (r == 0) + return (flags & REL_EQ) ? 1 : 0; + if (r > 0) + return (flags & REL_GT) ? 1 : 0; + return 0; + } + if (flags == 8 || flags == 10) /* startswith, not-startswith */ + { + r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 1); + return flags == 8 ? r == 0 : r != 0; + } + else if (flags == 9) /* compatible release op */ + { + r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 0); + if (r < 0) + return 0; + /* split off last component */ + while (versionlen > 0 && version[versionlen - 1] != '.') + versionlen--; + if (versionlen < 2) + return 0; + versionlen--; + r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 1); + return r == 0 ? 1 : 0; + } + return 0; + } + + /* do we have a '*' in the version */ + for (i = 0; i < versionlen; i++) + if (version[i] == '*') + { + for (i++; i < versionlen; i++) + if (version[i] != '*') + break; + if (i < versionlen) + return globmatch(evr, version, versionlen); + } + + if (versionlen > 1 && version[versionlen - 1] == '*') + { + /* startswith */ + while (versionlen > 0 && version[versionlen - 1] == '*') + versionlen--; + while (versionlen > 0 && version[versionlen - 1] == '.') + versionlen--; + r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 1); + return r == 0 ? 1 : 0; + } + /* do we have a '@' in the version? */ + for (i = 0; i < versionlen; i++) + if (version[i] == '@') + return strncmp(evr, version, versionlen) == 0 && evr[versionlen] == 0; + r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 0); + return r == 0 ? 1 : 0; +} + + +/* return true if solvable s matches the version */ /* see conda/models/match_spec.py */ int -solvable_conda_matchspec(Solvable *s, const char *spec) +solvable_conda_matchversion(Solvable *s, const char *version) { + const char *build; + size_t vl; + /* split off build */ + if ((build = strchr(version, ' ')) != 0) + { + vl = build - version; + while (*build == ' ') + build++; + } + else + vl = strlen(version); + return solvable_conda_matchversion_single(s, version, vl); } -#endif diff --git a/src/conda.h b/src/conda.h index e92557c2..7233f17b 100644 --- a/src/conda.h +++ b/src/conda.h @@ -14,6 +14,7 @@ #define LIBSOLV_CONDA_H int pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode); +int solvable_conda_matchversion(Solvable *s, const char *version); #endif /* LIBSOLV_CONDA_H */ diff --git a/src/pool.c b/src/pool.c index cc96664b..09354ef6 100644 --- a/src/pool.c +++ b/src/pool.c @@ -27,6 +27,9 @@ #include "util.h" #include "bitmap.h" #include "evr.h" +#ifdef ENABLE_CONDA +#include "conda.h" +#endif #define SOLVABLE_BLOCK 255 @@ -1284,6 +1287,23 @@ pool_addrelproviders(Pool *pool, Id d) queue_push(&plist, p); } break; +#ifdef ENABLE_CONDA + case REL_CONDA: + wp = pool_whatprovides(pool, name); + if (evr) + { + const char *evrstr = pool_id2str(pool, evr); + pp = pool->whatprovidesdata + wp; + while ((p = *pp++) != 0) + { + if (solvable_conda_matchversion(pool->solvables + p, evrstr)) + queue_push(&plist, p); + else + wp = 0; + } + } + break; +#endif default: break; } diff --git a/src/pool.h b/src/pool.h index d7a91f45..c90c29ae 100644 --- a/src/pool.h +++ b/src/pool.h @@ -233,6 +233,7 @@ struct s_Pool { #define REL_ERROR 27 /* parse errors and the like */ #define REL_WITHOUT 28 #define REL_UNLESS 29 /* AND_NOT */ +#define REL_CONDA 30 #if !defined(__GNUC__) && !defined(__attribute__) # define __attribute__(x) diff --git a/src/poolid.c b/src/poolid.c index 4cd09137..52d98c38 100644 --- a/src/poolid.c +++ b/src/poolid.c @@ -218,6 +218,8 @@ pool_id2rel(const Pool *pool, Id id) return " KIND "; case REL_ELSE: return pool->disttype == DISTTYPE_RPM ? " else " : " ELSE "; + case REL_CONDA: + return " "; case REL_ERROR: return " ERROR "; default: