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; }