From 78d53ea012dfbaf397bf8e6907efac5b51abac56 Mon Sep 17 00:00:00 2001 From: Kai Pastor Date: Fri, 23 Feb 2024 15:18:08 +0100 Subject: [PATCH] Revise serials, traversal, flattening Remove the 'traverse_serial' fields which were added in 2.1.1. Use the 'serial' field to track the current traversal. Stop using 'identifier' to sort packages in the flattened solution. Directly construct the flattened solution by a specific walk which also preserves the relative order in Requires and Requires.private. The topological sort is a single list, so don't fill requires_private. Purely private dependencies are marked in dependency flags. The ancestor flag is a pkg property, not a client property. --- cli/main.c | 5 +- libpkgconf/libpkgconf.h | 5 +- libpkgconf/pkg.c | 35 ++---- libpkgconf/queue.c | 242 +++++++++++++++++++++------------------- 4 files changed, 142 insertions(+), 145 deletions(-) diff --git a/cli/main.c b/cli/main.c index b1489eb..dfd65c7 100644 --- a/cli/main.c +++ b/cli/main.c @@ -280,7 +280,10 @@ print_digraph_node(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) { pkgconf_dependency_t *dep = node->data; - printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8]\n", pkg->id, dep->package); + if ((dep->flags & PKGCONF_PKG_DEPF_PRIVATE) == 0) + printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8]\n", pkg->id, dep->package); + else + printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8 color=gray]\n", pkg->id, dep->package); } PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node) diff --git a/libpkgconf/libpkgconf.h b/libpkgconf/libpkgconf.h index b2a654d..01cd845 100644 --- a/libpkgconf/libpkgconf.h +++ b/libpkgconf/libpkgconf.h @@ -137,7 +137,7 @@ struct pkgconf_path_ { #define PKGCONF_PKG_PROPF_CACHED 0x02 #define PKGCONF_PKG_PROPF_UNINSTALLED 0x08 #define PKGCONF_PKG_PROPF_VIRTUAL 0x10 -#define PKGCONF_PKG_PROPF_VISITED 0x20 +#define PKGCONF_PKG_PROPF_ANCESTOR 0x20 #define PKGCONF_PKG_PROPF_VISITED_PRIVATE 0x40 struct pkgconf_pkg_ { @@ -178,7 +178,6 @@ struct pkgconf_pkg_ { uint64_t serial; uint64_t identifier; - uint64_t traverse_serial; }; typedef bool (*pkgconf_pkg_iteration_func_t)(const pkgconf_pkg_t *pkg, void *data); @@ -215,7 +214,6 @@ struct pkgconf_client_ { uint64_t serial; uint64_t identifier; - uint64_t traverse_serial; pkgconf_pkg_t **cache_table; size_t cache_count; @@ -282,7 +280,6 @@ PKGCONF_API void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *p #define PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS 0x4000 #define PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES 0x8000 #define PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES 0x10000 -#define PKGCONF_PKG_PKGF_ANCESTOR 0x20000 #define PKGCONF_PKG_DEPF_INTERNAL 0x1 #define PKGCONF_PKG_DEPF_PRIVATE 0x2 diff --git a/libpkgconf/pkg.c b/libpkgconf/pkg.c index ee8f1f9..d271997 100644 --- a/libpkgconf/pkg.c +++ b/libpkgconf/pkg.c @@ -1491,7 +1491,7 @@ pkgconf_pkg_walk_list(pkgconf_client_t *client, unsigned int eflags = PKGCONF_PKG_ERRF_OK; pkgconf_node_t *node, *next; - parent->flags |= PKGCONF_PKG_PKGF_ANCESTOR; + parent->flags |= PKGCONF_PKG_PROPF_ANCESTOR; PKGCONF_FOREACH_LIST_ENTRY_SAFE(deplist->head, next, node) { @@ -1513,10 +1513,8 @@ pkgconf_pkg_walk_list(pkgconf_client_t *client, if (pkgdep == NULL) continue; - if((pkgdep->flags & PKGCONF_PKG_PKGF_ANCESTOR) != 0) + if((pkgdep->flags & PKGCONF_PKG_PROPF_ANCESTOR) != 0) { - pkgdep->identifier = ++client->identifier; - /* In this case we have a circular reference. * We break that by deleteing the circular node from the * the list, so that we dont create a situation where @@ -1544,14 +1542,12 @@ pkgconf_pkg_walk_list(pkgconf_client_t *client, pkgconf_audit_log_dependency(client, pkgdep, depnode); - pkgdep->identifier = ++client->identifier; - pkgdep->serial = client->serial; eflags |= pkgconf_pkg_traverse_main(client, pkgdep, func, data, depth - 1, skip_flags); next: pkgconf_pkg_unref(client, pkgdep); } - parent->flags &= ~PKGCONF_PKG_PKGF_ANCESTOR; + parent->flags &= ~PKGCONF_PKG_PROPF_ANCESTOR; return eflags; } @@ -1628,27 +1624,19 @@ pkgconf_pkg_traverse_main(pkgconf_client_t *client, unsigned int skip_flags) { unsigned int eflags = PKGCONF_PKG_ERRF_OK; - unsigned int visited_flag = (client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) ? PKGCONF_PKG_PROPF_VISITED_PRIVATE : PKGCONF_PKG_PROPF_VISITED; if (maxdepth == 0) return eflags; - /* If we have already visited this node, check if we have done so as a Requires or Requires.private - * query as appropriate, and short-circuit if so. + /* Short-circuit if we have already visited this node. */ - if ((root->flags & PKGCONF_PKG_PROPF_VIRTUAL) == 0 && root->traverse_serial == client->traverse_serial) - { - if (root->flags & visited_flag) - return eflags; - } - else - { - root->traverse_serial = client->traverse_serial; - root->flags &= ~(PKGCONF_PKG_PROPF_VISITED|PKGCONF_PKG_PROPF_VISITED_PRIVATE); - } + if (root->serial == client->serial) + return eflags; - /* Update this node to indicate how we've visited it so far. */ - root->flags |= visited_flag; + root->serial = client->serial; + + if (root->identifier == 0) + root->identifier = ++client->identifier; PKGCONF_TRACE(client, "%s: level %d, serial %"PRIu64, root->id, maxdepth, client->serial); @@ -1699,7 +1687,8 @@ pkgconf_pkg_traverse(pkgconf_client_t *client, if (root->flags & PKGCONF_PKG_PROPF_VIRTUAL) client->serial++; - client->traverse_serial++; + if ((client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) == 0) + skip_flags |= PKGCONF_PKG_DEPF_PRIVATE; return pkgconf_pkg_traverse_main(client, root, func, data, maxdepth, skip_flags); } diff --git a/libpkgconf/queue.c b/libpkgconf/queue.c index a9039ce..1f35575 100644 --- a/libpkgconf/queue.c +++ b/libpkgconf/queue.c @@ -103,132 +103,132 @@ pkgconf_queue_free(pkgconf_list_t *list) } static void -pkgconf_queue_collect_dependents(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +pkgconf_queue_mark_public(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) { + if (pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE) + { + pkgconf_list_t *list = data; + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(list->head, node) + { + pkgconf_dependency_t *dep = node->data; + if (dep->match == pkg) + dep->flags &= ~PKGCONF_PKG_DEPF_PRIVATE; + } + + pkg->flags &= ~PKGCONF_PKG_PROPF_VISITED_PRIVATE; + + PKGCONF_TRACE(client, "%s: updated, public", pkg->id); + } +} + +static unsigned int +pkgconf_queue_collect_dependencies_main(pkgconf_client_t *client, + pkgconf_pkg_t *root, + void *data, + int maxdepth); + +static inline unsigned int +pkgconf_queue_collect_dependencies_walk(pkgconf_client_t *client, + pkgconf_pkg_t *parent, + pkgconf_list_t *deplist, + void *data, + int depth) +{ + unsigned int eflags = PKGCONF_PKG_ERRF_OK; pkgconf_node_t *node; pkgconf_pkg_t *world = data; - if (pkg == world) - return; - - PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, node) - { - pkgconf_dependency_t *parent_dep = node->data; - pkgconf_dependency_t *flattened_dep; - - flattened_dep = pkgconf_dependency_copy(client, parent_dep); - - if ((client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) != PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) - pkgconf_node_insert(&flattened_dep->iter, flattened_dep, &world->required); - else - pkgconf_node_insert(&flattened_dep->iter, flattened_dep, &world->requires_private); - } - - if (client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) - { - PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node) - { - pkgconf_dependency_t *parent_dep = node->data; - pkgconf_dependency_t *flattened_dep; - - flattened_dep = pkgconf_dependency_copy(client, parent_dep); - - pkgconf_node_insert(&flattened_dep->iter, flattened_dep, &world->requires_private); - } - } -} - -static int -dep_sort_cmp(const void *a, const void *b) -{ - const pkgconf_dependency_t *depA = *(void **) a; - const pkgconf_dependency_t *depB = *(void **) b; - - return depB->match->identifier - depA->match->identifier; -} - -static inline void -flatten_dependency_set(pkgconf_client_t *client, pkgconf_list_t *list) -{ - pkgconf_node_t *node, *next; - pkgconf_dependency_t **deps = NULL; - size_t dep_count = 0, i; - - PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) + PKGCONF_FOREACH_LIST_ENTRY_REVERSE(deplist->tail, node) { pkgconf_dependency_t *dep = node->data; - pkgconf_pkg_t *pkg = pkgconf_pkg_verify_dependency(client, dep, NULL); + pkgconf_dependency_t *flattened_dep; + pkgconf_pkg_t *pkg = dep->match; - if (pkg == NULL) + if (*dep->package == '\0') continue; - if (pkg->serial == client->serial) - { - pkgconf_node_delete(node, list); - pkgconf_dependency_unref(client, dep); - goto next; - } - - if (dep->match == NULL) + if (pkg == NULL) { PKGCONF_TRACE(client, "WTF: unmatched dependency %p <%s>", dep, dep->package); abort(); } - /* for virtuals, we need to check to see if there are dupes */ - for (i = 0; i < dep_count; i++) - { - pkgconf_dependency_t *other_dep = deps[i]; - - PKGCONF_TRACE(client, "dedup %s = %s?", dep->package, other_dep->package); - - if (!strcmp(dep->package, other_dep->package)) - { - PKGCONF_TRACE(client, "skipping, "SIZE_FMT_SPECIFIER" deps", dep_count); - goto next; - } - } - - pkg->serial = client->serial; - - /* copy to the deps table */ - dep_count++; - deps = pkgconf_reallocarray(deps, dep_count, sizeof (void *)); - deps[dep_count - 1] = dep; - - PKGCONF_TRACE(client, "added %s to dep table", dep->package); -next: - pkgconf_pkg_unref(client, pkg); - } - - if (deps == NULL) - return; - - qsort(deps, dep_count, sizeof (void *), dep_sort_cmp); - - /* zero the list and start readding */ - pkgconf_list_zero(list); - - for (i = 0; i < dep_count; i++) - { - pkgconf_dependency_t *dep = deps[i]; - - if (dep->match == NULL) + if (pkg->serial == client->serial) continue; - memset(&dep->iter, '\0', sizeof (dep->iter)); - pkgconf_node_insert(&dep->iter, dep, list); + if (client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) + pkg->flags |= PKGCONF_PKG_PROPF_VISITED_PRIVATE; + else + pkg->flags &= ~PKGCONF_PKG_PROPF_VISITED_PRIVATE; - PKGCONF_TRACE(client, "slot "SIZE_FMT_SPECIFIER": dep %s matched to %p<%s> id %"PRIu64, i, dep->package, dep->match, dep->match->id, dep->match->identifier); + eflags |= pkgconf_queue_collect_dependencies_main(client, pkg, data, depth - 1); + + flattened_dep = pkgconf_dependency_copy(client, dep); + pkgconf_node_insert(&flattened_dep->iter, flattened_dep, &world->required); } - free(deps); + return eflags; +} + +static unsigned int +pkgconf_queue_collect_dependencies_main(pkgconf_client_t *client, + pkgconf_pkg_t *root, + void *data, + int maxdepth) +{ + unsigned int eflags = PKGCONF_PKG_ERRF_OK; + + if (maxdepth == 0) + return eflags; + + /* Short-circuit if we have already visited this node. + */ + if (root->serial == client->serial) + return eflags; + + root->serial = client->serial; + + if (client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) + { + PKGCONF_TRACE(client, "%s: collecting private dependencies, level %d", root->id, maxdepth); + + /* XXX: ugly */ + const unsigned int saved_flags = client->flags; + client->flags |= PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE; + eflags = pkgconf_queue_collect_dependencies_walk(client, root, &root->requires_private, data, maxdepth); + client->flags = saved_flags; + if (eflags != PKGCONF_PKG_ERRF_OK) + return eflags; + } + + PKGCONF_TRACE(client, "%s: collecting public dependencies, level %d", root->id, maxdepth); + + eflags = pkgconf_queue_collect_dependencies_walk(client, root, &root->required, data, maxdepth); + if (eflags != PKGCONF_PKG_ERRF_OK) + return eflags; + + PKGCONF_TRACE(client, "%s: finished, %s", root->id, (root->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE) ? "private" : "public"); + + return eflags; +} + +static inline unsigned int +pkgconf_queue_collect_dependencies(pkgconf_client_t *client, + pkgconf_pkg_t *root, + void *data, + int maxdepth) +{ + ++client->serial; + return pkgconf_queue_collect_dependencies_main(client, root, data, maxdepth); } static inline unsigned int pkgconf_queue_verify(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list, int maxdepth) { unsigned int result; + const unsigned int saved_flags = client->flags; pkgconf_pkg_t initial_world = { .id = "user:request", .realname = "virtual world package", @@ -241,31 +241,39 @@ pkgconf_queue_verify(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_lis return PKGCONF_PKG_ERRF_DEPGRAPH_BREAK; } - /* collect all the dependencies */ - result = pkgconf_pkg_traverse(client, &initial_world, pkgconf_queue_collect_dependents, world, maxdepth, 0); + PKGCONF_TRACE(client, "solving"); + result = pkgconf_pkg_traverse(client, &initial_world, NULL, NULL, maxdepth, 0); if (result != PKGCONF_PKG_ERRF_OK) { pkgconf_solution_free(client, &initial_world); return result; } + PKGCONF_TRACE(client, "flattening"); + result = pkgconf_queue_collect_dependencies(client, &initial_world, world, maxdepth); + if (result != PKGCONF_PKG_ERRF_OK) + { + pkgconf_solution_free(client, &initial_world); + return result; + } + + if (client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) + { + PKGCONF_TRACE(client, "marking public deps"); + client->flags &= ~PKGCONF_PKG_PKGF_SEARCH_PRIVATE; + client->flags |= PKGCONF_PKG_PKGF_SKIP_CONFLICTS; + result = pkgconf_pkg_traverse(client, &initial_world, pkgconf_queue_mark_public, &world->required, maxdepth, 0); + client->flags = saved_flags; + if (result != PKGCONF_PKG_ERRF_OK) + { + pkgconf_solution_free(client, &initial_world); + return result; + } + } + /* free the initial solution */ pkgconf_solution_free(client, &initial_world); - /* flatten the dependency set using serials. - * we copy the dependencies to a vector, and then erase the list. - * then we copy them back to the list. - */ - ++client->serial; - - PKGCONF_TRACE(client, "flattening 'Requires' deps"); - flatten_dependency_set(client, &world->required); - - ++client->serial; - - PKGCONF_TRACE(client, "flattening 'Requires.private' deps"); - flatten_dependency_set(client, &world->requires_private); - return PKGCONF_PKG_ERRF_OK; }