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.
This commit is contained in:
parent
2e3213de25
commit
0b50672a33
@ -31,8 +31,10 @@
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <xbps.h>
|
||||
#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] <pkgname>\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<rootdir>\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/.<pkgname>.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 <pkgname>.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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user