Eric,

I have incorporated most of the changes that you have identified.  This
patch is from very recent branch-1.6.

David

 NEWS              |   2 +
 THANKS            |   2 +
 checks/check-them |  37 ++++++++++-
 checks/get-them   |  13 +++-
 doc/m4.texi       | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/builtin.c     |  16 ++++-
 src/eval.c        |   1 +
 src/m4.c          | 151 +++++++++++++++++++++++++++++++++++++++++-
 src/m4.h          |  10 +++
 src/path.c        |  80 ++++++++++++++++++++++
 10 files changed, 496 insertions(+), 11 deletions(-)
diff --git a/NEWS b/NEWS
index 97c4d9c0..23d5bd5f 100644
--- a/NEWS
+++ b/NEWS
@@ -196,6 +196,8 @@ following beta features that were deemed worth deferring 
until 1.6:
* Noteworthy changes in release 1.4.19 (2021-05-28) [stable] +** Add new capability to automatically generate make dependency rules.
+
 ** A number of portability improvements inherited from gnulib, including
    the ability to perform stack overflow detection on more platforms
    without linking to GNU libsigsegv.
diff --git a/THANKS b/THANKS
index d441f56a..2e4de505 100644
--- a/THANKS
+++ b/THANKS
@@ -39,6 +39,7 @@ David Apps                   unlisted
 David arnsteinarnst...@pobox.com
 David J. mackenzie...@uunet.uu.net
 David perlinda...@nanosoft.com
+David warmea136...@warme.net
 Elbert polelbert....@gmail.com
 Elias benaliel...@users.sourceforge.net
 Erez zadok...@cs.columbia.edu
@@ -81,6 +82,7 @@ Konrad schwarzkonrad.schw...@siemens.com
 Kristine lundl...@lpnaxp.in2p3.fr
 Krste asanovickr...@icsi.berkeley.edu
 Lawson chanlawson.c...@tdsecurities.com
+Lorenzo Di gregoriolorenzo.digrego...@gmail.com
 Marijn Schouten              unlisted
 Marion hakansonhakan...@cse.ogi.edu
 Mark seiden...@seiden.com
diff --git a/checks/check-them b/checks/check-them
index 24e68029..09c7c14f 100755
--- a/checks/check-them
+++ b/checks/check-them
@@ -37,6 +37,7 @@ out=$tmp/m4-out
 err=$tmp/m4-err
 xout=$tmp/m4-xout
 xerr=$tmp/m4-xerr
+xaux=$tmp/m4-xaux
 failed=
 skipped=
 strip_needed=false
@@ -104,7 +105,8 @@ do
       continue ;;
   esac
- options=`${SED} -ne '3s/^dnl @ extra options: //p;3q' "$file"`
+  options=`${SED} -ne '3s/^dnl @ extra options: //p;3q' "$file" \
+    | ${SED} -e "s/TMP\//$tmp\//"`
   ${SED} -e '/^dnl @/d' -e '/^\^D$/q' "$file" \
     | LC_MESSAGES=C LANG=C M4PATH=$examples "$m4" -d $options - >$out 2>$err
   stat=$?
@@ -126,8 +128,9 @@ do
xoutfile=`${SED} -n 's/^dnl @ expected output: //p' "$file"`
   if test -z "$xoutfile" ; then
-    ${SED} -e '/^dnl @result{}/!d' -e 's///' -e "s|examples/|$examples/|" \
-      -e "s|@value{VERSION}|$m4version|" "$file" > $xout
+    ${SED} -ne 's/^dnl @result{}//p' "$file" \
+    | ${SED} -e "s|examples/|$examples/|" \
+      -e "s|@value{VERSION}|$m4version|" > $xout
   else
     cp "$examples/$xoutfile" $xout
   fi
@@ -149,6 +152,8 @@ do
         "$examples/$xerrfile" > $xerr ;;
   esac
+ auxfile=`${SED} -n 's/^dnl @ expected auxfile: //p' "$file"`
+
   # For the benefit of mingw, normalize \r\n line endings
   if $strip_needed ; then
     tr -d '\015' < $out > $out.t
