proj/portage-utils:master commit in: libq/

"Fabian Groffen" <[email protected]>
Newsgroups gmane.linux.gentoo.cvs
Message-ID <1782333542.c9ae9f01fd9b68606222f531ec938ff8228b3d40.grobian@gentoo>
commit:     c9ae9f01fd9b68606222f531ec938ff8228b3d40
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Wed Jun 24 20:39:02 2026 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Wed Jun 24 20:39:02 2026 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=c9ae9f01

libq/dep: implement masks, blockers and keywords

Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

 libq/dep.c | 197 ++++++++++++++++++++++++++++++++++++++++++++-----------------
 libq/dep.h |   6 +-
 2 files changed, 147 insertions(+), 56 deletions(-)

diff --git a/libq/dep.c b/libq/dep.c
index f05a0b85..c068b743 100644
--- a/libq/dep.c
+++ b/libq/dep.c
@@ -72,8 +72,11 @@ struct dep_node_ {
   tree_pkg_ctx     *ipkg;
   dep_node_t       *parent;
   array            *members;
+  atom_ctx         *blockatom;
+  dep_node_t       *fail;
   dep_type_t        type;
   bool              invert:1;
+  bool              inactive:1;
 };
 
 dep_node_t *dep_grow_tree
@@ -478,7 +481,8 @@ static void dep_print_tree_int
     if (root->pkg != NULL)
     {
       a = tree_pkg_atom(root->pkg, false);
-      if (hlatoms == NULL)
+      if (hlatoms == NULL &&
+          hlcolor != NULL)
         match = true;
     }
 
@@ -643,13 +647,18 @@ void dep_prune_use
  *   can be retrieved using TODO(FIXME)
  * - RESOLVE_NEWBLOCKER: like RESOLVE_OK, but a new blocker was added to
  *   the blockers list, and the caller should re-resolve the full tree
