]> git.ipfire.org Git - thirdparty/libsolv.git/commitdiff
Add support for REL_CONDA
authorMichael Schroeder <mls@suse.de>
Thu, 28 Mar 2019 14:08:04 +0000 (15:08 +0100)
committerMichael Schroeder <mls@suse.de>
Thu, 28 Mar 2019 14:09:15 +0000 (15:09 +0100)
Still to do: support for chaining versions with ',' or '|'.

bindings/solv.i
ext/testcase.c
src/conda.c
src/conda.h
src/pool.c
src/pool.h
src/poolid.c

index d1143c3beb9f26474318e69eb9045e7396adb48a..00a665b7c3ec8eed1b69016db5f94477e0242002 100644 (file)
@@ -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;
index 4654bcb9e035b618c8ed536ca42beb451b97a332..8edf58ff58c4083c685fdbd7e6fd2441c37c000c 100644 (file)
@@ -406,6 +406,7 @@ struct oplist {
   { REL_ELSE, "<ELSE>" },
   { REL_ERROR, "<ERROR>" },
   { REL_UNLESS, "<UNLESS>" },
+  { REL_CONDA, "<CONDA>" },
   { REL_LT, "<" },
   { 0, 0 }
 };
index fb9533ced67aebe8abc9661274cabf9d6141b67c..30b0f090ffac5649d7293845675dc0427f847041 100644 (file)
 #include <stdarg.h>
 #include <unistd.h>
 #include <string.h>
+#include <sys/types.h>
+#include <regex.h>
 
 #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(&reg, buf, REG_EXTENDED | REG_NOSUB))
+    return 0;
+  r = regexec(&reg, evr, 0, NULL, 0);
+  regfree(&reg);
+  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(&reg, buf, REG_EXTENDED | REG_NOSUB))
+    return 0;
+  r = regexec(&reg, evr, 0, NULL, 0);
+  regfree(&reg);
+  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
index e92557c24e34e6ba9ea1b7e92a322ff4ee7922a2..7233f17bfd25971084e0bd3825a05560c9f15f65 100644 (file)
@@ -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 */
 
index cc96664b4d417ac47178f7e9d15d4c60d55f1361..09354ef69ff41294ba7820546049e8a2c23f727b 100644 (file)
@@ -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;
        }
index d7a91f45fa10841a2672f7b77213c4b07bcfc46c..c90c29ae91529f23b1dd18cdebc14872a25e170d 100644 (file)
@@ -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)
index 4cd09137e8ac47525c02c9f451eaef3db97bb852..52d98c38fa4ac3535f8047a3f4973be100ccacc8 100644 (file)
@@ -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: