guix_mirror_bot pushed a commit to branch master
in repository guix.

commit f046a71cf96431422c3975334afc8a5b7cf6dbe5
Author: Danny Milosavljevic <[email protected]>
AuthorDate: Sun Dec 14 15:38:39 2025 +0100

    gnu: Add libsolv.
    
    * gnu/packages/patches/libsolv-conda-variant-priorization.patch: New file.
    * gnu/local.mk (dist_patch_DATA): Add reference to it.
    * gnu/packages/package-management.scm (libsolv): New variable.
    [source]: Use patch.
    
    Change-Id: Ie6cb43385b3489804f9a8fd8e1ddf1d2bb50f4cd
---
 gnu/local.mk                                       |   1 +
 gnu/packages/package-management.scm                |  46 +++
 .../libsolv-conda-variant-priorization.patch       | 455 +++++++++++++++++++++
 3 files changed, 502 insertions(+)

diff --git a/gnu/local.mk b/gnu/local.mk
index c87c83230b..ada9f9ed08 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1712,6 +1712,7 @@ dist_patch_DATA =                                         
\
   %D%/packages/patches/librewolf-compare-paths.patch           \
   %D%/packages/patches/librewolf-neuter-locale-download.patch  \
   %D%/packages/patches/librewolf-use-system-wide-dir.patch     \
+  %D%/packages/patches/libsolv-conda-variant-priorization.patch        \
   %D%/packages/patches/libvirt-add-install-prefix.patch        \
   %D%/packages/patches/libvirt-respect-modules-path.patch      \
   %D%/packages/patches/libzmf-doxygen-1.14.patch               \
diff --git a/gnu/packages/package-management.scm 
b/gnu/packages/package-management.scm
index 4f5b811059..434448c7e7 100644
--- a/gnu/packages/package-management.scm
+++ b/gnu/packages/package-management.scm
@@ -97,8 +97,10 @@
   #:use-module (gnu packages jupyter)
   #:use-module (gnu packages less)
   #:use-module (gnu packages libedit)
+  #:use-module (gnu packages libffi)
   #:use-module (gnu packages linux)
   #:use-module (gnu packages lisp)
+  #:use-module (gnu packages logging)
   #:use-module (gnu packages lua)
   #:use-module (gnu packages man)
   #:use-module (gnu packages markup)
@@ -113,6 +115,7 @@
   #:use-module (gnu packages pkg-config)
   #:use-module (gnu packages polkit)
   #:use-module (gnu packages popt)
+  #:use-module (gnu packages pretty-print)
   #:use-module (gnu packages python)
   #:use-module (gnu packages python-build)
   #:use-module (gnu packages python-check)
@@ -1661,6 +1664,49 @@ it easy to create independent environments even for C 
libraries.  Conda is
 written entirely in Python.")
     (license license:bsd-3)))
 