@@ -179,6 +184,32 @@ do
     diff $diffopts $xerr $err
   fi
+ if test -n "$auxfile"; then
+    tauxfile=`echo "$auxfile" | ${SED} -e "s/TMP\//$tmp\//"`
+    if test -f "$tauxfile"; then
+      ${SED} -ne 's|^dnl @auxresult{[A-Za-z0-9_/]*}||p' < $file >$xaux
+      if $strip_needed ; then
+        tr -d '\015' < $tauxfile > $tauxfile.t
+        mv $tauxfile.t $tauxfile
+        tr -d '\015' < $xaux > $xaux.t
+        mv $xaux.t $xaux
+      fi
+      if cmp -s $tauxfile $xaux; then
+        :
+      else
+        failed="$failed $file:aux"
+        echo `${SED} -e 's/^dnl //' -e 1q $file`
+        echo "$file auxfile mismatch"
+        diff $diffopts $xaux $tauxfile
+      fi
+      rm -f $tauxfile
+    else
+      failed="$failed $file:aux"
+      echo `${SED} -e 's/^dnl //' -e 1q $file`
+      echo "$file aux outfile $auxfile does not exist"
+    fi
+  fi
+
 done
rm -f $out $err $xout $xerr
diff --git a/checks/get-them b/checks/get-them
index 9028a6e3..dafdb591 100755
--- a/checks/get-them
+++ b/checks/get-them
@@ -36,6 +36,7 @@ BEGIN {
   options = "";
   xout = "";
   xerr = "";
+  auxfile = "";
 }
