Hello,

I'm following up on this (
http://lists.gnu.org/archive/html/m4-discuss/2010-10/msg00003.html) post,
because I've managed to find some time and write a patch for supporting
dependency generation with M4, which is something I've been really missing
for my makefiles.  You find the patch enclosed here as attachment.

To use the patched m4, you just call:

m4 -X depfile file1 file2 ...

this -X is equivalent to gcc's '-MD -MP -MF'.  -MT becomes unnecessary
because m4 outputs on stdout and you must set a rule with target and
prerequisites in the makefile.  If stdin is used, it does not get listed as
dependency, of course.  Since multiple target files are processed,
dependencies are listed for every target.  Only prerequisites which have not
been listed as targets are listed as phony targets.

Internally, I've augmented m4* context with four lists: prereqs, targets,
all_prereqs and all_targets.  They get allocated and filled by m4_add_dep(),
dumped and freed in main().  I've extended m4_push_file() with a few flags
to signal how to call m4_add_dep().

Hope the feature is ok and you like it: I'd be happy to see it within the
tool.
Of course corrections/suggestions/changes/improvements are always welcome!

Best Regards,
Lorenzo
From 7e6b620e25f4241293f10701c51ba94a57bcfab6 Mon Sep 17 00:00:00 2001
From: Lorenzo Di Gregorio <lorenzo.digrego...@gmail.com>
Date: Tue, 19 Oct 2010 13:34:10 +0200
Subject: [PATCH] Support for GNU make dependency generation.

all files on the command line are targets and all read files are prerequisites.
The dependency information gets generated in the form:

target1 : prerequisiteA ...
target2 : prerequisiteX ...

of course, targets can be prerequisites of other targets.  All prerequisites
which are not listed as targets already, become phony targets like:

prerequisiteJ :
prerequisiteK :

the option to generate dependency information is -X,--depend=FILE.
---
 m4/input.c     |   58 +++++++++++++++++++++++++++++-
 m4/m4module.h  |    5 ++-
 m4/m4private.h |    5 +++
 modules/m4.c   |    2 +-
 src/main.c     |  109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 5 files changed, 173 insertions(+), 6 deletions(-)

diff --git a/m4/input.c b/m4/input.c
index 3804b66..85a5ff5 100644
--- a/m4/input.c
+++ b/m4/input.c
@@ -428,6 +428,48 @@ file_consume (m4_input_block *me, m4 *context, size_t len)
     assert (false);
 }
 
+/* m4_add_dep () adds new files to the list of prerequisites or to the list of
+   target files.  The list entries consist of a pointer to the next entry
+   followed by a zero-terminated C string. */
+
+void m4_add_dep(void **list, const char *title)
+{
+  void *this;
+  void *last;
+  void *buffer;
+
+  last = NULL;
+  this = *list;
+
+  while(this != NULL)
+    {
+      if (strcmp((char *) (((void **) this)+1),title) == 0) return;
+      last = this;
+      this = *((void **) this);
+    }
+
+  buffer = malloc(sizeof(void *)+strlen(title));
+
+  if (buffer == NULL)
+    {
+      assert (!"ERROR: failed to allocate memory.");
+      abort ();
+    }
+
+  strcpy((char *) (((void **) buffer)+1),title);
+  *((void **) buffer) = NULL;
+
+  if (last == NULL)
+    {
+      *list = buffer;
+      return;
+    }
+
+  *((void **) last) = buffer;
+  return;
+
+}
+
 /* m4_push_file () pushes an input file FP with name TITLE on the
   input stack, saving the current file name and line number.  If next
   is non-NULL, this push invalidates a call to m4_push_string_init (),
@@ -439,7 +481,7 @@ file_consume (m4_input_block *me, m4 *context, size_t len)
   alone is taken as belonging to the line it ends, and the current
   line number is not incremented until the next character is read.  */
 void
-m4_push_file (m4 *context, FILE *fp, const char *title, bool close_file)
+m4_push_file (m4 *context, FILE *fp, const char *title, bool close_file, bool prereq, bool target)
 {
   m4_input_block *i;
 
@@ -449,6 +491,20 @@ m4_push_file (m4 *context, FILE *fp, const char *title, bool close_file)
       next = NULL;
     }
 