+ * blockers is a hash containing arrays to multiple masks or blockers
+ * can be stored while still allowing to lookup per PN
+ * accept_keywords a set of keywords of which one should match with the
+ * one defined in KEYWORDS for the package
  */
 dep_status_t dep_resolve_tree
 (
   dep_node_t *root,
   tree_ctx   *tree,
   set_t      *use,
-  hash_t     *blockers
+  hash_t     *blockers,
+  set_t      *accept_keywords
 )
 {
   dep_status_t ret = DEP_OK;  /* resolving succeed */
@@ -664,97 +673,140 @@ dep_status_t dep_resolve_tree
     {
       if (root->atom->blocker != ATOM_BL_NONE)
       {
+        array    *atoms;
         atom_ctx *prevatom;
+        size_t    n;
 
         if (blockers == NULL)
           break;  /* ignore */
 
-        /* add blocker to the list of blockers under CAT/PN:SLOT key */
-        blockers = hash_add(blockers,
-                            atom_format("%[CAT]%[PN]%[SLOT]", root->atom),
-                            atom_clone(root->atom), (void **)&prevatom);
-        ret = DEP_NEWBLOCKER;
+        atoms = hash_get(blockers,
+                         atom_format("%[CAT]%[PN]%[SLOT]", root->atom));
+        if (atoms == NULL)
+        {
+          atoms = array_new();
+          hash_add(blockers,
+                   atom_format("%[CAT]%[PN]%[SLOT]", root->atom),
+                   atoms, NULL /* must be unset */);
+        }
 
-        /* FIXME: this means we have two blockers that cover the same
-         * key, which doesn't mean they are the same or that they are
-         * compatible, e.g. !foo-1 and !foo-2, how should we handle
-         * this? */
-        if (prevatom != NULL)
+        ret = DEP_NEWBLOCKER;
+        array_for_each(atoms, n, prevatom)
         {
-          if (atom_compare(root->atom, prevatom) == EQUAL)
+          /* atom_compare cannot work with blocker or range ops on both
+           * sides, so check the two atoms to be identical instead */
+          if (atom_compare_flg(root->atom, prevatom,
+                               (ATOM_COMP_EXACT |
+                                ATOM_COMP_NOREPO)) == EQUAL)
+          {
             ret = DEP_OK;
-          atom_implode(prevatom);
+            break;
+          }
         }
+
+        /* add new/different blocker */
+        if (ret == DEP_NEWBLOCKER)
+          array_append(atoms, atom_clone(root->atom));
       }
       else
       {
         array        *r;
+        array        *blkatoms;
         atom_ctx     *blkatom;
         tree_pkg_ctx *pkgw;
         size_t        n;
+        size_t        m;
+        bool          ismasked = false;
+        bool          isunkeyw = false;
 
-        blkatom = hash_get(blockers,
-                           atom_format("%[CAT]%[PN]%[SLOT]", root->atom));
+        blkatoms = hash_get(blockers,
+                            atom_format("%[CAT]%[PN]%[SLOT]", root->atom));
         r = tree_match_atom(tree, root->atom,
                             (TREE_MATCH_DEFAULT |
-                             (blkatom == NULL ? TREE_MATCH_LATEST : 0)));
+                             TREE_MATCH_SORT));
 
-        if (blkatom != NULL)
+        /* consume from the returned matches the first VDB and non-VDB
+         * that are not covered by masks or keywords */
+        array_for_each(r, n, pkgw)
         {
-          atom_equality eq;
-          array_for_each(r, n, pkgw)
+          atom_ctx *atom     = tree_pkg_atom(pkgw, true);
+          char     *kwstr;
+          set_t    *keywords;
+
+          /* check whether masks apply */
+          ismasked = false;
+          array_for_each(blkatoms, m, blkatom)
           {
-            eq = atom_compare(tree_pkg_atom(pkgw, true), blkatom);
+            atom_equality eq;
+
+            eq = atom_compare(atom, blkatom);
             if (blkatom->blocker != ATOM_BL_NONE)  /* blocker */
             {
-              if (eq == EQUAL)
+              if (eq != EQUAL)
               {
-                if (root->pkg == NULL)
-                  root->pkg = pkgw;
-                if (tree_pkg_get_treetype(pkgw) == TREETYPE_VDB)
-                {
-                  root->ipkg = pkgw;
-                  break;
-                }
+                ismasked = true;
+                break;
               }
             }
             else  /* mask entry */
             {
-              if (eq == NOT_EQUAL ||
-                  eq == NEWER)
+              if (eq == EQUAL)
               {
-                if (root->pkg == NULL)
-                  root->pkg = pkgw;
-                if (tree_pkg_get_treetype(pkgw) == TREETYPE_VDB)
-                {
-                  root->ipkg = pkgw;
-                  break;
-                }
+                ismasked = true;
+                break;
               }
             }
           }
+
+          if (ismasked)
+            continue;
+
+          /* check keywords */
+          kwstr = tree_pkg_meta(pkgw, Q_KEYWORDS);
+          if (kwstr == NULL)
+          {
+            isunkeyw = true;
+            continue;
+          }
+
+          keywords = set_add_from_string(set_new(), kwstr);
+          if (!set_has_intersection(keywords, accept_keywords))
+          {
+            isunkeyw = true;
+            continue;
+          }
+          set_free(keywords);
+
+          /* finally, assign */
           if (root->pkg == NULL)
-            ret = DEP_FAIL;
+            root->pkg = pkgw;
+          if (tree_pkg_get_treetype(pkgw) == TREETYPE_VDB)
+          {
+            if (root->ipkg == NULL)
+              root->ipkg = pkgw;
+            break;
+          }
+
+          if (root->pkg != NULL &&
+              root->ipkg != NULL)
+            break;  /* stop searching, we got 'em */
         }
-        else
+
+        if (root->pkg == NULL)
         {
-          root->pkg = array_get(r, 0);
-          if (root->pkg != NULL)
+          ret = DEP_FAIL;
+          if (ismasked)
           {
-            array_for_each(r, n, pkgw)
-            {
-              if (tree_pkg_get_treetype(pkgw) == TREETYPE_VDB)
-              {
-                root->ipkg = pkgw;
-                break;
-              }
-            }
+            root->blockatom = blkatom;
+            if (blkatom->blocker == ATOM_BL_NONE)
+              ret = DEP_MASK;  /* masked */
           }
-          else
+          else if (isunkeyw)
           {
-            ret = DEP_FAIL;
+            ret = DEP_KEYWORD;  /* missing keyword */
           }
         }
+
         array_free(r);
       }
     }