/^@node / {
@@ -83,6 +84,11 @@ BEGIN {
   gsub ("@comment xerr: ", "", xerr);
 }
+/^@comment auxfile: / {
+  auxfile = $0;
+  gsub ("@comment auxfile: ", "", auxfile);
+}
+
 /^@example$/, /^@end example$/ {
   if (seq < 0)
     next;
@@ -105,10 +111,13 @@ BEGIN {
         printf("dnl @ expected output: %s\n", xout) > file;
     if (xerr)
         printf("dnl @ expected error: %s\n", xerr) > file;
+    if (auxfile)
+        printf("dnl @ expected auxfile: %s\n", auxfile) > file;
     status = 0;
     options = "";
     xout = "";
     xerr = "";
+    auxfile = "";
     next;
   }
   if ($0 ~ /^@end example$/) {
@@ -116,9 +125,9 @@ BEGIN {
   }
   if ($0 ~ /^\^D$/)
     next;
-  if ($0 ~ /^\$ @kbd/)
+  if ($0 ~ /^\$ @kbd/ || $0 ~ /^@kbd/)
     next;
-  if ($0 ~ /^@result\{\}/ || $0 ~ /^@error\{\}/)
+  if ($0 ~ /^@result\{\}/ || $0 ~ /^@error\{\}/ || $0 ~ 
/^@auxresult\{[A-Za-z0-9_\/]*\}/)
     prefix = "dnl ";
   else
prefix = ""; diff --git a/doc/m4.texi b/doc/m4.texi index 612ed945..e7a9d5f6 100644 --- a/doc/m4.texi +++ b/doc/m4.texi @@ -36,6 +36,14 @@ @r{[}@var{\varname\} = @samp{\default\}@r{]} @end macro +@c @auxresult{auxfile} +@c ------------ +@c Used in examples to indicate output produced within the +@c ``auxilliary output file.'' +@macro auxresult{auxfile} +\auxfile\ @b{@result{}} +@end macro + @comment %**end of header @comment ======================================================== @@ -148,6 +156,7 @@ * Preprocessor features:: Command line options for preprocessor features * Limits control:: Command line options for limits control * Frozen state:: Command line options for frozen state +* Make dependency generation:: Generating make dependency rules * Debugging options:: Command line options for debugging * Command line files:: Specifying input files on the command line @@ -583,6 +592,7 @@ * Preprocessor features:: Command line options for preprocessor features * Limits control:: Command line options for limits control * Frozen state:: Command line options for frozen state +* Make dependency generation:: Generating make dependency rules * Debugging options:: Command line options for debugging * Command line files:: Specifying input files on the command line @end menu @@ -904,6 +914,191 @@ files are read. @end table +@node Make dependency generation +@section Command line options for generating Makefile dependency rules + +Makefile dependency rules can be automatically generated by specifying +both the @code{--makedep=}@var{file} and +@code{--makedep-target=}@var{target} options. + +@table @code +@item --makedep=@var{file} +Causes @code{m4} to generate a dependency rule into the specified +@var{file}. Macro expansion output is still written to stdout as +normal. This option is analogous to the @code{-MF} option of +@code{gcc}. + +@item --makedep-target=@var{target} +Specifies @var{target} to be the target of the generated dependency +rule. The string @var{target} is used verbatim, and can contain several +logical targets separated by spaces. It is the user's responsibility to +properly express characters that @code{make} handles specially (such as +'@code{$}', or spaces within file names). Since @code{m4} sends its +macro expansion output to stdout, it never really knows the name of the +target file being generated, so the target must always be specified +explicitly by the user with this option. This option is analogous to +the @code{-MT} option of @code{gcc} (except that @code{gcc} allows +@code{-MT} to be specified multiple times). +@end table + +Note that the @code{--makedep=}@var{file} and +@code{makedep-target=}@var{target} options must either (a) both be +specified, or (b) neither be specified. They cannot be used +independently of each other. + +@comment options: --makedep=TMP/depfile --makedep-target=Target foo +@comment auxfile: TMP/depfile +@example +$ @kbd{m4 -I examples --makedep=TMP/depfile --makedep-target=Target foo} +@result{}bar +@auxresult{TMP/depfile}# Automatically generated by GNU m4. +@auxresult{TMP/depfile}Target: ./../examples/foo +@end example + +@comment Separate these two examples. +@sp 1 + +@comment options: --makedep=TMP/depfile --makedep-target=Target foo +@comment auxfile: TMP/depfile +@example +$ @kbd{m4 -I examples --makedep=TMP/depfile --makedep-target=Target foo -} +@result{}bar +include(`incl.m4') +@result{}Include file start +@result{}foo +@result{}Include file end +@result{} +^D +@auxresult{TMP/depfile}# Automatically generated by GNU m4. +@auxresult{TMP/depfile}Target: ./../examples/foo ./../examples/incl.m4 +@end example + +The following additional options can also be used when dependency rules +are being generated (these options are only valid when both +@code{makedep=}@var{file} and @code{makedep-target=}@var{target} have +also been specified): + +@table @code +@item --makedep-gen-missing-argfiles +Causes @code{m4} to assume that any file listed on the command line that +is missing (i.e., does not exist) is an automatically generated file. +@code{M4} includes such missing files as dependencies in the generated +rule regardless. In this case the dependency appears exactly as +specified on the command line and is not modified by any +@code{-I}@var{searchdir} prefixes. Note that the macro expansion output +generated to stdout will be incorrect when this happens because the +missing file is assumed to be an empty file. A warning is produced on +stderr for each missing command line file handled in this manner. + +@item --makedep-gen-missing-include +Causes @code{m4} to assume that any file included via the +@code{include()} macro that is missing (i.e., does not exist) is an +automatically generated file. @code{M4} includes such missing files as +dependencies in the generated rule regardless. In this case the +dependency appears exactly as specified in the argument to +@code{include()} and is not modified by any @code{-I}@var{searchdir} +prefixes. Note that the macro expansion output generated to stdout will +be incorrect when this happens because the missing file is assumed to be +an empty file. This option causes the @code{m4} @code{include()} macro +to behave like @code{sinclude()}, except that a warning message is +produced on stderr to indicate that the requested file was missing. +This option is analogous to the @code{-MG} option of @code{gcc}. + +@item --makedep-gen-missing-sinclude +Causes @code{m4} to assume that any file included via the +@code{sinclude()} macro that is missing (i.e., does not exist) is an +automatically generated file. @code{M4} includes such missing files as +dependencies in the generated rule regardless. In this case the +dependency appears exactly as specified in the argument to +@code{sinclude()} and is not modified by any @code{-I}@var{searchdir} +prefixes. Note that the macro expansion output generated to stdout will +be incorrect when this happens because the missing file is assumed to be +an empty file. This option does not alter @code{sinclude()}'s behavior +of silently ignoring requests to @code{sinclude()} files that do not exist. + +@item --makedep-gen-missing-all +This option is equivalent to specifying all three options +@code{--makedep-gen-missing-argfiles}, +@code{--makedep-gen-missing-include} and +@code{--makedep-gen-missing-sinclude}. +@end table + +Note that the above @code{makedep-gen-missing-*} options assume that the +missing files will ultimately not @code{include()} or @code{sinclude()} +any additional files -- if they do, then these additional files will be +missing from the generated dependency rules. + + +@comment status: 1 +@comment options: --makedep=TMP/depfile --makedep-target=Target --makedep-gen-missing-all foo none +@comment auxfile: TMP/depfile +@example +$ @kbd{m4 -I examples --makedep=TMP/depfile --makedep-target=Target \} +@kbd{ --makedep-gen-missing-all foo none -} +@result{}bar +@error{}m4: cannot open 'none': No such file or directory +include(`incl.m4') +@result{}Include file start +@result{}foo +@result{}Include file end +@result{} +include(`none2') +@result{} +@error{}m4:stdin:2: include: cannot open 'none2': No such file or directory +sinclude(`none3') +@result{} +^D +@auxresult{TMP/depfile}# Automatically generated by GNU m4. +@auxresult{TMP/depfile}Target: ./../examples/foo none ./../examples/incl.m4 none2 none3 +@end example + +The following options control the generation of ``phony'' targets for +certain classes of dependencies. These dummy rules are used to work +around errors @code{make} gives if you remove files without updating the +@code{Makefile} to match. Dependencies that match one or more of these +classes cause a single dummy rule to be generated for them: + +@table @code +@item --makedep-phony-argfiles +Causes @code{m4} to generate a ``phony'' target for each file that is +specified on the command line. + +@item --makedep-phony-include +Causes @code{m4} to generate a ``phony'' target for each file that is +the subject of an @code{include()} macro. This option is analogous to +the @code{-MP} option of @code{gcc}. + +@item --makedep-phony-sinclude +Causes @code{m4} to generate a ``phony'' target for each file that is +the subject of an @code{sinclude()} macro. + +@item --makedep-phony-all +is equivalent to specifying all three options: +@code{--makedep-phony-argfiles} +@code{--makedep-phony-include} +@code{--makedep-phony-sinclude}. +@end table + +@comment options: --makedep=TMP/depfile --makedep-target=Target --makedep-phony-all foo +@comment auxfile: TMP/depfile +@example +$ @kbd{m4 -I examples --makdep=TMP/depfile --makedep-target=Target \} +@kbd{ --makedep-phony-all foo -} +@result{}bar +include(`incl.m4') +@result{}Include file start +@result{}foo +@result{}Include file end +@result{} +^D +@auxresult{TMP/depfile}# Automatically generated by GNU m4. +@auxresult{TMP/depfile}Target: ./../examples/foo ./../examples/incl.m4 +@auxresult{TMP/depfile} +@auxresult{TMP/depfile}./../examples/foo: +@auxresult{TMP/depfile} +@auxresult{TMP/depfile}./../examples/incl.m4: +@end example + @node Debugging options @section Command line options for debugging diff --git a/src/builtin.c b/src/builtin.c index bf0d1798..36b9ed00 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -1494,13 +1494,14 @@ m4_changecom (struct obstack *obs MAYBE_UNUSED, int argc, argument, if it exists. Complain about inaccessible files iff SILENT is false. */ static void -include (int argc, macro_arguments *argv, bool silent) +include (int argc, macro_arguments *argv, bool silent, int ref_from) { const call_info *me = arg_info (argv); FILE *fp; char *name; const char *arg; size_t len; + int fail; if (bad_argc (me, argc, 1, 1)) return; @@ -1513,12 +1514,21 @@ include (int argc, macro_arguments *argv, bool silent) fp = m4_path_search (arg, false, &name); if (fp == NULL) { + fail = !silent; + if ((makedep_gen_missing & ref_from) != 0) + { + record_dependency (arg, ref_from); + fail = 0; + } if (!silent) m4_error (0, errno, me, _("cannot open %s"),
                   quotearg_style (locale_quoting_style, arg));
+      if (fail)
+        retcode = EXIT_FAILURE;
       return;
     }
