Thank you for showing me graphviz, it's pretty neat!
The attached patch adds the ``-g, --graph'' options to make; it will
spit out a graphviz file to stdout. (The patch is against the 3.82
release)
Note that this will NOT include recursive use of make, I was working on
that, but couldn't get it working.
I've been manually adding the line
rankdir=LR;
into the graphs, and rendering them with dot.
~ LukeShu
On Thu, 2010-08-19 at 19:57 +0800, Lynn Lin wrote:
> We can generate some data format which describe dependency,then we can
> use graphviz to get dependency graph
>
> On 8/19/10, Luke Shumaker <[email protected]> wrote:
> >> Hi,
> >> is that possible that we can add an option to "make" that we can
> >> generatge dependency graph? This will help us to debug issues (I know
> >> we already have --debug and --print-data-base option)
> >>
> >>
> >> Thanks
> >> Lynn
> >
> > Since the dependency graph is more complex than just a tree (lines must
> > cross) this is difficult to do with ASCII output; if you were to patch
> > make to print out dependencies of files as it processes them, you would
> > end up with many ``This part of the tree has already been printed''
> > messages.
> >
> > Running the command:
> > make YOUR_ARGS -pqs|sed -n 's/.$/& /;/\# Not a
> > target:/N;/^[^\t#=%][^#=%]*:[^=]/p'
> >
> > should give you a good simple list of targets and their dependencies.
> > Given the proper graphing software, it should be trivial to generate a
> > graph from this data.
> >
> > ~ LukeShu
> >
> >
diff -ur make-3.82/file.c make-wch/file.c
--- make-3.82/file.c 2010-07-12 21:20:39.000000000 -0400
+++ make-wch/file.c 2010-08-19 23:28:20.162660391 -0400
@@ -1021,6 +1021,129 @@
hash_print_stats (&files, stdout);
}
+/* Dump the dependency graph to a Graphviz file (on stdout) */
+
+void
+print_graph_prereqs (const char *filename, const struct dep *deps)
+{
+ const struct dep *ood = 0;
+
+ /* Print all normal dependencies; note any order-only deps. */
+ for (; deps != 0; deps = deps->next)
+ if (! deps->ignore_mtime)
+ printf (" \"%s\" -> \"%s\";\n", filename, dep_name (deps));
+
+ /* Print order-only deps, if we have any. */
+ if (ood)
+ {
+ for (ood = ood->next; ood != 0; ood = ood->next)
+ if (ood->ignore_mtime)
+ printf (" %s -> %s [style=dotted];\n", filename, dep_name (ood));
+ /* XXX: we need to distinguish these some how.
+ * Is dotting them the right way? */
+ }
+}
+
+static void
+print_graph_file (const void *item)
+{
+ const struct file *f = item;
+
+ int built_in_special_target=(
+ (0==strcmp(f->name,".PHONY"))
+ || (0==strcmp(f->name,".SUFFIXES"))
+ || (0==strcmp(f->name,".DEFAULT"))
+ || (0==strcmp(f->name,".PRECIOUS"))
+ || (0==strcmp(f->name,".INTERMEDIATE"))
+ || (0==strcmp(f->name,".SECONDARY"))
+ || (0==strcmp(f->name,".SECONDEXPANSION"))
+ || (0==strcmp(f->name,".DELETE_ON_ERROR"))
+ || (0==strcmp(f->name,".IGNORE"))
+ || (0==strcmp(f->name,".LOW_RESOLUTION_TIME"))
+ || (0==strcmp(f->name,".SILENT"))
+ || (0==strcmp(f->name,".EXPORT_ALL_VARIABLES"))
+ || (0==strcmp(f->name,".NOTPARALLEL"))
+ || (0==strcmp(f->name,".ONESHELL"))
+ || (0==strcmp(f->name,".POSIX"))
+ );
+ if ((f->is_target) && (!built_in_special_target))
+ {
+ printf (" \"%s\" [", f->name);
+ /* XXX some of these should be attached to the nodes in some way;
+ * though I'm not sure what style changes should be made for which ones.
+ * ~ LukeShu
+ if (f->double_colon) puts (_("// Double-colon rule."));
+ if (f->precious) puts (_("// Precious file (prerequisite of .PRECIOUS)."));
+ */if (f->phony) puts (_(" color=blue "));/*
+ if (f->cmd_target) puts (_("// Command line target."));
+ if (f->dontcare) puts (_("// A default, MAKEFILES, or -include/sinclude makefile."));
+ if (f->tried_implicit) puts (_("// Implicit rule search has been done."));
+ else puts (_("// Implicit rule search has not been done."));
+ if (f->stem != 0) printf (_("// Implicit/static pattern stem: `%s'\n"), f->stem);
+ if (f->intermediate) puts (_("// File is an intermediate prerequisite."));
+ if (f->also_make != 0)
+ {
+ const struct dep *d;
+ fputs (_("# Also makes:"), stdout);
+ for (d = f->also_make; d != 0; d = d->next)
+ printf (" %s\n", dep_name (d));
+ }
+ if (f->last_mtime == UNKNOWN_MTIME) puts (_("// Modification time never checked."));
+ else if (f->last_mtime == NONEXISTENT_MTIME) puts (_("// File does not exist."));
+ else if (f->last_mtime == OLD_MTIME) puts (_("// File is very old."));
+ else
+ {
+ char buf[FILE_TIMESTAMP_PRINT_LEN_BOUND + 1];
+ file_timestamp_sprintf (buf, f->last_mtime);
+ printf (_("// Last modified %s\n"), buf);
+ }
+ if (f->updated) puts (_("// File has been updated."));
+ else puts (_("// File has not been updated."));
+ switch (f->command_state)
+ {
+ case cs_running: puts (_("// Update: Running (THIS IS A BUG).")); break;
+ case cs_deps_running: puts (_("// Update: Dependencies running (THIS IS A BUG).")); break;
+ case cs_not_started:
+ case cs_finished:
+ switch (f->update_status)
+ {
+ case -1: break;
+ case 0: puts (_("// Update: Successfully")); break;
+ case 1: assert (question_flag);
+ puts (_("// Update: Needs to be (-q is set)")); break;
+ case 2: puts (_("// Update: Failed")); break;
+ default: puts (_("// Update: Invalid `update_status' value));
+ fflush (stdout);
+ fflush (stderr);
+ abort ();
+ }
+ break;
+ default: puts (_("// Update: Invalid `command_state' value"));
+ fflush (stdout);
+ fflush (stderr);
+ abort ();
+ }
+ if (f->variables != 0) print_file_variables (f);
+ if (f->cmds != 0) print_commands (f->cmds);
+ */
+ puts("];");
+ print_graph_prereqs (f->name, f->deps);
+ }
+
+ if (f->prev)
+ print_graph_file ((const void *) f->prev);
+}
+
+void
+print_graph (void)
+{
+ printf ("%sgraph make%i {\n",
+ (makelevel==0)?"di":"sub",
+ getpid());
+ hash_map (&files, print_graph_file);
+ puts ("}");
+}
+
/* Verify the integrity of the data base of files. */
#define VERIFY_CACHED(_p,_n) \
diff -ur make-3.82/filedef.h make-wch/filedef.h
--- make-3.82/filedef.h 2010-07-12 21:20:39.000000000 -0400
+++ make-wch/filedef.h 2010-08-19 21:09:34.314660209 -0400
@@ -116,6 +116,7 @@
char *build_target_list (char *old_list);
void print_prereqs (const struct dep *deps);
void print_file_data_base (void);
+void print_graph (void);
#if FILE_TIMESTAMP_HI_RES
# define FILE_TIMESTAMP_STAT_MODTIME(fname, st) \
diff -ur make-3.82/main.c make-wch/main.c
--- make-3.82/main.c 2010-07-19 03:10:53.000000000 -0400
+++ make-wch/main.c 2010-08-19 22:31:29.182649083 -0400
@@ -175,6 +175,11 @@
int print_data_base_flag = 0;
+/* Nonzero means don't remake anything, just print the dependency graph
+ that results from reading the makefile (-g). */
+
+int print_graph_flag = 0;
+
/* Nonzero means don't remake anything; just return a nonzero status
if the specified targets are not up to date (-q). */
@@ -321,6 +326,9 @@
-f FILE, --file=FILE, --makefile=FILE\n\
Read FILE as a makefile.\n"),
N_("\
+ -g, --graph Print make's internal dependency graph\n\
+ in Graphviz format.\n"),
+ N_("\
-h, --help Print this message and exit.\n"),
N_("\
-i, --ignore-errors Ignore errors from recipes.\n"),
@@ -385,6 +393,7 @@
#endif
{ 'e', flag, &env_overrides, 1, 1, 0, 0, 0, "environment-overrides", },
{ 'f', filename, &makefiles, 0, 0, 0, 0, 0, "file" },
+ { 'g', flag, &print_graph_flag, 1, 1, 0, 0, 0, "graph" },
{ 'h', flag, &print_usage_flag, 0, 0, 0, 0, 0, "help" },
{ 'i', flag, &ignore_errors_flag, 1, 1, 0, 0, 0, "ignore-errors" },
{ 'I', filename, &include_directories, 1, 1, 0, 0, 0,
@@ -2278,33 +2287,37 @@
{
int status;
+ if (print_graph_flag)
+ status=0;
+ else
+ {
+ switch (update_goal_chain (goals))
+ {
+ case -1:
+ /* Nothing happened. */
+ case 0:
+ /* Updated successfully. */
+ status = makefile_status;
+ break;
+ case 1:
+ /* We are under -q and would run some commands. */
+ status = MAKE_TROUBLE;
+ break;
+ case 2:
+ /* Updating failed. POSIX.2 specifies exit status >1 for this;
+ but in VMS, there is only success and failure. */
+ status = MAKE_FAILURE;
+ break;
+ default:
+ abort ();
+ }
- switch (update_goal_chain (goals))
- {
- case -1:
- /* Nothing happened. */
- case 0:
- /* Updated successfully. */
- status = makefile_status;
- break;
- case 1:
- /* We are under -q and would run some commands. */
- status = MAKE_TROUBLE;
- break;
- case 2:
- /* Updating failed. POSIX.2 specifies exit status >1 for this;
- but in VMS, there is only success and failure. */
- status = MAKE_FAILURE;
- break;
- default:
- abort ();
- }
-
- /* If we detected some clock skew, generate one last warning */
- if (clock_skew_detected)
- error (NILF,
- _("warning: Clock skew detected. Your build may be incomplete."));
-
+ /* If we detected some clock skew, generate one last warning */
+ if (clock_skew_detected)
+ error (NILF,
+ _("warning: Clock skew detected. Your build may be incomplete."));
+ }
+
/* Exit. */
die (status);
}
@@ -3042,7 +3055,7 @@
{
static int printed_version = 0;
- char *precede = print_data_base_flag ? "# " : "";
+ char *precede = (print_data_base_flag||print_graph_flag) ? "# " : "";
if (printed_version)
/* Do it only once. */
@@ -3188,6 +3201,10 @@
if (print_data_base_flag)
print_data_base ();
+
+ if (print_graph_flag)
+ print_graph ();
+
verify_file_data_base ();
clean_jobserver (status);
_______________________________________________
Help-make mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/help-make