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 <stdarg.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include <xbps.h>
|
#include <xbps.h>
|
||||||
|
#include "queue.h"
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
#pragma clang diagnostic ignored "-Wformat-nonliteral"
|
#pragma clang diagnostic ignored "-Wformat-nonliteral"
|
||||||
@ -79,6 +81,15 @@ struct defprops {
|
|||||||
{ .sect = "node-sub", .prop = "opt-fillcolor", .val = "grey" }
|
{ .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))
|
static void __attribute__((noreturn))
|
||||||
die(const char *fmt, ...)
|
die(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
@ -103,8 +114,10 @@ usage(void)
|
|||||||
"Usage: xbps-dgraph [options] <pkgname>\n\n"
|
"Usage: xbps-dgraph [options] <pkgname>\n\n"
|
||||||
" Options\n"
|
" Options\n"
|
||||||
" -c\t\tPath to configuration file\n"
|
" -c\t\tPath to configuration file\n"
|
||||||
|
" -d\t\tDebug mode shown to stderr\n"
|
||||||
" -g\t\tGenerate a default config file\n"
|
" -g\t\tGenerate a default config file\n"
|
||||||
" -R\t\tEnable repository mode\n"
|
" -R\t\tEnable repository mode\n"
|
||||||
|
" -f\t\tGenerate a full dependency graph\n"
|
||||||
" -r\t\t<rootdir>\n\n");
|
" -r\t\t<rootdir>\n\n");
|
||||||
exit(EXIT_FAILURE);
|
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
|
static void
|
||||||
create_dot_graph(struct xbps_handle *xhp,
|
create_dot_graph(struct xbps_handle *xhp,
|
||||||
FILE *f,
|
FILE *f,
|
||||||
xbps_dictionary_t plistd,
|
xbps_dictionary_t plistd,
|
||||||
xbps_dictionary_t confd,
|
xbps_dictionary_t confd,
|
||||||
bool repomode)
|
bool repomode,
|
||||||
|
bool fulldepgraph)
|
||||||
{
|
{
|
||||||
xbps_dictionary_t sub_confd;
|
xbps_dictionary_t sub_confd;
|
||||||
xbps_array_t allkeys, rdeps;
|
xbps_array_t allkeys, rdeps;
|
||||||
@ -366,23 +453,29 @@ create_dot_graph(struct xbps_handle *xhp,
|
|||||||
write_conf_property_on_stream(f, "node", confd);
|
write_conf_property_on_stream(f, "node", confd);
|
||||||
fprintf(f, "];\n");
|
fprintf(f, "];\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process all objects in package's dictionary from its metadata
|
||||||
|
* property list file, aka XBPS_META_PATH/.<pkgname>.plist
|
||||||
|
*/
|
||||||
|
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 {
|
||||||
/*
|
/*
|
||||||
* Process the node-sub section in config file.
|
* Process the node-sub section in config file.
|
||||||
*/
|
*/
|
||||||
fprintf(f, " main [");
|
fprintf(f, " main [");
|
||||||
sub_confd = xbps_dictionary_get(confd, "node-sub");
|
sub_confd = xbps_dictionary_get(confd, "node-sub");
|
||||||
xbps_dictionary_get_cstring_nocopy(sub_confd, "main-style", &cfprop);
|
if (xbps_dictionary_get_cstring_nocopy(sub_confd, "main-style", &cfprop))
|
||||||
if (cfprop)
|
|
||||||
fprintf(f, "style=%s,", cfprop);
|
fprintf(f, "style=%s,", cfprop);
|
||||||
xbps_dictionary_get_cstring_nocopy(sub_confd, "main-fillcolor", &cfprop);
|
if (xbps_dictionary_get_cstring_nocopy(sub_confd, "main-fillcolor", &cfprop))
|
||||||
if (cfprop)
|
|
||||||
fprintf(f, "fillcolor=\"%s\",", cfprop);
|
fprintf(f, "fillcolor=\"%s\",", cfprop);
|
||||||
fprintf(f, "label=\"Dictionary\"];\n");
|
|
||||||
|
|
||||||
/*
|
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) {
|
if (repomode) {
|
||||||
rdeps = xbps_rpool_get_pkg_revdeps(xhp, pkgver);
|
rdeps = xbps_rpool_get_pkg_revdeps(xhp, pkgver);
|
||||||
} else {
|
} else {
|
||||||
@ -393,6 +486,7 @@ create_dot_graph(struct xbps_handle *xhp,
|
|||||||
|
|
||||||
allkeys = xbps_dictionary_all_keys(plistd);
|
allkeys = xbps_dictionary_all_keys(plistd);
|
||||||
parse_array_in_pkg_dictionary(f, plistd, sub_confd, allkeys);
|
parse_array_in_pkg_dictionary(f, plistd, sub_confd, allkeys);
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Terminate the stream...
|
* Terminate the stream...
|
||||||
*/
|
*/
|
||||||
@ -408,22 +502,28 @@ main(int argc, char **argv)
|
|||||||
struct xbps_handle xh;
|
struct xbps_handle xh;
|
||||||
FILE *f = NULL;
|
FILE *f = NULL;
|
||||||
const char *conf_file = NULL, *rootdir = NULL;
|
const char *conf_file = NULL, *rootdir = NULL;
|
||||||
char *outfile;
|
int c, rv, flags = 0;
|
||||||
int c, rv;
|
bool repomode = false, fulldepgraph = false;
|
||||||
bool repomode = false;
|
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "c:gRr:")) != -1) {
|
while ((c = getopt(argc, argv, "c:dgfRr:")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'c':
|
case 'c':
|
||||||
/* Configuration file. */
|
/* Configuration file. */
|
||||||
conf_file = optarg;
|
conf_file = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'd':
|
||||||
|
flags |= XBPS_FLAG_DEBUG;
|
||||||
|
break;
|
||||||
case 'g':
|
case 'g':
|
||||||
/* Generate auto conf file. */
|
/* Generate auto conf file. */
|
||||||
generate_conf_file();
|
generate_conf_file();
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
case 'f':
|
||||||
|
/* generate a full dependency graph */
|
||||||
|
fulldepgraph = true;
|
||||||
|
break;
|
||||||
case 'R':
|
case 'R':
|
||||||
/* Also create graphs for reverse deps. */
|
/* enable repository mode */
|
||||||
repomode = true;
|
repomode = true;
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
@ -447,14 +547,10 @@ main(int argc, char **argv)
|
|||||||
if (rootdir != NULL)
|
if (rootdir != NULL)
|
||||||
xbps_strlcpy(xh.rootdir, rootdir, sizeof(xh.rootdir));
|
xbps_strlcpy(xh.rootdir, rootdir, sizeof(xh.rootdir));
|
||||||
|
|
||||||
|
xh.flags = flags;
|
||||||
if ((rv = xbps_init(&xh)) != 0)
|
if ((rv = xbps_init(&xh)) != 0)
|
||||||
die("failed to initialize libxbps: %s", strerror(rv));
|
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.
|
* 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.
|
* Create the output FILE.
|
||||||
*/
|
*/
|
||||||
if ((f = fopen(outfile, "w")) == NULL)
|
if ((f = fdopen(STDOUT_FILENO, "w")) == NULL)
|
||||||
die("cannot create target file '%s'", outfile);
|
die("cannot open stdout");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the dot(1) graph!
|
* 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);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user