+ record_dependency (name, ref_from);
   push_file (fp, name, true);
   free (name);
 }
@@ -1527,7 +1537,7 @@ include (int argc, macro_arguments *argv, bool silent)
 static void
 m4_include (struct obstack *obs MAYBE_UNUSED, int argc, macro_arguments *argv)
 {
-  include (argc, argv, false);
+  include (argc, argv, false, REF_INCLUDE);
 }
/* Include a file, ignoring errors. */
@@ -1535,7 +1545,7 @@ static void
 m4_sinclude (struct obstack *obs MAYBE_UNUSED, int argc,
              macro_arguments *argv)
 {
-  include (argc, argv, true);
+  include (argc, argv, true, REF_SINCLUDE);
 }
 
 /* More miscellaneous builtins -- "maketemp", "errprint", "__file__",
diff --git a/src/eval.c b/src/eval.c
index 69cd5e41..d91fa3cd 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -431,6 +431,7 @@ parse_expr (int32_t *v1, eval_error er, unsigned min_prec)
              that the implementation-defined overflow when casting
              unsigned to signed is a silent twos-complement
              wrap-around.  */
+          u3 = 1;
           if (v2 < 0)
             er = NEGATIVE_EXPONENT;
           else if (*v1 == 0 && v2 == 0)