@@ -765,8 +817,11 @@ dep_status_t dep_resolve_tree
      * original dep-tree, would not want to see the tree be modified
      * (which is what prune_use does) */
     if ((!set_contains(use, root->word)) ^ root->invert)
+    {
+      root->inactive = true;
       break;
-    /* TODO: maybe flag the node as being selected or something? */
+    }
+    root->inactive = false;
     /* fall through -- handle as ALL-group */
   case DEP_ALL:
     if (root->members)
@@ -776,9 +831,13 @@ dep_status_t dep_resolve_tree
 
       array_for_each(root->members, n, memb)
       {
-        if ((ret = dep_resolve_tree(memb, tree, use, blockers)) != DEP_OK)
+        if ((ret = dep_resolve_tree(memb, tree, use,
+                                    blockers, accept_keywords)) != DEP_OK)
           break;
       }
+
+      if (ret != DEP_FAIL)
+        root->fail = memb;
     }
     break;
   case DEP_ANY:
@@ -794,7 +853,8 @@ dep_status_t dep_resolve_tree
       ret = DEP_FAIL;
       array_for_each(root->members, n, memb)
       {
-        if ((sret = dep_resolve_tree(memb, tree, use, blockers)) == DEP_FAIL)
+        if ((sret = dep_resolve_tree(memb, tree, use,
+                                     blockers, accept_keywords)) == DEP_FAIL)
           continue;
         if (sret == DEP_NEWBLOCKER)
           ret = DEP_NEWBLOCKER;
@@ -819,6 +879,7 @@ static void dep_flatten_tree_int
 )
 {
   if (root->type == DEP_NULL ||
+      root->inactive ||
       out == NULL)
     return;
 
@@ -903,4 +964,30 @@ atom_ctx *dep_node_atom
   return node->atom;
 }
 
+atom_ctx *dep_node_mask
+(
+  dep_node_t *node
+)
+{
+  if (node == NULL)
+    return NULL;
+
+  return node->blockatom;
+}
+
+atom_ctx *dep_node_fail_input
+(
+  dep_node_t *node
+)
+{
+  if (node == NULL)
+    return NULL;
+
+  /* skip over USE */
+  while (node->fail != NULL)
+    node = node->fail;
+
+  return node->atom;
+}
+
 /* vim: set ts=2 sw=2 expandtab cino+=\:0 foldmethod=marker: */

diff --git a/libq/dep.h b/libq/dep.h
index 705f9172..3cf6b1dc 100644
--- a/libq/dep.h
+++ b/libq/dep.h
@@ -20,6 +20,8 @@ typedef enum dep_status_ dep_status_t;
 enum dep_status_ {
   DEP_OK = 1,
   DEP_FAIL,
+  DEP_MASK,
+  DEP_KEYWORD,
   DEP_NEWBLOCKER,
 };
 
@@ -28,7 +30,7 @@ dep_node_t   *dep_grow_tree(const char *depend);
 void          dep_print_tree(FILE *fp, const dep_node_t *root, size_t space,
                              array *m, const char *c, int verbose);
 dep_status_t  dep_resolve_tree(dep_node_t *root, tree_ctx *t,
-                               set_t *use, hash_t *blockers);
+                               set_t *use, hash_t *blockers, set_t *keywords);
 void          dep_prune_use(dep_node_t *root, set_t *use);
 array        *dep_flatten_tree(dep_node_t *root);
 void          dep_burn_tree(dep_node_t *root);
@@ -45,6 +47,8 @@ array        *dep_nodes(dep_node_t *node);
 tree_pkg_ctx *dep_node_pkg(dep_node_t *node);
 tree_pkg_ctx *dep_node_ipkg(dep_node_t *node);
 atom_ctx     *dep_node_atom(dep_node_t *node);
+atom_ctx     *dep_node_mask(dep_node_t *node);
+atom_ctx     *dep_node_fail_input(dep_node_t *node);
 
 #endif
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.