+  if (context->dep_file != NULL)
+    {
+      if (prereq == true)
+        {
+          m4_add_dep(&(context->prereqs), title);
+          m4_add_dep(&(context->all_prereqs), title);
+        }
+      if (target == true)
+        {
+          m4_add_dep(&(context->targets), title);
+          m4_add_dep(&(context->all_targets), title);
+        }
+    }
+
   m4_debug_message (context, M4_DEBUG_TRACE_INPUT, _("input read from %s"),
                     quotearg_style (locale_quoting_style, title));
 
diff --git a/m4/m4module.h b/m4/m4module.h
index fa245ec..d61a02a 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -522,13 +522,16 @@ extern  void    m4_skip_line    (m4 *context, const m4_call_info *);
 
 /* push back input */
 
-extern  void    m4_push_file    (m4 *, FILE *, const char *, bool);
+extern  void    m4_push_file    (m4 *, FILE *, const char *, bool, bool, bool);
 extern  void    m4_push_builtin (m4 *, m4_obstack *, m4_symbol_value *);
 extern  m4_obstack      *m4_push_string_init    (m4 *, const char *, int);
 extern  void    m4_push_string_finish   (void);
 extern  bool    m4_pop_wrapup   (m4 *);
 extern  void    m4_input_print  (m4 *, m4_obstack *, int);
 
+/* dependencies */
+
+extern  void    m4_add_dep      (void **, const char *);
 
 
 /* --- OUTPUT MANAGEMENT --- */
diff --git a/m4/m4private.h b/m4/m4private.h
index f7b47f8..a205435 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -65,6 +65,11 @@ struct m4 {
   int                   output_line;    /* Current output line.  */
 
   FILE *        debug_file;             /* File for debugging output.  */
+  FILE *        dep_file;
+  void *        prereqs;
+  void *        all_prereqs;
+  void *        targets;
+  void *        all_targets;
   m4_obstack    trace_messages;
   int           exit_status;            /* Cumulative exit status.  */
   int           current_diversion;      /* Current output diversion.  */
diff --git a/modules/m4.c b/modules/m4.c
index 9091d81..9696320 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -684,7 +684,7 @@ include (m4 *context, int argc, m4_macro_args *argv, bool silent)
       return;
     }
 
-  m4_push_file (context, fp, name, true);
+  m4_push_file (context, fp, name, true, true, false);
   free (name);
 }
 
diff --git a/src/main.c b/src/main.c
index 240e241..148c830 100644
--- a/src/main.c
+++ b/src/main.c
@@ -101,6 +101,7 @@ Operation modes:\n\
   -r, --regexp-syntax[=SPEC]   set default regexp syntax to SPEC [GNU_M4]\n\
       --safer                  disable potentially unsafe builtins\n\
   -W, --warnings               enable all warnings\n\
+  -X, --depend=FILE            output makefile dependencies into FILE\n\
 "), stdout);
       puts ("");
       fputs (_("\
@@ -247,6 +248,7 @@ static const struct option long_options[] =
   {"traditional", no_argument, NULL, 'G'},
   {"undefine", required_argument, NULL, 'U'},
   {"warnings", no_argument, NULL, 'W'},
+  {"depend", required_argument, NULL, 'X'},
 
   {"arglength", required_argument, NULL, ARGLENGTH_OPTION},
   {"debugfile", optional_argument, NULL, DEBUGFILE_OPTION},
@@ -272,7 +274,7 @@ static const struct option long_options[] =
    behavior also handles -s between files.  Starting OPTSTRING with
    '-' forces getopt_long to hand back file names as arguments to opt
    '\1', rather than reordering the command line.  */
-#define OPTSTRING "-B:D:EF:GH:I:L:M:PQR:S:T:U:Wbcd::egil:m:o:p:r::st:"
+#define OPTSTRING "-B:D:EF:GH:I:L:M:PQR:S:T:U:X:Wbcd::egil:m:o:p:r::st:"
 
 /* For determining whether to be interactive.  */
 enum interactive_choice
@@ -304,7 +306,7 @@ process_file (m4 *context, const char *name)
   if (STREQ (name, "-"))
     /* TRANSLATORS: This is a short name for `standard input', used
        when a command line file was given as `-'.  */
-    m4_push_file (context, stdin, _("stdin"), false);
+    m4_push_file (context, stdin, _("stdin"), false, false, false);
   else
     {
       char *full_name;
@@ -315,10 +317,43 @@ process_file (m4 *context, const char *name)
                     quotearg_style (locale_quoting_style, name));
           return;
         }
-      m4_push_file (context, fp, full_name, true);
+      m4_push_file (context, fp, full_name, true, false, true);
       free (full_name);
     }
   m4_macro_expand_input (context);