diff --git a/src/m4.c b/src/m4.c
index 89a2ec2f..aa2327a5 100644
--- a/src/m4.c
+++ b/src/m4.c
@@ -65,6 +65,21 @@ int warning_status = 0;
 /* Artificial limit for expansion_level in macro.c.  */
 int nesting_limit = 1024;
+/* Pathname of dependency file being made (--makedep=PATH). */
+static const char *makedep_path = NULL;
+
+/* Target for dependency rule being made (--makedep-target=TARGET). */
+static const char *makedep_target = NULL;
+
+/* Bitmask of places that will assume non-existent files are actually
+   generated, and so a dependency should be listed regardless
+   (--makedep-gen-missing-*). */
+int makedep_gen_missing = REF_NONE;
+
+/* Bitmask of which places files are referenced from that will trigger
+   phony rules to be generated (--makedep-phony-*). */
+static int makedep_phony = REF_NONE;
+
 /* Global catchall for any errors that should affect final error status, but
    where we try to continue execution in the meantime.  */
 int retcode;
@@ -283,6 +298,37 @@ Frozen state files:\n\
 "), stdout);
       puts ("");
       fputs (_("\
+Make dependency generation:\n\
+      --makedep=FILE           write make dependency rule(s) into FILE\n\
+      --makedep-target=TARGET  specify target of generated dependency rule\n\
+                                 The --makedep and --makedep-target options\n\
+                                 must be used together, either both present,\n\
+                                 or neither present.\n\
+      --makedep-gen-missing-argfiles\n\
+      --makedep-gen-missing-include\n\
+      --makedep-gen-missing-sinclude\n\
+                               files that do not exist (on command line,\n\
+                                 via include(), or via sinclude(),\n\
+                                 respectively) are assumed to be generated\n\
+                                 files and become dependencies regardless.\n\
+      --makedep-gen-missing-all\n\
+                               equivalent to --makedep-gen-missing-argfiles\n\
+                                 --makedep-gen-missing-include\n\
+                                 --makedep-gen-missing-sinclude\n\
+      --makedep-phony-argfiles\n\
+      --makedep-phony-include\n\
+      --makedep-phony-sinclude\n\
+                               generate a \"phony\" target for each file\n\
+                                 that is specified on the command line, the\n\
+                                 subject of an include() macro, or the\n\
+                                 subject of an sinclude() macro,\n\
+                                 respectively.\n\
+      --makedep-phony-all      equivalent to --makedep-phony-argfiles\n\
+                                 --makedep-phony-include\n\
+                                 --makedep-phony-sinclude\n\
+"), stdout);
+      puts ("");
+      fputs (_("\
 Debugging:\n\
   -d, --debug[=[-|+]FLAGS], --debugmode[=[-|+]FLAGS]\n\
                                set debug level (no FLAGS implies `+adeq')\n\
@@ -333,6 +379,16 @@ enum
 {
   DEBUGFILE_OPTION = CHAR_MAX + 1,      /* no short opt */
   WARN_MACRO_SEQUENCE_OPTION,   /* no short opt */
+  MAKEDEP_OPTION,                       /* no short opt */
+  MAKEDEP_TARGET_OPTION,                /* no short opt */
+  MAKEDEP_GEN_MISSING_ARGFILES_OPTION,  /* no short opt */
+  MAKEDEP_GEN_MISSING_INCLUDE_OPTION,   /* no short opt */
+  MAKEDEP_GEN_MISSING_SINCLUDE_OPTION,  /* no short opt */
+  MAKEDEP_GEN_MISSING_ALL_OPTION,       /* no short opt */
+  MAKEDEP_PHONY_ARGFILES_OPTION,        /* no short opt */
+  MAKEDEP_PHONY_INCLUDE_OPTION,         /* no short opt */
+  MAKEDEP_PHONY_SINCLUDE_OPTION,        /* no short opt */
+  MAKEDEP_PHONY_ALL_OPTION,             /* no short opt */
HELP_OPTION, /* no short opt */
   VERSION_OPTION                /* no short opt */
@@ -365,6 +421,21 @@ static const struct option long_options[] = {
   {"warn-macro-sequence", optional_argument, NULL,
    WARN_MACRO_SEQUENCE_OPTION},
+ {"makedep", required_argument, NULL, MAKEDEP_OPTION},
+  {"makedep-target", required_argument, NULL, MAKEDEP_TARGET_OPTION},
+  {"makedep-gen-missing-argfiles", no_argument, NULL,
+     MAKEDEP_GEN_MISSING_ARGFILES_OPTION},
+  {"makedep-gen-missing-include", no_argument, NULL,
+     MAKEDEP_GEN_MISSING_INCLUDE_OPTION},
+  {"makedep-gen-missing-sinclude", no_argument, NULL,
+     MAKEDEP_GEN_MISSING_SINCLUDE_OPTION},
+  {"makedep-gen-missing-all", no_argument, NULL,
+     MAKEDEP_GEN_MISSING_ALL_OPTION},
+  {"makedep-phony-argfiles", no_argument, NULL, MAKEDEP_PHONY_ARGFILES_OPTION},
+  {"makedep-phony-include", no_argument, NULL, MAKEDEP_PHONY_INCLUDE_OPTION},
+  {"makedep-phony-sinclude", no_argument, NULL, MAKEDEP_PHONY_SINCLUDE_OPTION},
+  {"makedep-phony-all", no_argument, NULL, MAKEDEP_PHONY_ALL_OPTION},
+
   {"help", no_argument, NULL, HELP_OPTION},
   {"version", no_argument, NULL, VERSION_OPTION},
@@ -394,11 +465,15 @@ process_file (const char *name)
         {
           error (0, errno, _("cannot open %s"),
                  quotearg_style (locale_quoting_style, name));
-          /* Set the status to EXIT_FAILURE, even though we
-             continue to process files after a missing file.  */
-          retcode = EXIT_FAILURE;
+          if ((makedep_gen_missing & REF_CMD_LINE) != 0)
+            record_dependency (name, REF_CMD_LINE);
+          else
+            /* Set the status to EXIT_FAILURE, even though we
+               continue to process files after a missing file.  */
+            retcode = EXIT_FAILURE;
           return;
         }
+      record_dependency (full_name, REF_CMD_LINE);
       push_file (fp, full_name, true);
       free (full_name);
     }
@@ -641,6 +716,50 @@ main (int argc, char *const *argv, char *const *envp 
MAYBE_UNUSED)
            (which disables these warnings).  */
         macro_sequence = optarg;
         break;
+
+      case MAKEDEP_OPTION:
+        if (makedep_path != NULL)
+          usage (EXIT_FAILURE);
+        makedep_path = optarg;
+        break;
+
+      case MAKEDEP_TARGET_OPTION:
+        if (makedep_target != NULL)
+          usage (EXIT_FAILURE);
+        makedep_target = optarg;
+        break;
+
+      case MAKEDEP_GEN_MISSING_ARGFILES_OPTION:
+        makedep_gen_missing |= REF_CMD_LINE;
+        break;
+
+      case MAKEDEP_GEN_MISSING_INCLUDE_OPTION:
+        makedep_gen_missing |= REF_INCLUDE;
+        break;
+
+      case MAKEDEP_GEN_MISSING_SINCLUDE_OPTION:
+        makedep_gen_missing |= REF_SINCLUDE;
+        break;
+
+      case MAKEDEP_GEN_MISSING_ALL_OPTION:
+        makedep_gen_missing |= REF_ALL;
+        break;
+
+      case MAKEDEP_PHONY_ARGFILES_OPTION:
+        makedep_phony |= REF_CMD_LINE;
+        break;
+
+      case MAKEDEP_PHONY_INCLUDE_OPTION:
+        makedep_phony |= REF_INCLUDE;
+        break;
+
+      case MAKEDEP_PHONY_SINCLUDE_OPTION:
+        makedep_phony |= REF_SINCLUDE;
+        break;
+
+      case MAKEDEP_PHONY_ALL_OPTION:
+        makedep_phony |= REF_ALL;
+        break;
case VERSION_OPTION:
         version_etc (stdout, PACKAGE, PACKAGE_NAME, VERSION, AUTHORS, NULL);
@@ -659,6 +778,30 @@ main (int argc, char *const *argv, char *const *envp 
MAYBE_UNUSED)
     m4_error (0, errno, NULL, _("cannot set debug file %s"),
               quotearg_style (locale_quoting_style, debugfile));
+ /* Verify mutual consistency of makedep options. */
+  if ((makedep_path == NULL) && (makedep_target == NULL))
+    {
+      /* Makedep mode is NOT active. */
+      if (makedep_gen_missing != 0)
+        m4_error (0, 0, NULL,
+                  _("--makedep-gen-missing-* requires --makedep and 
--makedep-target"));
+      if (makedep_phony != 0)
+        m4_error (0, 0, NULL,
+                  _("--makedep-phony-* requires --makedep and 
--makedep-target"));
+      if ((makedep_gen_missing | makedep_phony) != 0)
+        exit (EXIT_FAILURE);
+    }
+  else if ((makedep_path != NULL) && (makedep_target != NULL))
+    {
+      /* Makedep mode is active. */
+    }
+  else
+    {
+      m4_error (0, 0, NULL,
+                _("--makedep must be used with --makedep-target"));
+      exit (EXIT_FAILURE);
+    }
+
   input_init ();
   output_init ();
   symtab_init (hash_table_size);
@@ -769,6 +912,8 @@ main (int argc, char *const *argv, char *const *envp 
MAYBE_UNUSED)
       undivert_all ();
     }
   output_exit ();
+  if (makedep_path != NULL)
+    generate_make_dependencies (makedep_path, makedep_target, makedep_phony);
 #ifndef NDEBUG
   /* Only spend time freeing memory to help isolate leaks; if
      assertions are disabled, save the time and exit now.  */
diff --git a/src/m4.h b/src/m4.h
index 79dca438..d2d5c3f6 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -94,6 +94,14 @@
 # undef bindtextdomain
 # define bindtextdomain(Domainname, Dirname)    /* empty */
 #endif
+extern int makedep_gen_missing;         /* --makedep-gen-missing-* */
+
+/* Bit masks indicating places a file is referenced from. */
+#define REF_CMD_LINE    0x01    /* File referenced from command line */
+#define REF_INCLUDE     0x02    /* File referenced from m4_include() */
+#define REF_SINCLUDE    0x04    /* File referenced from m4_sinclude() */
+#define REF_ALL         0x07    /* All of the above */
+#define REF_NONE        0x00    /* None of the above */
#define _(msgid) gettext (msgid)
 
@@ -545,6 +553,8 @@ extern void include_init (void);
 extern void include_env_init (void);
 extern void add_include_directory (const char *);
 extern FILE *m4_path_search (const char *, bool, char **);
+extern void record_dependency (const char *, int);
+extern void generate_make_dependencies (const char *, const char *, int);
 
 /* File: eval.c  --- expression evaluation.  */
diff --git a/src/path.c b/src/path.c
index 1c9993d6..93bd08d2 100644
--- a/src/path.c
+++ b/src/path.c
@@ -36,6 +36,18 @@ typedef struct includes includes;
 static includes *dir_list;      /* the list of path directories */
 static includes *dir_list_end;  /* the end of same */
 static int dir_max_length;      /* length of longest directory name */
+
+struct dependency
+{
+  struct dependency *next;      /* next in list of dependencies */
+  int ref_from;                 /* bit mask: places file referenced from */
+  char *path;                   /* pathname of this dependency */
+};
+
+typedef struct dependency dependency;
+
+static dependency *dependency_list;     /* the list of dependencies */
+static dependency *dependency_list_end; /* the end of same */
 
void
@@ -194,6 +206,74 @@ m4_path_search (const char *file, bool binary, char 
**result)
   return fp;
 }
+void
+record_dependency (const char *path, int ref_from)
+{
+  dependency *dp;
+  for (dp = dependency_list; dp != NULL; dp = dp->next)
+    if (strcmp (path, dp->path) == 0)
+      {
+        /* Remember all the places this file has been referenced from. */
+        dp->ref_from |= ref_from;
+        return;
+      }
+
+  dp = xmalloc (sizeof (dependency));
+  dp->next = NULL;
+  dp->ref_from = ref_from;
+  dp->path = xstrdup (path);
+
+  if (dependency_list_end == NULL)
+    dependency_list = dp;
+  else
+    dependency_list_end->next = dp;
+  dependency_list_end = dp;
+}
+
+void
+generate_make_dependencies (const char *path, const char *target, int phony)
+{
+  FILE *fp;
+  size_t col, len, maxcol;
+  dependency *dp;
+
+  fp = fopen (path, "w");
+  if (fp == NULL)
+    {
+      m4_error (0, errno, NULL, _("unable to open %s"),
+                quotearg_style_mem (locale_quoting_style,
+                                    path, strlen (path)));
+      return;
+    }
+
+  fputs (_("# Automatically generated by GNU m4.\n"), fp);
+
+  /* Generate the main dependency rule. */
+  maxcol = 78;
+  fprintf (fp, "%s:", target);
+  col = strlen (target) + 1;
+  for (dp = dependency_list; dp != NULL; dp = dp->next)
+    {
+      len = 1 + strlen (dp->path);
+      if (col + len + 2 > maxcol) /* +2 is for trailing space/backslash */
+        {
+          fprintf (fp," \\\n ");
+          col = 1;
+        }
+      fprintf (fp, " %s", dp->path);
+      col += len;
+    }
+  fprintf (fp, "\n");
+
+  /* Generate phony targets for user-specified subset of dependencies. */
+  if (phony != 0)
+    for (dp = dependency_list; dp != NULL; dp = dp->next)
+      if ((dp->ref_from & phony) != 0)
+        fprintf (fp, "\n%s:\n", dp->path);
+
+  fclose (fp);
+}
+
 #ifdef DEBUG_INCL
static void MAYBE_UNUSED
_______________________________________________
M4-patches mailing list
M4-patches@gnu.org
https://lists.gnu.org/mailman/listinfo/m4-patches

Reply via email to