+(define-public libsolv
+  (package
+    (name "libsolv")
+    (version "0.7.35")
+    (source
+     (origin
+       (method git-fetch)
+       (uri (git-reference
+             (url "https://github.com/openSUSE/libsolv";)
+             (commit version)))
+       (file-name (git-file-name name version))
+       (sha256
+        (base32 "0gfzh9qzb7z4z7kr8fj0gafky0zvnrr6j6rb9fhmvcxvss6h4w8c"))
+       (patches
+        (search-patches "libsolv-conda-variant-priorization.patch"))))
+    (build-system cmake-build-system)
+    (arguments
+     (list
+      #:configure-flags
+      #~(list "-DENABLE_CONDA=ON"
+              "-DWITH_LIBXML2=ON"
+              "-DENABLE_LZMA_COMPRESSION=ON"
+              "-DENABLE_BZIP2_COMPRESSION=ON"
+              "-DENABLE_ZSTD_COMPRESSION=ON"
+              (string-append "-DZSTD_INCLUDE_DIRS="
+                             (assoc-ref %build-inputs "zstd")
+                             "/include")
+              (string-append "-DZSTD_LIBRARY="
+                             (assoc-ref %build-inputs "zstd")
+                             "/lib/libzstd.so"))))
+    (native-inputs
+     (list pkg-config))
+    (inputs
+     (list libxml2
+           xz
+           bzip2
+           `(,zstd "lib")))
+    (home-page "https://github.com/openSUSE/libsolv";)
+    (synopsis "Library for solving package dependencies")
+    (description
+     "Libsolv is a library for solving package dependencies using a SAT solver.
+It is used by the RPM package manager and the Mamba/Conda package managers.")
+    (license license:bsd-3)))
 (define-public conan
   (package
     (name "conan")
diff --git a/gnu/packages/patches/libsolv-conda-variant-priorization.patch 
b/gnu/packages/patches/libsolv-conda-variant-priorization.patch
new file mode 100644
index 0000000000..4e72698dd8
--- /dev/null
+++ b/gnu/packages/patches/libsolv-conda-variant-priorization.patch
@@ -0,0 +1,455 @@
+Author: Wolf Vollprecht <[email protected]>
+Date: Wed Jun 30 12:19:34 2021 +0200
+Subject: Conda variant prioritization for libsolv.
+SPDX-License-Identifier: BSD-3-Clause
+
+This patch adds timestamp-based tie-breaking for conda packages when other
+criteria (version, build number, etc.) are equal.  It ensures that packages
+with later timestamps are preferred.
+
+Origin: 
<https://github.com/conda-forge/libsolv-feedstock/blob/main/recipe/conda_variant_priorization.patch>
+
+This patch is also included in the libsolv package from conda's defaults
+channel (repo.anaconda.com/pkgs/main).  Verified by extracting from:
+<https://repo.anaconda.com/pkgs/main/linux-64/libsolv-0.7.30-h6f1ccf3_2.tar.bz2>
+at path info/recipe/parent/patches/conda_variant_priorization.patch
+(sha256: 9864c23404c0ab75880b8784b1b34fdb61416de7319371702e4ef0f886ea6c3c)
+
+diff --git a/src/conda.c b/src/conda.c
+index 21ad6bfb..408a236a 100644
+--- a/src/conda.c
++++ b/src/conda.c
+@@ -134,7 +134,7 @@ solv_vercmp_conda(const char *s1, const char *q1, const 
char *s2, const char *q2
+               return -1;
+             if (s1p - s1 > s2p - s2)
+               return 1;
+-            r = s1p - s1 ? strncmp(s1, s2, s1p - s1) : 0;
++            r = (s1p - s1) ? strncmp(s1, s2, s1p - s1) : 0;
+             if (r)
+               return r;
+           }
+diff --git a/src/policy.c b/src/policy.c
+index c02d2373..d6354cd2 100644
+--- a/src/policy.c
++++ b/src/policy.c
+@@ -833,6 +833,79 @@ move_installed_to_front(Pool *pool, Queue *plist)
+     }
+ }
+ 
++/*
++ * prune_to_best_version
++ *
++ * sort list of packages (given through plist) by name and evr
++ * return result through plist
++ */
++void
++prune_to_best_version(Pool *pool, Queue *plist)
++{
++#ifdef ENABLE_CONDA
++  if (pool->disttype == DISTTYPE_CONDA)
++     return prune_to_best_version_conda(pool, plist);
++#endif
++
++  int i, j, r;
++  Solvable *s, *best;
++
++  if (plist->count < 2)               /* no need to prune for a single entry 
*/
++    return;
++  POOL_DEBUG(SOLV_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count);
++
++  /* sort by name first, prefer installed */
++  solv_sort(plist->elements, plist->count, sizeof(Id), 
prune_to_best_version_sortcmp, pool);
++
++  /* now find best 'per name' */
++  best = 0;
++  for (i = j = 0; i < plist->count; i++)
++    {
++      s = pool->solvables + plist->elements[i];
++
++      POOL_DEBUG(SOLV_DEBUG_POLICY, "- %s [%d]%s\n",
++               pool_solvable2str(pool, s), plist->elements[i], 
++               (pool->installed && s->repo == pool->installed) ? "I" : "");
++
++      if (!best)              /* if no best yet, the current is best */
++        {
++          best = s;
++          continue;
++        }
++
++      /* name switch: finish group, re-init */
++      if (best->name != s->name)   /* new name */
++        {
++          plist->elements[j++] = best - pool->solvables; /* move old best to 
front */
++          best = s;           /* take current as new best */
++          continue;
++        }
++     
++      r = 0; 
++      if (r == 0)
++        r = best->evr != s->evr ? pool_evrcmp(pool, best->evr, s->evr, 
EVRCMP_COMPARE) : 0;
++#ifdef ENABLE_LINKED_PKGS
++      if (r == 0 && has_package_link(pool, s))
++        r = pool_link_evrcmp(pool, best, s);
++#endif
++      if (r < 0)
++      best = s;
++    }
++
++  plist->elements[j++] = best - pool->solvables;      /* finish last group */
++  plist->count = j;
++
++  /* we reduced the list to one package per name, now look at
++   * package obsoletes */
++  if (plist->count > 1)
++    {
++      if (plist->count == 2)
++        prune_obsoleted_2(pool, plist);
++      else
++        prune_obsoleted(pool, plist);
++    }
++}
++
+ #ifdef ENABLE_CONDA
+ static int
+ pool_featurecountcmp(Pool *pool, Solvable *s1, Solvable *s2)
+@@ -863,23 +936,221 @@ pool_buildflavorcmp(Pool *pool, Solvable *s1, Solvable 
*s2)
+     return 0;
+   return pool_evrcmp_str(pool, f1 ? f1 : "" , f2 ? f2 : "", EVRCMP_COMPARE);
+ }
+-#endif
++
++void intersect_selection(Pool* pool, Id dep, Queue* prev)
++{
++  Queue tmp;
++  int i = 0, j = 0, isectidx = 0;
++
++  queue_init(&tmp);
++
++  Id* pp, p;
++  pp = pool_whatprovides_ptr(pool, dep);
++  while ((p = *pp++) != 0)
++    queue_push(&tmp, p);
++
++  // set intersection, assuming sorted arrays
++  while (i < prev->count && j < tmp.count) 
++    if (prev->elements[i] < tmp.elements[j])
++      i++;
++    else if (tmp.elements[j] < prev->elements[i])
++      j++;
++    else
++      {
++        if (isectidx != i)
++          prev->elements[isectidx] = prev->elements[i];
++        i++, j++, isectidx++;
++      }
++
++  prev->count = isectidx;
++  queue_free(&tmp);
++}
++
++int check_deps_unequal(Pool* pool, Queue* q1, Queue* q2, Id name)
++{
++  Id dep;
++  int i, j;
++  int found = 0;
++  for (i = 0; i < q1->count; ++i)
++  {
++    dep = q1->elements[i];
++    if (ISRELDEP(dep) && GETRELDEP(pool, dep)->name == name)
++    {
++      for (j = 0; j < q2->count; ++j)
++      {
++        if (q2->elements[j] == dep)
++        {
++          found = 1;
++          break;
++        }
++      }
++      if (!found)
++        return 1;
++
++      found = 0;
++    }
++  }
++  return 0;
++}
++
++Id best_matching(Pool* pool, Queue* q, Id name, int* all_have_trackfeatures)
++{
++  int first = 1;
++  Id dep, p, *pp;
++
++  Queue selection;
++  queue_init(&selection);
++
++  for (int i = 0; i < q->count; ++i)
++  {
++    dep = q->elements[i];
++    if (!ISRELDEP(dep) || GETRELDEP(pool, dep)->name != name) continue;
++
++    if (first)
++    {
++      pp = pool_whatprovides_ptr(pool, dep);
++      while ((p = *pp++) != 0)
++        queue_push(&selection, p);
++      first = 0;
++    }
++    else
++      intersect_selection(pool, dep, &selection);
++  }
++
++  if (selection.count == 0)
++    return 0;
++
++  Solvable *stmp, *best = pool_id2solvable(pool, selection.elements[0]);
++  int cmp;
++
++  *all_have_trackfeatures = 1;
++  for (int i = 0; i < selection.count; ++i)
++    if (solvable_lookup_count(pool_id2solvable(pool, selection.elements[i]),
++                              SOLVABLE_TRACK_FEATURES) == 0)
++      {
++        *all_have_trackfeatures = 0;
++        break;
++      }
++  
++  for (int i = 0; i < selection.count; ++i)
++  {
++    stmp = pool_id2solvable(pool, selection.elements[i]);
++    cmp = pool_evrcmp(pool, best->evr, stmp->evr, 0);
++    if (cmp < 0) best = stmp;
++  }
++
++  return best->evr;
++}
++
++int conda_compare_dependencies(Pool *pool, Solvable *s1, Solvable *s2)
++{
++  int i, j, has_seen;
++  Queue q1, q2, seen;
++
++  queue_init(&q1);
++  queue_init(&q2);
++  queue_init(&seen);
++
++  solvable_lookup_deparray(s1, SOLVABLE_REQUIRES, &q1, -1);
++  solvable_lookup_deparray(s2, SOLVABLE_REQUIRES, &q2, -1);
++
++  int comparison_result = 0;
++
++  for (i = 0; i < q1.count; ++i)
++  {
++    Id x1 = q1.elements[i];
++    has_seen = 0;
++
++    if (!ISRELDEP(x1))
++      continue;
++
++    Reldep* rd1 = GETRELDEP(pool, x1);
++    for (j = 0; j < seen.count && has_seen == 0; ++j)
++      if (seen.elements[j] == rd1->name)
++        has_seen = 1;
++
++    if (has_seen)
++      continue;
++
++    // first make sure that deps are different between a & b
++    int deps_unequal = check_deps_unequal(pool, &q1, &q2, rd1->name);
++    if (!deps_unequal)
++      {
++        queue_push(&seen, rd1->name);
++        continue;
++      }
++
++    int aht_1, aht_2; // all have track features check
++    Id b1 = best_matching(pool, &q1, rd1->name, &aht_1);
++    Id b2 = best_matching(pool, &q2, rd1->name, &aht_2);
++
++    // one of both or both is not solvable...
++    // ignoring this case for now
++    if (b1 == 0 || b2 == 0)
++      continue;
++
++    // if one has deps with track features, and the other does not, 
++    // downweight the one with track features
++    if (aht_1 != aht_2)
++      comparison_result += (aht_1 - aht_2) * 100;
++
++    comparison_result += pool_evrcmp(pool, b2, b1, 0);
++  }
++
++  queue_free(&q1);
++  queue_free(&q2);
++  queue_free(&seen);
++
++  return comparison_result;
++}
++
++static int
++sort_by_best_dependencies(const void *ap, const void *bp, void *dp)
++{
++  Pool* pool = (Pool*) dp;
++
++  Id a = *(Id *)ap;
++  Id b = *(Id *)bp;
++  Solvable *sa, *sb;
++
++  sa = pool->solvables + a;
++  sb = pool->solvables + b;
++
++  int res = conda_compare_dependencies(pool, sa, sb);
++  if (res == 0)
++  {
++    // no differences, select later build
++    Repodata* ra = repo_last_repodata(sa->repo);
++    Repodata* rb = repo_last_repodata(sb->repo);
++
++    unsigned long long bta = repodata_lookup_num(ra, a, SOLVABLE_BUILDTIME, 
0ull);
++    unsigned long long btb = repodata_lookup_num(rb, b, SOLVABLE_BUILDTIME, 
0ull);
++
++    res = (btb > bta) ? 1 : -1;
++    POOL_DEBUG(SOLV_DEBUG_POLICY, "Fallback to timestamp comparison: %llu vs 
%llu: [%d]\n", bta, btb, res);
++  }
++
++  POOL_DEBUG(SOLV_DEBUG_POLICY, "Selecting variant [%c] of (a) %s vs (b) %s 
(score: %d)\n",
++             (res < 0 ? 'a' : 'b'), pool_solvable2str(pool, sa), 
pool_solvable2str(pool, sb), res);
++
++  return res; 
++}
+ 
+ /*
+- * prune_to_best_version
++ * prune_to_best_version_conda
+  *
+  * sort list of packages (given through plist) by name and evr
+  * return result through plist
+  */
+ void
+-prune_to_best_version(Pool *pool, Queue *plist)
++prune_to_best_version_conda(Pool *pool, Queue *plist)
+ {
+   int i, j, r;
+   Solvable *s, *best;
+ 
+-  if (plist->count < 2)               /* no need to prune for a single entry 
*/
++  if (plist->count < 2)         /* no need to prune for a single entry */
+     return;
+-  POOL_DEBUG(SOLV_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count);
++  POOL_DEBUG(SOLV_DEBUG_POLICY, "prune_to_best_version_conda %d\n", 
plist->count);
+ 
+   /* sort by name first, prefer installed */
+   solv_sort(plist->elements, plist->count, sizeof(Id), 
prune_to_best_version_sortcmp, pool);
+@@ -891,10 +1162,10 @@ prune_to_best_version(Pool *pool, Queue *plist)
+       s = pool->solvables + plist->elements[i];
+ 
+       POOL_DEBUG(SOLV_DEBUG_POLICY, "- %s [%d]%s\n",
+-               pool_solvable2str(pool, s), plist->elements[i], 
+-               (pool->installed && s->repo == pool->installed) ? "I" : "");
++                 pool_solvable2str(pool, s), plist->elements[i], 
++                 (pool->installed && s->repo == pool->installed) ? "I" : "");
+ 
+-      if (!best)              /* if no best yet, the current is best */
++      if (!best)                /* if no best yet, the current is best */
+         {
+           best = s;
+           continue;
+@@ -904,49 +1175,54 @@ prune_to_best_version(Pool *pool, Queue *plist)
+       if (best->name != s->name)   /* new name */
+         {
+           plist->elements[j++] = best - pool->solvables; /* move old best to 
front */
+-          best = s;           /* take current as new best */
++          best = s;             /* take current as new best */
+           continue;
+         }
+      
+       r = 0; 
+-#ifdef ENABLE_CONDA
+-      if (pool->disttype == DISTTYPE_CONDA)
+-        r = pool_featurecountcmp(pool, best, s);
+-#endif
++      r = pool_featurecountcmp(pool, best, s);
+       if (r == 0)
+         r = best->evr != s->evr ? pool_evrcmp(pool, best->evr, s->evr, 
EVRCMP_COMPARE) : 0;
+-#ifdef ENABLE_LINKED_PKGS
+-      if (r == 0 && has_package_link(pool, s))
+-        r = pool_link_evrcmp(pool, best, s);
+-#endif
+-#ifdef ENABLE_CONDA
+-      if (pool->disttype == DISTTYPE_CONDA)
+-      {
+-        if (r == 0)
+-          r = (best->repo ? best->repo->subpriority : 0) - (s->repo ? 
s->repo->subpriority : 0);
+-        if (r == 0)
+-          r = pool_buildversioncmp(pool, best, s);
+-        if (r == 0)
+-          r = pool_buildflavorcmp(pool, best, s);
+-      }
+-#endif
++      if (r == 0)
++        r = (best->repo ? best->repo->subpriority : 0) - (s->repo ? 
s->repo->subpriority : 0);
++      if (r == 0)
++        r = pool_buildversioncmp(pool, best, s);
++      // this can be removed as this comparison doesn't effect anything
++      if (r == 0)
++        r = pool_buildflavorcmp(pool, best, s);
+       if (r < 0)
+-      best = s;
++        best = s;
+     }
+-  plist->elements[j++] = best - pool->solvables;      /* finish last group */
+-  plist->count = j;
+ 
+-  /* we reduced the list to one package per name, now look at
+-   * package obsoletes */
+-  if (plist->count > 1)
++  Queue q;
++  queue_init(&q);
++  for (i = 0; i < plist->count; i++)
+     {
+-      if (plist->count == 2)
+-        prune_obsoleted_2(pool, plist);
+-      else
+-        prune_obsoleted(pool, plist);
++      s = pool->solvables + plist->elements[i];
++      r = pool_featurecountcmp(pool, best, s);
++      if (r == 0)
++        r = best->evr != s->evr ? pool_evrcmp(pool, best->evr, s->evr, 
EVRCMP_COMPARE) : 0;
++      if (r == 0)
++        r = (best->repo ? best->repo->subpriority : 0) - (s->repo ? 
s->repo->subpriority : 0);
++      if (r == 0)
++        r = pool_buildversioncmp(pool, best, s);
++      if (r == 0)
++        queue_push(&q, s - pool->solvables);
+     }
+-}
+ 
++  if (q.count > 1)
++    {
++      // order by first-level deps
++      solv_sort(q.elements, q.count, sizeof(Id), sort_by_best_dependencies, 
pool);
++    }
++
++  for (i = 0; i < q.count; ++i)
++    plist->elements[i] = q.elements[i];
++  plist->count = q.count;
++
++  queue_free(&q);
++}
++#endif  // ENABLE_CONDA
+ 
+ static int
+ sort_by_name_evr_sortcmp(const void *ap, const void *bp, void *dp)
+diff --git a/src/policy.h b/src/policy.h
+index 3ae1005a..a79483a4 100644
+--- a/src/policy.h
++++ b/src/policy.h
+@@ -45,6 +45,9 @@ extern void pool_best_solvables(Pool *pool, Queue *plist, 
int flags);
+ extern void prune_to_best_version(Pool *pool, Queue *plist);
+ extern void policy_prefer_favored(Solver *solv, Queue *plist);
+ 
++#ifdef ENABLE_CONDA
++extern void prune_to_best_version_conda(Pool *pool, Queue *plist);
++#endif
+ 
+ #ifdef __cplusplus
+ }

Reply via email to