+
+  /* Dump the dependencies */
+
+  if (context->dep_file != NULL)
+    {
+      void *last;
+      void *this;
+
+      this = context->targets;
+      while(this != NULL)
+        {
+          fprintf(context->dep_file,"%s ",(char *) (((void **) this)+1));
+          last = this;
+          this = *((void **) this);
+          free(last);
+        }
+
+      fprintf(context->dep_file,":", name);
+
+      this = context->prereqs;
+      while(this != NULL)
+        {
+          fprintf(context->dep_file," %s",(char *) (((void **) this)+1));
+          last = this;
+          this = *((void **) this);
+          free(last);
+        }
+
+      fprintf(context->dep_file,"\n");
+
+    }
+  context->targets = NULL;
+  context->prereqs = NULL;
 }
 
 
@@ -357,6 +392,12 @@ main (int argc, char *const *argv, char *const *envp)
 
   m4__module_init (context);
 
+  context->dep_file     = NULL;
+  context->prereqs      = NULL;
+  context->all_prereqs  = NULL;
+  context->targets      = NULL;
+  context->all_targets  = NULL;
+
 #ifdef USE_STACKOVF
   setup_stackovf_trap (argv, envp, stackovf_handler);
 #endif
@@ -501,6 +542,15 @@ main (int argc, char *const *argv, char *const *envp)
             }
           break;
 
+        case 'X':
+          if ((context->dep_file = fopen (optarg,"w")) == NULL)
+            {
+              m4_error (context, EXIT_FAILURE, 0, NULL,
+                        _("failed to open dependency file %s for writing"),
+                        quotearg_style (locale_quoting_style, optarg));
+            }
+          break;
+
         case 'P':
           m4_set_prefix_builtins_opt (context, true);
           break;
@@ -772,6 +822,59 @@ main (int argc, char *const *argv, char *const *envp)
     for (; optind < argc; optind++)
       process_file (context, argv[optind]);
 
+  /* Dump the dependencies (phony targets) */
+
+  if (context->dep_file != NULL)
+    {
+      void *last;
+      void *this;
+      void *that;
+      bool  seen;
+
+      this = context->all_prereqs;
+      while (this != NULL)
+        {
+          /* check whether the file in 'this' is contained in context->all_targets */
+          seen = false;
+          that = context->all_targets;
+          while (that != NULL)
+            if (strcmp((char *) (((void **) this)+1),(char *) (((void **) that)+1)) == 0)
+              {
+                seen = true;
+                break;
+              }
+            else
+              that = *((void **) that);
+
+          if (seen == false)
+            fprintf(context->dep_file,"%s :\n",(char *) (((void **) this)+1));
+
+          last = this;
+          this = *((void **) this);
+          free(last);
+        }
+    }
+  context->all_prereqs = NULL;
+
+  /* free all_targets */
+
+  void *that;
+  void *last;
+  that = context->all_targets;
+  while (that != NULL)
+    {
+      last = that;
+      that = *((void **) that);
+      free(last);
+    }
+  context->all_targets = NULL;
+
+  if (fclose(context->dep_file) != 0)
+    {
+      assert (!"INTERNAL ERROR: cannot close dependency file.");
+      abort ();
+    }
+
   /* Now handle wrapup text.
      FIXME - when -F is in effect, should wrapped text be frozen?  */
   while (m4_pop_wrapup (context))
-- 
1.7.0.4

_______________________________________________
M4-patches mailing list
M4-patches@gnu.org
http://lists.gnu.org/mailman/listinfo/m4-patches

Reply via email to