feature: Add Makefile dependency command-line options

Similar to what gcc can do, make it possible for m4 to output Makefile
fragments that track the files that were included during processing,
in order to automatically rebuild files in the correct dependency
chains later on.

* NEWS: Document the feature.
* THANKS: Update.
* checks/get-them: Add support for declaring a test's auxfile.
* checks/check-them: Add code for handling auxilliary files, to make
testing the feature possible.
* doc/m4.texi (auxresult): New macro.
(Make dependency generation): New chapter.
* src/m4.h (makedep_gen_missing, REF_CMD_LINE, REF_INCLUDE)
(REF_SINCLUDE, REF_ALL, REF_NONE): Prepare for new options.
(record_dependency, generate_make_dependencies): New prototypes.
* src/m4.c (makedep_path, makedep_target, makedep_gen_missing)
(makedep_phony): Track new options.
(usage): Document new options.
(process_file): Track dependencies.
(main): Parse new options.
* src/builtin.c (include, m4_include, m4_sinclude): Track include
source.
* src/path.c (struct dependency): New struct.
(dependency_list, dependency_list_end): New variables.
(record_dependency, generate_make_dependencies): Output dependencies.
Co-developed-by: Lorenzo Di Gregorio <lorenzo.digregorio@gmail.com>
diff --git a/NEWS b/NEWS
index 960e42f..69aad93 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,14 @@
 * Noteworthy changes in Version 1.6 (????-??-??) [stable]
   Released by ????, based on git versions 1.4.10b.x-* and 1.5.*
 
+** Add new capability to automatically generate make dependency rules.
+   This is enabled by the new command-line option pair '--makedep=FILE'
+   and '--makedep-target=TARGET' and further tuned by
+   '--makedep-gen-missing-argfiles', '--makedep-gen-missing-include',
+   '--makedep-gen-missing-sinclude', '--makedep-gen-missing-all',
+   '--makedep-phony-argfiles', '--makedep-phony-include',
+   '--makedep-phony-sinclude', and '--makedep-phony-all'.
+
 ** Fix regressions introduced in 1.4.10b:
 *** Using `builtin' or `indir' to perform nested `shift' calls triggered
     an assertion failure (not present in 1.4.11).
diff --git a/THANKS b/THANKS
index d441f56..2e4de50 100644
--- a/THANKS
+++ b/THANKS
@@ -39,6 +39,7 @@
 David Arnstein               arnstein@pobox.com
 David J. MacKenzie           djm@uunet.uu.net
 David Perlin                 davep@nanosoft.com
+David Warme                  a136dmw@warme.net
 Elbert Pol                   elbert.pol@gmail.com
 Elias Benali                 elptr@users.sourceforge.net
 Erez Zadok                   ezk@cs.columbia.edu
@@ -81,6 +82,7 @@
 Kristine Lund                lund@lpnaxp.in2p3.fr
 Krste Asanovic               krste@icsi.berkeley.edu
 Lawson Chan                  Lawson.Chan@tdsecurities.com
+Lorenzo Di Gregorio          lorenzo.digregorio@gmail.com
 Marijn Schouten              unlisted
 Marion Hakanson              hakanson@cse.ogi.edu
 Mark Seiden                  mis@seiden.com
diff --git a/checks/check-them b/checks/check-them
index 24e6802..df7a305 100755
--- a/checks/check-them
+++ b/checks/check-them
@@ -37,6 +37,7 @@
 err=$tmp/m4-err
 xout=$tmp/m4-xout
 xerr=$tmp/m4-xerr
+xaux=$tmp/m4-xaux
 failed=
 skipped=
 strip_needed=false
@@ -97,14 +98,15 @@
     *stackovf.test)
       "$file" "$m4"
       case $? in
-	77) skipped="$skipped $file";;
-	0) ;;
-	*) failed="$failed $file"
+        77) skipped="$skipped $file" ;;
+        0) ;;
+        *) failed="$failed $file" ;;
       esac
       continue ;;
   esac
 
-  options=`${SED} -ne '3s/^dnl @ extra options: //p;3q' "$file"`
+  options=`${SED} -ne "3 { s|TMP/|$tmp/|g; s/^dnl @ extra options: //p; q }" \
+     "$file"`
   ${SED} -e '/^dnl @/d' -e '/^\^D$/q' "$file" \
     | LC_MESSAGES=C LANG=C M4PATH=$examples "$m4" -d $options - >$out 2>$err
   stat=$?
@@ -149,6 +151,8 @@
         "$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 +183,32 @@
     diff $diffopts $xerr $err
   fi
 
+  if test -n "$auxfile"; then
+    tauxfile=`echo "$auxfile" | ${SED} -e "s|TMP/|$tmp/|g"`
+    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 5848db5..0d62065 100755
--- a/checks/get-them
+++ b/checks/get-them
@@ -36,6 +36,7 @@
   options = "";
   xout = "";
   xerr = "";
+  auxfile = "";
 }
 
 /^@node / {
@@ -83,6 +84,11 @@
   gsub ("@comment xerr: ", "", xerr);
 }
 
+/^@comment auxfile: / {
+  auxfile = $0;
+  gsub ("@comment auxfile: ", "", auxfile);
+}
+
 /^@example$/, /^@end example$/ {
   if (seq < 0)
     next;
@@ -105,10 +111,13 @@
         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 @@
   }
   if ($0 ~ /^\^D$/)
     next;
