From 0b50672a33574dce5ba493c13f6c00f798d05520 Mon Sep 17 00:00:00 2001 From: Juan RP Date: Thu, 20 Nov 2014 13:32:21 +0100 Subject: [PATCH] xbps-dgraph: added support to generate full dependency graphs. - Full dependency graphs can be generated with -f in pkgdb or repository mode (-R). - Removed -o and writing dot files by default, the generated dot file is written to stdout. - Misc tweaks that I cannot remember right now. --- bin/xbps-dgraph/main.c | 171 ++++++++++++++++++++++++++++++++--------- 1 file changed, 135 insertions(+), 36 deletions(-) diff --git a/bin/xbps-dgraph/main.c b/bin/xbps-dgraph/main.c index 82c29321..da5c6433 100644 --- a/bin/xbps-dgraph/main.c +++ b/bin/xbps-dgraph/main.c @@ -31,8 +31,10 @@ #include #include #include +#include #include +#include "queue.h" #ifdef __clang__ #pragma clang diagnostic ignored "-Wformat-nonliteral" @@ -79,6 +81,15 @@ struct defprops { { .sect = "node-sub", .prop = "opt-fillcolor", .val = "grey" } }; +struct pkgdep { + SLIST_ENTRY(pkgdep) pkgdep_entries; + unsigned int idx; + const char *pkgver; +}; + +static SLIST_HEAD(pkgdep_head, pkgdep) pkgdep_list = + SLIST_HEAD_INITIALIZER(pkgdep_list); + static void __attribute__((noreturn)) die(const char *fmt, ...) { @@ -103,8 +114,10 @@ usage(void) "Usage: xbps-dgraph [options] \n\n" " Options\n" " -c\t\tPath to configuration file\n" + " -d\t\tDebug mode shown to stderr\n" " -g\t\tGenerate a default config file\n" " -R\t\tEnable repository mode\n" + " -f\t\tGenerate a full dependency graph\n" " -r\t\t\n\n"); exit(EXIT_FAILURE); } @@ -325,12 +338,86 @@ parse_array_in_pkg_dictionary(FILE *f, xbps_dictionary_t plistd, } } +static void +process_fulldeptree(struct xbps_handle *xhp, FILE *f, + xbps_dictionary_t pkgd, xbps_array_t rdeps, + bool repomode) +{ + xbps_array_t rpkgrdeps; + struct pkgdep *pd; + const char *pkgver; + unsigned int i, x; + + xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); + + for (i = 0; i < xbps_array_count(rdeps); i++) { + xbps_dictionary_t rpkgd; + const char *pkgdep; + unsigned int pkgidx = 0; + bool found = false; + + xbps_array_get_cstring_nocopy(rdeps, i, &pkgdep); + SLIST_FOREACH(pd, &pkgdep_list, pkgdep_entries) { + if (strcmp(pd->pkgver, pkgdep) == 0) { + found = true; + break; + } + pkgidx++; + } + if (!found) { + pd = malloc(sizeof(*pd)); + assert(pd); + pd->idx = pkgidx; + pd->pkgver = pkgdep; + SLIST_INSERT_HEAD(&pkgdep_list, pd, pkgdep_entries); + } + if (repomode) { + rpkgd = xbps_rpool_get_pkg(xhp, pkgdep); + } else { + rpkgd = xbps_pkgdb_get_pkg(xhp, pkgdep); + } + assert(rpkgd); + rpkgrdeps = xbps_dictionary_get(rpkgd, "run_depends"); + for (x = 0; x < xbps_array_count(rpkgrdeps); x++) { + struct pkgdep *ppd; + const char *rpkgdep; + + xbps_array_get_cstring_nocopy(rpkgrdeps, x, &rpkgdep); + SLIST_FOREACH(ppd, &pkgdep_list, pkgdep_entries) { + if (xbps_pkgpattern_match(ppd->pkgver, rpkgdep)) + fprintf(f, "\t%u -> %u;\n", pkgidx, ppd->idx); + } + } + fprintf(f, "\t%u [label=\"%s\"", pkgidx, pkgdep); + if (repomode && xbps_pkgdb_get_pkg(xhp, pkgdep)) + fprintf(f, ",style=\"filled\",fillcolor=\"yellowgreen\""); + + fprintf(f, "]\n"); + } + i = 0; + SLIST_FOREACH(pd, &pkgdep_list, pkgdep_entries) + ++i; + + fprintf(f, "\t%u [label=\"%s\",style=\"filled\",fillcolor=\"darksalmon\"];\n", i, pkgver); + rpkgrdeps = xbps_dictionary_get(pkgd, "run_depends"); + for (x = 0; x < xbps_array_count(rpkgrdeps); x++) { + const char *rpkgdep; + + xbps_array_get_cstring_nocopy(rpkgrdeps, x, &rpkgdep); + SLIST_FOREACH(pd, &pkgdep_list, pkgdep_entries) { + if (xbps_pkgpattern_match(pd->pkgver, rpkgdep)) + fprintf(f, "\t%u -> %u;\n", i, pd->idx); + } + } +} + static void create_dot_graph(struct xbps_handle *xhp, FILE *f, xbps_dictionary_t plistd, xbps_dictionary_t confd, - bool repomode) + bool repomode, + bool fulldepgraph) { xbps_dictionary_t sub_confd; xbps_array_t allkeys, rdeps; @@ -366,33 +453,40 @@ create_dot_graph(struct xbps_handle *xhp, write_conf_property_on_stream(f, "node", confd); fprintf(f, "];\n"); - /* - * Process the node-sub section in config file. - */ - fprintf(f, " main ["); - sub_confd = xbps_dictionary_get(confd, "node-sub"); - xbps_dictionary_get_cstring_nocopy(sub_confd, "main-style", &cfprop); - if (cfprop) - fprintf(f, "style=%s,", cfprop); - xbps_dictionary_get_cstring_nocopy(sub_confd, "main-fillcolor", &cfprop); - if (cfprop) - fprintf(f, "fillcolor=\"%s\",", cfprop); - fprintf(f, "label=\"Dictionary\"];\n"); - /* * Process all objects in package's dictionary from its metadata * property list file, aka XBPS_META_PATH/..plist */ - if (repomode) { - rdeps = xbps_rpool_get_pkg_revdeps(xhp, pkgver); + if (fulldepgraph) { + if (repomode) { + rdeps = xbps_rpool_get_pkg_fulldeptree(xhp, pkgver); + } else { + rdeps = xbps_pkgdb_get_pkg_fulldeptree(xhp, pkgver); + } + process_fulldeptree(xhp, f, plistd, rdeps, repomode); } else { - rdeps = xbps_pkgdb_get_pkg_revdeps(xhp, pkgver); - } - if (xbps_array_count(rdeps)) - xbps_dictionary_set(plistd, "requiredby", rdeps); + /* + * Process the node-sub section in config file. + */ + fprintf(f, " main ["); + sub_confd = xbps_dictionary_get(confd, "node-sub"); + if (xbps_dictionary_get_cstring_nocopy(sub_confd, "main-style", &cfprop)) + fprintf(f, "style=%s,", cfprop); + if (xbps_dictionary_get_cstring_nocopy(sub_confd, "main-fillcolor", &cfprop)) + fprintf(f, "fillcolor=\"%s\",", cfprop); - allkeys = xbps_dictionary_all_keys(plistd); - parse_array_in_pkg_dictionary(f, plistd, sub_confd, allkeys); + fprintf(f, "label=\"Dictionary\"];\n"); + if (repomode) { + rdeps = xbps_rpool_get_pkg_revdeps(xhp, pkgver); + } else { + rdeps = xbps_pkgdb_get_pkg_revdeps(xhp, pkgver); + } + if (xbps_array_count(rdeps)) + xbps_dictionary_set(plistd, "requiredby", rdeps); + + allkeys = xbps_dictionary_all_keys(plistd); + parse_array_in_pkg_dictionary(f, plistd, sub_confd, allkeys); + } /* * Terminate the stream... */ @@ -408,22 +502,28 @@ main(int argc, char **argv) struct xbps_handle xh; FILE *f = NULL; const char *conf_file = NULL, *rootdir = NULL; - char *outfile; - int c, rv; - bool repomode = false; + int c, rv, flags = 0; + bool repomode = false, fulldepgraph = false; - while ((c = getopt(argc, argv, "c:gRr:")) != -1) { + while ((c = getopt(argc, argv, "c:dgfRr:")) != -1) { switch (c) { case 'c': /* Configuration file. */ conf_file = optarg; break; + case 'd': + flags |= XBPS_FLAG_DEBUG; + break; case 'g': /* Generate auto conf file. */ generate_conf_file(); exit(EXIT_SUCCESS); + case 'f': + /* generate a full dependency graph */ + fulldepgraph = true; + break; case 'R': - /* Also create graphs for reverse deps. */ + /* enable repository mode */ repomode = true; break; case 'r': @@ -447,14 +547,10 @@ main(int argc, char **argv) if (rootdir != NULL) xbps_strlcpy(xh.rootdir, rootdir, sizeof(xh.rootdir)); + xh.flags = flags; if ((rv = xbps_init(&xh)) != 0) die("failed to initialize libxbps: %s", strerror(rv)); - /* - * Output file will be .dot if not specified. - */ - outfile = xbps_xasprintf("%s.dot", argv[0]); - /* * If -c not set and config file does not exist, use defaults. */ @@ -482,13 +578,16 @@ main(int argc, char **argv) /* * Create the output FILE. */ - if ((f = fopen(outfile, "w")) == NULL) - die("cannot create target file '%s'", outfile); + if ((f = fdopen(STDOUT_FILENO, "w")) == NULL) + die("cannot open stdout"); /* * Create the dot(1) graph! */ - create_dot_graph(&xh, f, plistd, confd, repomode); - + if (fulldepgraph) { + create_dot_graph(&xh, f, plistd, confd, repomode, true); + } else { + create_dot_graph(&xh, f, plistd, confd, repomode, false); + } exit(EXIT_SUCCESS); }