-  if ($0 ~ /^\$ @kbd\{/)
+  if ($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 2bd2d04..9729d2a 100644
--- a/doc/m4.texi
+++ b/doc/m4.texi
@@ -36,6 +36,14 @@
 @r{[}@var{\varname\} = @samp{\default\}@r{]}
 @end macro
 
+@c @auxresult{}
+@c ------------
+@c Used in examples to indicate output produced within the
+@c ``auxiliary output file.''
+@macro auxresult
+@b{@result{}}
+@end macro
+
 @comment %**end of header
 @comment ========================================================
 
@@ -146,6 +154,7 @@
 
 * Operation modes::             Command line options for operation modes
 * Preprocessor features::       Command line options for preprocessor features
+* Make dependency generation::  Generating make dependency rules
 * Limits control::              Command line options for limits control
 * Frozen state::                Command line options for frozen state
 * Debugging options::           Command line options for debugging
@@ -580,6 +589,7 @@
 @menu
 * Operation modes::             Command line options for operation modes
 * Preprocessor features::       Command line options for preprocessor features
+* Make dependency generation::  Generating make dependency rules
 * Limits control::              Command line options for limits control
 * Frozen state::                Command line options for frozen state
 * Debugging options::           Command line options for debugging
@@ -796,6 +806,195 @@
 file names.
 @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 @option{--makedep=}@var{file} and
+@option{--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 @option{-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 @option{-MT} option of @code{gcc} (except that @code{gcc} allows
+@option{-MT} to be specified multiple times).
+@end table
+
+Note that the @option{--makedep=}@var{file} and
+@option{--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
+$ @kbd{cat TMP/depfile}
+@auxresult{}# Automatically generated by GNU m4.
+@auxresult{}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
+$ @kbd{cat TMP/depfile}
+@auxresult{}# Automatically generated by GNU m4.
+@auxresult{}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
+@option{-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 @option{-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 @option{-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 @option{-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
+@option{--makedep-gen-missing-argfiles},
+@option{--makedep-gen-missing-include} and
+@option{--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
+$ @kbd{cat TMP/depfile}
+@auxresult{}# Automatically generated by GNU m4.
+@auxresult{}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 @option{-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:
+@option{--makedep-phony-argfiles}
+@option{--makedep-phony-include}
+@option{--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
+$ @kbd{cat TMP/depfile}
+@auxresult{}# Automatically generated by GNU m4.
+@auxresult{}Target: ./../examples/foo ./../examples/incl.m4
+@auxresult{}
+@auxresult{}./../examples/foo:
+@auxresult{}
+@auxresult{}./../examples/incl.m4:
+@end example
+
 @node Limits control
 @section Command line options for limits control
 
diff --git a/src/builtin.c b/src/builtin.c
index bf0d179..e880261 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -1494,13 +1494,14 @@
    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 @@
   fp = m4_path_search (arg, false, &name);
   if (fp == NULL)
     {
+      fail = !silent;
+      if (makedep_gen_missing & ref_from)
+        {
+          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 @@
 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 @@
 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/m4.c b/src/m4.c
index 89a2ec2..3b350b3 100644
--- a/src/m4.c
+++ b/src/m4.c
@@ -65,6 +65,21 @@
 /* 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 @@
 "), 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\
@@ -332,6 +378,16 @@
 enum
 {
   DEBUGFILE_OPTION = CHAR_MAX + 1,      /* 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 */
   WARN_MACRO_SEQUENCE_OPTION,   /* no short opt */
 
   HELP_OPTION,                  /* no short opt */
@@ -365,6 +421,23 @@
   {"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 +467,15 @@
         {
           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)
+            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);
     }
@@ -642,6 +719,50 @@
         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);
         exit (EXIT_SUCCESS);
@@ -659,6 +780,32 @@
     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 && !makedep_target)
+    {
+      /* Makedep mode is NOT active. */
+      if (makedep_gen_missing)
+        m4_error (0, 0, NULL,
+                  _("--makedep-gen-missing-* requires --makedep and "
+                    "--makedep-target"));
+      if (makedep_phony)
+        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 && makedep_target)
+    {
+      /* 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 +916,8 @@
       undivert_all ();
     }
   output_exit ();
+  if (makedep_path)
+    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 79dca43..5eb0296 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_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 1c9993d..d43ef59 100644
--- a/src/path.c
+++ b/src/path.c
@@ -36,6 +36,18 @@
 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,78 @@
   return fp;
 }
 
+/* Track that PATH was opened from the given REF_FROM.  */
+void
+record_dependency (const char *path, int ref_from)
+{
+  dependency *dp;
+  for (dp = dependency_list; dp != NULL; dp = dp->next)
+    if (STREQ (path, dp->path))
+      {
+        /* 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;
+}
+
+/* Output the Makefile fragment at PATH tracking all dependencies seen,
+   using TARGET as the rule to regenerate this run.  PHONY controls
+   whether additional '.PHONY' rules are also output.  */
+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 for trailing space/backslash */
+        {
+          fputs (" \\\n ", fp);
+          col = 1;
+        }
+      fprintf (fp, " %s", dp->path);
+      col += len;
+    }
+  fputc ('\n', fp);
+
+  /* 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