Re: A 'makedep' feature for m4.
"David M. Warme" <[email protected]> Tue, 20 May 2025 14:31:04 -0400
| Newsgroups | gmane.comp.gnu.m4.patches |
|---|---|
| Message-ID | <[email protected]> |
This is a multi-part message in MIME format. --===============3538069760711764656== Content-Type: multipart/alternative; boundary="------------CPX3LGelT46DInmb58R6Q6HY" Content-Language: en-US This is a multi-part message in MIME format. --------------CPX3LGelT46DInmb58R6Q6HY Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Eric, I have incorporated most of the changes that you have identified. This patch is from very recent branch-1.4. David ChangeLog-2014 | 16 +++++ NEWS | 2 + THANKS | 2 + checks/check-them | 36 +++++++++- checks/get-them | 13 +++- doc/m4.texi | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/builtin.c | 19 ++++-- src/m4.c | 151 +++++++++++++++++++++++++++++++++++++++++- src/m4.h | 15 ++++- src/path.c | 78 ++++++++++++++++++++++ 10 files changed, 511 insertions(+), 15 deletions(-) diff --git a/ChangeLog-2014 b/ChangeLog-2014 index 0783ad62..a16111c5 100644 --- a/ChangeLog-2014 +++ b/ChangeLog-2014 @@ -1,3 +1,19 @@ +2025-05-20 David Warme<[email protected]> + Lorenzo Di Gregorio<[email protected]> + + Add ability to generate make dependency rules. + * checks/check-them: Added auxfile, auxresult and TMP/. + * checks/get-them: Added auxfile, auxresult. + * doc/m4.texi: Document new options and examples. + * src/builtin.c: Record dependencies for include() / sinclude(). + * src/m4.c: Add new options. Record dependencies for files + specified on the command line. Generate dependencies at end. + * src/m4.h: Add defines for tracking how files are referenced, and + new functions for recording / generation of dependencies. + * src/path.c: Added recording / generation of dependencies. + * NEWS: Added a blurb for this new feature. + * THANKS: Added us to the list. + 2014-05-13 Eric Blake<[email protected]> doc: fix line-wrapped macro definitions diff --git a/NEWS b/NEWS index 00a81b7f..2b5b6d6a 100644 --- a/NEWS +++ b/NEWS @@ -39,6 +39,8 @@ GNU M4 NEWS - User visible changes. * 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 016dd6e2..f73c59ea 100644 --- a/THANKS +++ b/THANKS @@ -37,6 +37,7 @@ David Apps unlisted David [email protected] David J. [email protected] David [email protected] +David [email protected] Elbert [email protected] Elias [email protected] Erez [email protected] @@ -79,6 +80,7 @@ Konrad [email protected] Kristine [email protected] Krste [email protected] Lawson [email protected] +Lorenzo Di [email protected] Marijn Schouten unlisted Marion [email protected] Mark [email protected] diff --git a/checks/check-them b/checks/check-them index c0a8d350..2c400e53 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 @@ -101,7 +102,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 M4PATH=$examples "$m4" -d $options - >$out 2>$err stat=$? @@ -123,8 +125,8 @@ 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/|" \ - "$file" > $xout + ${SED} -ne 's/^dnl @result{}//p' "$file" \ + | ${SED} "s|examples/|$examples/|" > $xout else cp "$examples/$xoutfile" $xout fi @@ -142,6 +144,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 @@ -172,6 +176,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 b80f70fb..b901cf15 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 7cbad39e..99952f14 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 ======================================================== @@ -161,6 +169,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 depencency rules * Debugging options:: Command line options for debugging * Command line files:: Specifying input files on the command line @@ -589,6 +598,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 @@ -864,6 +874,190 @@ 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 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: 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 2819ba49..0f237c5b 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -1388,10 +1388,11 @@ m4_changeword (struct obstack *obs MAYBE_UNUSED, int argc, token_data **argv) `---------------------------------------------------------------*/ static void -include (int argc, token_data **argv, bool silent) +include (int argc, token_data **argv, bool silent, int ref_from) { FILE *fp; char *name; + int fail; if (bad_argc (argv[0], argc, 2, 2)) return; @@ -1399,14 +1400,20 @@ include (int argc, token_data **argv, bool silent) fp = m4_path_search (ARG (1), false, &name); if (fp == NULL) { - if (!silent) + fail = !silent; + if ((makedep_gen_missing & ref_from) != 0) { - M4ERROR ((warning_status, errno, _("cannot open `%s'"), ARG (1))); - retcode = EXIT_FAILURE; + record_dependency (ARG (1), ref_from); + fail = 0; } + if (!silent) + M4ERROR ((warning_status, errno, _("cannot open `%s'"), ARG (1))); + if (fail) + retcode = EXIT_FAILURE; return; } + record_dependency (name, ref_from); push_file (fp, name, true); free (name); } @@ -1418,7 +1425,7 @@ include (int argc, token_data **argv, bool silent) static void m4_include (struct obstack *obs MAYBE_UNUSED, int argc, token_data **argv) { - include (argc, argv, false); + include (argc, argv, false, REF_INCLUDE); } /*----------------------------------. @@ -1428,7 +1435,7 @@ m4_include (struct obstack *obs MAYBE_UNUSED, int argc, token_data **argv) static void m4_sinclude (struct obstack *obs MAYBE_UNUSED, int argc, token_data **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 8f9f6d2e..227d8a20 100644 --- a/src/m4.c +++ b/src/m4.c @@ -77,6 +77,21 @@ int nesting_limit = 1024; const char *user_word_regexp = ""; #endif +/* 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; @@ -267,6 +282,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] set debug level (no FLAGS implies `aeq')\n\ --debugfile[=FILE] redirect debug and trace output to FILE\n\ @@ -317,6 +363,16 @@ enum DEBUGFILE_OPTION = CHAR_MAX + 1, /* no short opt */ DIVERSIONS_OPTION, /* not quite -N, because of message */ 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 */ @@ -354,6 +410,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}, @@ -380,11 +451,15 @@ process_file (const char *name) if (fp == NULL) { error (0, errno, _("cannot open `%s'"), 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); } @@ -615,6 +690,50 @@ main (int argc, char *const *argv) (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); @@ -632,6 +751,30 @@ main (int argc, char *const *argv) if (debugfile && !debug_set_output (debugfile)) M4ERROR ((warning_status, errno, _("cannot set debug file `%s'"), debugfile)); + + /* Verify mutual consistency of makedep options. */ + if ((makedep_path == NULL) && (makedep_target == NULL)) + { + /* Makedep mode is NOT active. */ + if (makedep_gen_missing != 0) + M4ERROR ((EXIT_FAILURE, 0, + _("--makedep-gen-missing-* requires --makedep and --makedep-target"))); + if (makedep_phony != 0) + M4ERROR ((EXIT_FAILURE, 0, + _("--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 + { + M4ERROR ((EXIT_FAILURE, 0, + _("--makedep must be used with --makedep-target"))); + exit (EXIT_FAILURE); + } input_init (); output_init (); @@ -738,6 +881,8 @@ main (int argc, char *const *argv) undivert_all (); } output_exit (); + if (makedep_path != NULL) + generate_make_dependencies (makedep_path, makedep_target, makedep_phony); free_macro_sequence (); exit (retcode); } diff --git a/src/m4.h b/src/m4.h index 8d0d96dd..f8735abc 100644 --- a/src/m4.h +++ b/src/m4.h @@ -135,6 +135,14 @@ extern int nesting_limit; /* -L */ #ifdef ENABLE_CHANGEWORD extern const char *user_word_regexp; /* -W */ #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 */ /* Error handling. */ extern int retcode; @@ -412,7 +420,10 @@ extern void hack_all_symbols (hack_symbol *, void *); extern int expansion_level; extern void expand_input (void); -extern void call_macro (symbol *, int, token_data **, struct obstack *); +extern void call_macro (symbol *, int, token_data **, struct obstack *) + /* This doesn't seem right, but gcc-8.5.0 is suggesting that this */ + /* function might be a candidate for attribute 'cold', stopping the build. */ + ATTRIBUTE_COLD; /* File: builtin.c --- builtins. */ @@ -470,6 +481,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 af5e7cf0..f34ef8a3 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 @@ -189,6 +201,72 @@ 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) + { + M4ERROR ((0, errno, "unable to open `%s'.\n", 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 --------------CPX3LGelT46DInmb58R6Q6HY Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable <!DOCTYPE html> <html> <head> <meta http-equiv=3D"content-type" content=3D"text/html; charset=3DUTF= -8"> </head> <body> <pre>Eric, I have incorporated most of the changes that you have identified. This patch is from very recent branch-1.4. David ChangeLog-2014 | 16 +++++ NEWS | 2 + THANKS | 2 + checks/check-them | 36 +++++++++- checks/get-them | 13 +++- doc/m4.texi | 194 ++++++++++++++++++++++++++++++++++++++++++++++++= ++++++ src/builtin.c | 19 ++++-- src/m4.c | 151 +++++++++++++++++++++++++++++++++++++++++- src/m4.h | 15 ++++- src/path.c | 78 ++++++++++++++++++++++ 10 files changed, 511 insertions(+), 15 deletions(-) diff --git a/ChangeLog-2014 b/ChangeLog-2014 index 0783ad62..a16111c5 100644 --- a/ChangeLog-2014 +++ b/ChangeLog-2014 @@ -1,3 +1,19 @@ +2025-05-20 David Warme <a class=3D"moz-txt-link-rfc2396E" href=3D"mail= to:[email protected]"><[email protected]></a> + Lorenzo Di Gregorio <a class=3D"moz-txt-link-rfc2396E" href=3D"mai= lto:[email protected]"><[email protected]></a= > + + Add ability to generate make dependency rules. + * checks/check-them: Added auxfile, auxresult and TMP/. + * checks/get-them: Added auxfile, auxresult. + * doc/m4.texi: Document new options and examples. + * src/builtin.c: Record dependencies for include() / sinclude(). + * src/m4.c: Add new options. Record dependencies for files + specified on the command line. Generate dependencies at end. + * src/m4.h: Add defines for tracking how files are referenced, and + new functions for recording / generation of dependencies. + * src/path.c: Added recording / generation of dependencies. + * NEWS: Added a blurb for this new feature. + * THANKS: Added us to the list. + 2014-05-13 Eric Blake <a class=3D"moz-txt-link-rfc2396E" href=3D"mailt= o:[email protected]"><[email protected]></a> =20 doc: fix line-wrapped macro definitions diff --git a/NEWS b/NEWS index 00a81b7f..2b5b6d6a 100644 --- a/NEWS +++ b/NEWS @@ -39,6 +39,8 @@ GNU M4 NEWS - User visible changes. =20 * Noteworthy changes in release 1.4.19 (2021-05-28) [stable] =20 +** 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 016dd6e2..f73c59ea 100644 --- a/THANKS +++ b/THANKS @@ -37,6 +37,7 @@ David Apps unlisted David Arnstein <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> David J. MacKenzie <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> David Perlin <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> +David Warme <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> Elbert Pol <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> Elias Benali <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> Erez Zadok <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> @@ -79,6 +80,7 @@ Konrad Schwarz <a class=3D"moz-txt-link-abbrev= iated" href=3D"mailto:[email protected]">konrad.schwarz@siemens.= com</a> Kristine Lund <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> Krste Asanovic <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> Lawson Chan <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> +Lorenzo Di Gregorio <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> Marijn Schouten unlisted Marion Hakanson <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> Mark Seiden <a class=3D"moz-txt-link-abbreviated" href=3D"ma= ilto:[email protected]">[email protected]</a> diff --git a/checks/check-them b/checks/check-them index c0a8d350..2c400e53 100755 --- a/checks/check-them +++ b/checks/check-them @@ -37,6 +37,7 @@ out=3D$tmp/m4-out err=3D$tmp/m4-err xout=3D$tmp/m4-xout xerr=3D$tmp/m4-xerr +xaux=3D$tmp/m4-xaux failed=3D skipped=3D strip_needed=3Dfalse @@ -101,7 +102,8 @@ do continue ;; esac =20 - options=3D`${SED} -ne '3s/^dnl @ extra options: //p;3q' "$file"` + options=3D`${SED} -ne '3s/^dnl @ extra options: //p;3q' "$file" \ + | ${SED} -e "s/TMP\//$tmp\//"` ${SED} -e '/^dnl @/d' -e '/^\^D$/q' "$file" \ | LC_MESSAGES=3DC M4PATH=3D$examples "$m4" -d $options - >$out 2&= gt;$err stat=3D$? @@ -123,8 +125,8 @@ do =20 xoutfile=3D`${SED} -n 's/^dnl @ expected output: //p' "$file"` if test -z "$xoutfile" ; then - ${SED} -e '/^dnl @result{}/!d' -e 's///' -e "s|examples/|$examples/|= " \ - "$file" > $xout + ${SED} -ne 's/^dnl @result{}//p' "$file" \ + | ${SED} "s|examples/|$examples/|" > $xout else cp "$examples/$xoutfile" $xout fi @@ -142,6 +144,8 @@ do "$examples/$xerrfile" > $xerr ;; esac =20 + auxfile=3D`${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 @@ -172,6 +176,32 @@ do diff $diffopts $xerr $err fi =20 + if test -n "$auxfile"; then + tauxfile=3D`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=3D"$failed $<a class=3D"moz-txt-link-freetext" href=3D"fi= le:aux">file:aux</a>" + echo `${SED} -e 's/^dnl //' -e 1q $file` + echo "$file auxfile mismatch" + diff $diffopts $xaux $tauxfile + fi + rm -f $tauxfile + else + failed=3D"$failed $<a class=3D"moz-txt-link-freetext" href=3D"file= :aux">file:aux</a>" + echo `${SED} -e 's/^dnl //' -e 1q $file` + echo "$file aux outfile $auxfile does not exist" + fi + fi + done =20 rm -f $out $err $xout $xerr diff --git a/checks/get-them b/checks/get-them index b80f70fb..b901cf15 100755 --- a/checks/get-them +++ b/checks/get-them @@ -36,6 +36,7 @@ BEGIN { options =3D ""; xout =3D ""; xerr =3D ""; + auxfile =3D ""; } =20 /^@node / { @@ -83,6 +84,11 @@ BEGIN { gsub ("@comment xerr: ", "", xerr); } =20 +/^@comment auxfile: / { + auxfile =3D $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 =3D 0; options =3D ""; xout =3D ""; xerr =3D ""; + auxfile =3D ""; 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 =3D "dnl "; else prefix =3D "<a class=3D"moz-txt-link-rfc2396E" href=3D"mailto:;diff-= -gita/doc/m4.texib/doc/m4.texiindex7cbad39e..99952f14100644---a/doc/m4.te= xi+++b/doc/m4.texi@@-36,6+36,14@@@r{[}@var{\varname\}=3D@samp{\default\}@= r{]}@endmacro+@c@auxresult{auxfile}+@c------------+@cUsedinexamplestoindi= cateoutputproducedwithinthe+@c``auxilliaryoutputfile.''+@macroauxresult{a= uxfile}+\auxfile\@b{@result{}}+@endmacro+@comment%**endofheader@comment=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D@@-161,6+169,7@@*Preprocessorfeatures::Commandlineoptionsf= orpreprocessorfeatures*Limitscontrol::Commandlineoptionsforlimitscontrol*= Frozenstate::Commandlineoptionsforfrozenstate+*Makedependencygeneration::= Generatingmakedepencencyrules*Debuggingoptions::Commandlineoptionsfordebu= gging*Commandlinefiles::Specifyinginputfilesonthecommandline@@-589,6+598,= 7@@*Preprocessorfeatures::Commandlineoptionsforpreprocessorfeatures*Limit= scontrol::Commandlineoptionsforlimitscontrol*Frozenstate::Commandlineopti= onsforfrozenstate+*Makedependencygeneration::Generatingmakedependencyrule= s*Debuggingoptions::Commandlineoptionsfordebugging*Commandlinefiles::Spec= ifyinginputfilesonthecommandline@endmenu@@-864,6+874,190@@filesareread.@e= ndtable+@nodeMakedependencygeneration+@sectionCommandlineoptionsforgenera= tingMakefiledependencyrules++Makefiledependencyrulescanbeautomaticallygen= eratedbyspecifying+boththe@code{--makedep=3D}@var{file}and+@code{--makede= p-target=3D}@var{target}options.++@table@code+@item--makedep=3D@var{file}= +Causes@code{m4}togenerateadependencyruleintothespecified+@var{file}.Macr= oexpansionoutputisstillwrittentostdoutas+normal.Thisoptionisanalogoustoth= e@code{-MF}optionof+@code{gcc}.++@item--makedep-target=3D@var{target}+Spe= cifies@var{target}tobethetargetofthegenerateddependency+rule.Thestring@va= r{target}isusedverbatim,andcancontainseveral+logicaltargetsseparatedbyspa= ces.Itistheuser'sresponsibilityto+properlyexpresscharactersthat@code{make= }handlesspecially(suchas+'@code{$}',orspaceswithinfilenames).Since@code{m= 4}sendsits+macroexpansionoutputtostdout,itneverreallyknowsthenameofthe+ta= rgetfilebeinggenerated,sothetargetmustalwaysbespecified+explicitlybytheus= erwiththisoption.Thisoptionisanalogousto+the@code{-MT}optionof@code{gcc}(= exceptthat@code{gcc}allows+@code{-MT}tobespecifiedmultipletimes).+@endtab= le++Notethatthe@code{--makedep=3D}@var{file}and+@code{makedep-target=3D}@= var{target}optionsmusteither(a)bothbe+specified,or(b)neitherbespecified.T= heycannotbeused+independentlyofeachother.++@commentoptions:--makedep=3DTM= P/depfile--makedep-target=3DTargetfoo+@commentauxfile:TMP/depfile+@exampl= e+$@kbd{m4-Iexamples--makedep=3DTMP/depfile--makedep-target=3DTargetfoo}+= @result{}bar+@auxresult{TMP/depfile}#AutomaticallygeneratedbyGNUm4.+@auxr= esult{TMP/depfile}Target:./../examples/foo+@endexample++@commentSeparatet= hesetwoexamples.+@sp1++@commentoptions:--makedep=3DTMP/depfile--makedep-t= arget=3DTargetfoo+@commentauxfile:TMP/depfile+@example+$@kbd{m4-Iexamples= --makedep=3DTMP/depfile--makedep-target=3DTargetfoo-}+@result{}bar+includ= e(`incl.m4')+@result{}Includefilestart+@result{}foo+@result{}Includefilee= nd+@result{}+^D+@auxresult{TMP/depfile}#AutomaticallygeneratedbyGNUm4.+@a= uxresult{TMP/depfile}Target:./../examples/foo./../examples/incl.m4+@endex= ample++Thefollowingadditionaloptionscanalsobeusedwhendependencyrules+areb= einggenerated(theseoptionsareonlyvalidwhenboth+@code{makedep=3D}@var{file= }and@code{makedep-target=3D}@var{target}have+alsobeenspecified):++@table@= code+@item--makedep-gen-missing-argfiles+Causes@code{m4}toassumethatanyfi= lelistedonthecommandlinethat+ismissing(i.e.,doesnotexist)isanautomaticall= ygeneratedfile.+@code{M4}includessuchmissingfilesasdependenciesinthegener= ated+ruleregardless.Inthiscasethedependencyappearsexactlyas+specifiedonth= ecommandlineandisnotmodifiedbyany+@code{-I}@var{searchdir}prefixes.Noteth= atthemacroexpansionoutput+generatedtostdoutwillbeincorrectwhenthishappens= becausethe+missingfileisassumedtobeanemptyfile.Awarningisproducedon+stder= rforeachmissingcommandlinefilehandledinthismanner.++@item--makedep-gen-mi= ssing-include+Causes@code{m4}toassumethatanyfileincludedviathe+@code{incl= ude()}macrothatismissing(i.e.,doesnotexist)isan+automaticallygeneratedfil= e.@code{M4}includessuchmissingfilesas+dependenciesinthegeneratedruleregar= dless.Inthiscasethe+dependencyappearsexactlyasspecifiedintheargumentto+@c= ode{include()}andisnotmodifiedbyany@code{-I}@var{searchdir}+prefixes.Note= thatthemacroexpansionoutputgeneratedtostdoutwill+beincorrectwhenthishappe= nsbecausethemissingfileisassumedtobe+anemptyfile.Thisoptioncausesthe@code= {m4}@code{include()}macro+tobehavelike@code{sinclude()},exceptthatawarnin= gmessageis+producedonstderrtoindicatethattherequestedfilewasmissing.+This= optionisanalogoustothe@code{-MG}optionof@code{gcc}.++@item--makedep-gen-m= issing-sinclude+Causes@code{m4}toassumethatanyfileincludedviathe+@code{si= nclude()}macrothatismissing(i.e.,doesnotexist)isan+automaticallygenerated= file.@code{M4}includessuchmissingfilesas+dependenciesinthegeneratedrulere= gardless.Inthiscasethe+dependencyappearsexactlyasspecifiedintheargumentto= +@code{sinclude()}andisnotmodifiedbyany@code{-I}@var{searchdir}+prefixes.= Notethatthemacroexpansionoutputgeneratedtostdoutwill+beincorrectwhenthish= appensbecausethemissingfileisassumedtobe+anemptyfile.Thisoptiondoesnotalt= er@code{sinclude()}'sbehavior+ofsilentlyignoringrequeststo@code{sinclude(= )}filesthatdonotexist.++@item--makedep-gen-missing-all+Thisoptionisequiva= lenttospecifyingallthreeoptions+@code{--makedep-gen-missing-argfiles},+@c= ode{--makedep-gen-missing-include}and+@code{--makedep-gen-missing-sinclud= e}.+@endtable++Notethattheabove@code{makedep-gen-missing-*}optionsassumet= hatthe+missingfileswillultimatelynot@code{include()}or@code{sinclude()}+a= nyadditionalfiles--iftheydo,thentheseadditionalfileswillbe+missingfromthe= generateddependencyrules.+++@commentoptions:--makedep=3DTMP/depfile--make= dep-target=3DTarget--makedep-gen-missing-allfoonone+@commentauxfile:TMP/d= epfile+@example+$@kbd{m4-Iexamples--makedep=3DTMP/depfile--makedep-target= =3DTarget\}+@kbd{--makedep-gen-missing-allfoonone-}+@result{}bar+@error{}= m4:cannotopen`none':Nosuchfileordirectory+include(`incl.m4')+@result{}Inc= ludefilestart+@result{}foo+@result{}Includefileend+@result{}+include(`non= e2')+@result{}+@error{}m4:stdin:2:cannotopen`none2':Nosuchfileordirectory= +sinclude(`none3')+@result{}+^D+@auxresult{TMP/depfile}#Automaticallygene= ratedbyGNUm4.+@auxresult{TMP/depfile}Target:./../examples/foonone./../exa= mples/incl.m4none2none3+@endexample++Thefollowingoptionscontrolthegenerat= ionof``phony''targetsfor+certainclassesofdependencies.Thesedummyrulesareu= sedtowork+arounderrors@code{make}givesifyouremovefileswithoutupdatingthe+= @code{Makefile}tomatch.Dependenciesthatmatchoneormoreofthese+classescause= asingledummyruletobegeneratedforthem:++@table@code+@item--makedep-phony-a= rgfiles+Causes@code{m4}togeneratea``phony''targetforeachfilethatis+specif= iedonthecommandline.++@item--makedep-phony-include+Causes@code{m4}togener= atea``phony''targetforeachfilethatis+thesubjectofan@code{include()}macro.= Thisoptionisanalogousto+the@code{-MP}optionof@code{gcc}.++@item--makedep-= phony-sinclude+Causes@code{m4}togeneratea``phony''targetforeachfilethatis= +thesubjectofan@code{sinclude()}macro.++@item--makedep-phony-all+isequiva= lenttospecifyingallthreeoptions:+@code{--makedep-phony-argfiles}+@code{--= makedep-phony-include}+@code{--makedep-phony-sinclude}.+@endtable++@comme= ntoptions:--makedep=3DTMP/depfile--makedep-target=3DTarget--makedep-phony= -allfoo+@commentauxfile:TMP/depfile+@example+$@kbd{m4-Iexamples--makdep=3D= TMP/depfile--makedep-target=3DTarget\}+@kbd{--makedep-phony-allfoo-}+@res= ult{}bar+include(`incl.m4')+@result{}Includefilestart+@result{}foo+@resul= t{}Includefileend+@result{}+^D+@auxresult{TMP/depfile}#Automaticallygener= atedbyGNUm4.+@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:+@= endexample+@nodeDebuggingoptions@sectionCommandlineoptionsfordebuggingdif= f--gita/src/builtin.cb/src/builtin.cindex2819ba49..0f237c5b100644---a/src= /builtin.c+++b/src/builtin.c@@-1388,10+1388,11@@m4_changeword(structobsta= ck*obsMAYBE_UNUSED,intargc,token_data**argv)`----------------------------= -----------------------------------*/staticvoid-include(intargc,token_dat= a**argv,boolsilent)+include(intargc,token_data**argv,boolsilent,intref_fr= om){FILE*fp;char*name;+intfail;if(bad_argc(argv[0],argc,2,2))return;@@-13= 99,14+1400,20@@include(intargc,token_data**argv,boolsilent)fp=3Dm4_path_s= earch(ARG(1),false,&name);if(fp=3D=3DNULL){-if(!silent)+fail=3D!silen= t;+if((makedep_gen_missing&ref_from)!=3D0){-M4ERROR((warning_status,e= rrno,_(">"; diff --git a/doc/m4.texi b/doc/m4.texi index 7cbad39e..99952f14 100644 --- a/doc/m4.texi +++ b/doc/m4.texi @@ -36,6 +36,14 @@ @r{[}@var{\varname\} =3D @samp{\default\}@r{]} @end macro =20 +@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 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 @@ -161,6 +169,7 @@ * Preprocessor features:: Command line options for preprocessor fe= atures * Limits control:: Command line options for limits control * Frozen state:: Command line options for frozen state +* Make dependency generation:: Generating make depencency rules * Debugging options:: Command line options for debugging * Command line files:: Specifying input files on the command li= ne =20 @@ -589,6 +598,7 @@ * Preprocessor features:: Command line options for preprocessor fe= atures * 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 li= ne @end menu @@ -864,6 +874,190 @@ files are read. @end table =20 +@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=3D}@var{file} and +@code{--makedep-target=3D}@var{target} options. + +@table @code +@item --makedep=3D@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=3D@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=3D}@var{file} and +@code{makedep-target=3D}@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=3DTMP/depfile --makedep-target=3DTarget foo +@comment auxfile: TMP/depfile +@example +$ @kbd{m4 -I examples --makedep=3DTMP/depfile --makedep-target=3DTarget = 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=3DTMP/depfile --makedep-target=3DTarget foo +@comment auxfile: TMP/depfile +@example +$ @kbd{m4 -I examples --makedep=3DTMP/depfile --makedep-target=3DTarget = 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=3D}@var{file} and @code{makedep-target=3D}@var{target} hav= e +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 exi= st. + +@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 options: --makedep=3DTMP/depfile --makedep-target=3DTarget --ma= kedep-gen-missing-all foo none +@comment auxfile: TMP/depfile +@example +$ @kbd{m4 -I examples --makedep=3DTMP/depfile --makedep-target=3DTarget = \} +@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: 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= =2Em4 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=3DTMP/depfile --makedep-target=3DTarget --ma= kedep-phony-all foo +@comment auxfile: TMP/depfile +@example +$ @kbd{m4 -I examples --makdep=3DTMP/depfile --makedep-target=3DTarget \= } +@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 =20 diff --git a/src/builtin.c b/src/builtin.c index 2819ba49..0f237c5b 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -1388,10 +1388,11 @@ m4_changeword (struct obstack *obs MAYBE_UNUSED, = int argc, token_data **argv) `---------------------------------------------------------------*/ =20 static void -include (int argc, token_data **argv, bool silent) +include (int argc, token_data **argv, bool silent, int ref_from) { FILE *fp; char *name; + int fail; =20 if (bad_argc (argv[0], argc, 2, 2)) return; @@ -1399,14 +1400,20 @@ include (int argc, token_data **argv, bool silent= ) fp =3D m4_path_search (ARG (1), false, &name); if (fp =3D=3D NULL) { - if (!silent) + fail =3D !silent; + if ((makedep_gen_missing & ref_from) !=3D 0) { - M4ERROR ((warning_status, errno, _("</a>cannot open `%s'"), AR= G (1))); - retcode =3D EXIT_FAILURE; + record_dependency (ARG (1), ref_from); + fail =3D 0; } + if (!silent) + M4ERROR ((warning_status, errno, _("cannot open `%s'"), ARG (1))= ); + if (fail) + retcode =3D EXIT_FAILURE; return; } =20 + record_dependency (name, ref_from); push_file (fp, name, true); free (name); } @@ -1418,7 +1425,7 @@ include (int argc, token_data **argv, bool silent) static void m4_include (struct obstack *obs MAYBE_UNUSED, int argc, token_data **arg= v) { - include (argc, argv, false); + include (argc, argv, false, REF_INCLUDE); } =20 /*----------------------------------. @@ -1428,7 +1435,7 @@ m4_include (struct obstack *obs MAYBE_UNUSED, int a= rgc, token_data **argv) static void m4_sinclude (struct obstack *obs MAYBE_UNUSED, int argc, token_data **ar= gv) { - include (argc, argv, true); + include (argc, argv, true, REF_SINCLUDE); } =0C /* More miscellaneous builtins -- "maketemp", "errprint", "__file__", diff --git a/src/m4.c b/src/m4.c index 8f9f6d2e..227d8a20 100644 --- a/src/m4.c +++ b/src/m4.c @@ -77,6 +77,21 @@ int nesting_limit =3D 1024; const char *user_word_regexp =3D ""; #endif =20 +/* Pathname of dependency file being made (--makedep=3DPATH). */ +static const char *makedep_path =3D NULL; + +/* Target for dependency rule being made (--makedep-target=3DTARGET). */= +static const char *makedep_target =3D 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 =3D REF_NONE; + +/* Bitmask of which places files are referenced from that will trigger + phony rules to be generated (--makedep-phony-*). */ +static int makedep_phony =3D 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; @@ -267,6 +282,37 @@ Frozen state files:\n\ "), stdout); puts (""); fputs (_("\ +Make dependency generation:\n\ + --makedep=3DFILE write make dependency rule(s) into FILE= \n\ + --makedep-target=3DTARGET specify target of generated dependency = rule\n\ + The --makedep and --makedep-target opti= ons\n\ + must be used together, either both pres= ent,\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 generat= ed\n\ + files and become dependencies regardles= s.\n\ + --makedep-gen-missing-all\n\ + equivalent to --makedep-gen-missing-argfi= les\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[=3DFLAGS] set debug level (no FLAGS implies `aeq'= )\n\ --debugfile[=3DFILE] redirect debug and trace output to FILE= \n\ @@ -317,6 +363,16 @@ enum DEBUGFILE_OPTION =3D CHAR_MAX + 1, /* no short opt */ DIVERSIONS_OPTION, /* not quite -N, because of message */ 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 */ =20 HELP_OPTION, /* no short opt */ VERSION_OPTION /* no short opt */ @@ -354,6 +410,21 @@ static const struct option long_options[] =3D { {"warn-macro-sequence", optional_argument, NULL, WARN_MACRO_SEQUENCE_OPTION}, =20 + {"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_O= PTION}, + {"makedep-phony-include", no_argument, NULL, MAKEDEP_PHONY_INCLUDE_OPT= ION}, + {"makedep-phony-sinclude", no_argument, NULL, MAKEDEP_PHONY_SINCLUDE_O= PTION}, + {"makedep-phony-all", no_argument, NULL, MAKEDEP_PHONY_ALL_OPTION}, + {"help", no_argument, NULL, HELP_OPTION}, {"version", no_argument, NULL, VERSION_OPTION}, =20 @@ -380,11 +451,15 @@ process_file (const char *name) if (fp =3D=3D NULL) { error (0, errno, _("cannot open `%s'"), name); - /* Set the status to EXIT_FAILURE, even though we - continue to process files after a missing file. */ - retcode =3D EXIT_FAILURE; + if ((makedep_gen_missing & REF_CMD_LINE) !=3D 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 =3D EXIT_FAILURE; return; } + record_dependency (full_name, REF_CMD_LINE); push_file (fp, full_name, true); free (full_name); } @@ -615,6 +690,50 @@ main (int argc, char *const *argv) (which disables these warnings). */ macro_sequence =3D optarg; break; +=20 + case MAKEDEP_OPTION: + if (makedep_path !=3D NULL) + usage (EXIT_FAILURE); + makedep_path =3D optarg; + break; + + case MAKEDEP_TARGET_OPTION: + if (makedep_target !=3D NULL) + usage (EXIT_FAILURE); + makedep_target =3D optarg; + break; + + case MAKEDEP_GEN_MISSING_ARGFILES_OPTION: + makedep_gen_missing |=3D REF_CMD_LINE; + break; + + case MAKEDEP_GEN_MISSING_INCLUDE_OPTION: + makedep_gen_missing |=3D REF_INCLUDE; + break; + + case MAKEDEP_GEN_MISSING_SINCLUDE_OPTION: + makedep_gen_missing |=3D REF_SINCLUDE; + break; + + case MAKEDEP_GEN_MISSING_ALL_OPTION: + makedep_gen_missing |=3D REF_ALL; + break; + + case MAKEDEP_PHONY_ARGFILES_OPTION: + makedep_phony |=3D REF_CMD_LINE; + break; + + case MAKEDEP_PHONY_INCLUDE_OPTION: + makedep_phony |=3D REF_INCLUDE; + break; + + case MAKEDEP_PHONY_SINCLUDE_OPTION: + makedep_phony |=3D REF_SINCLUDE; + break; + + case MAKEDEP_PHONY_ALL_OPTION: + makedep_phony |=3D REF_ALL; + break; =20 case VERSION_OPTION: version_etc (stdout, PACKAGE, PACKAGE_NAME, VERSION, AUTHORS, NU= LL); @@ -632,6 +751,30 @@ main (int argc, char *const *argv) if (debugfile && !debug_set_output (debugfile)) M4ERROR ((warning_status, errno, _("cannot set debug file `%s'"), debugfile)); +=20 + /* Verify mutual consistency of makedep options. */ + if ((makedep_path =3D=3D NULL) && (makedep_target =3D=3D NULL)= ) + { + /* Makedep mode is NOT active. */ + if (makedep_gen_missing !=3D 0) + M4ERROR ((EXIT_FAILURE, 0, + _("--makedep-gen-missing-* requires --makedep and --ma= kedep-target"))); + if (makedep_phony !=3D 0) + M4ERROR ((EXIT_FAILURE, 0, + _("--makedep-phony-* requires --makedep and --makedep-= target"))); + if ((makedep_gen_missing | makedep_phony) !=3D 0) + exit (EXIT_FAILURE); + } + else if ((makedep_path !=3D NULL) && (makedep_target !=3D NULL= )) + { + /* Makedep mode is active. */ + } + else + { + M4ERROR ((EXIT_FAILURE, 0, + _("--makedep must be used with --makedep-target"))); + exit (EXIT_FAILURE); + } =20 input_init (); output_init (); @@ -738,6 +881,8 @@ main (int argc, char *const *argv) undivert_all (); } output_exit (); + if (makedep_path !=3D NULL) + generate_make_dependencies (makedep_path, makedep_target, makedep_ph= ony); free_macro_sequence (); exit (retcode); } diff --git a/src/m4.h b/src/m4.h index 8d0d96dd..f8735abc 100644 --- a/src/m4.h +++ b/src/m4.h @@ -135,6 +135,14 @@ extern int nesting_limit; /* -L */ #ifdef ENABLE_CHANGEWORD extern const char *user_word_regexp; /* -W */ #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 */ =20 /* Error handling. */ extern int retcode; @@ -412,7 +420,10 @@ extern void hack_all_symbols (hack_symbol *, void *)= ; extern int expansion_level; =20 extern void expand_input (void); -extern void call_macro (symbol *, int, token_data **, struct obstack *);= +extern void call_macro (symbol *, int, token_data **, struct obstack *) + /* This doesn't seem right, but gcc-8.5.0 is suggesting that this */ + /* function might be a candidate for attribute 'cold', stopping the bu= ild. */ + ATTRIBUTE_COLD; =0C /* File: builtin.c --- builtins. */ =20 @@ -470,6 +481,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)= ; =0C /* File: eval.c --- expression evaluation. */ =20 diff --git a/src/path.c b/src/path.c index af5e7cf0..f34ef8a3 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 */ +=20 +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 */ =0C =20 void @@ -189,6 +201,72 @@ m4_path_search (const char *file, bool binary, char = **result) return fp; } =20 +void +record_dependency (const char *path, int ref_from) +{ + dependency *dp; + for (dp =3D dependency_list; dp !=3D NULL; dp =3D dp->next) + if (strcmp (path, dp->path) =3D=3D 0) + { + /* Remember all the places this file has been referenced from. *= / + dp->ref_from |=3D ref_from; + return; + } + + dp =3D xmalloc (sizeof (dependency)); + dp->next =3D NULL; + dp->ref_from =3D ref_from; + dp->path =3D xstrdup (path); + + if (dependency_list_end =3D=3D NULL) + dependency_list =3D dp; + else + dependency_list_end->next =3D dp; + dependency_list_end =3D dp; +} + +void +generate_make_dependencies (const char *path, const char *target, int ph= ony) +{ + FILE *fp; + size_t col, len, maxcol; + dependency *dp; + + fp =3D fopen (path, "w"); + if (fp =3D=3D NULL) + { + M4ERROR ((0, errno, "unable to open `%s'.\n", path)); + return; + } + + fputs (_("# Automatically generated by GNU m4.\n"), fp); + + /* Generate the main dependency rule. */ + maxcol =3D 78; + fprintf (fp, "%s:", target); + col =3D strlen (target) + 1; + for (dp =3D dependency_list; dp !=3D NULL; dp =3D dp->next) + { + len =3D 1 + strlen (dp->path); + if (col + len + 2 > maxcol) /* +2 is for trailing space/backsla= sh */ + { + fprintf (fp," \\\n "); + col =3D 1; + } + fprintf (fp, " %s", dp->path); + col +=3D len; + } + fprintf (fp, "\n"); + + /* Generate phony targets for user-specified subset of dependencies. *= / + if (phony !=3D 0) + for (dp =3D dependency_list; dp !=3D NULL; dp =3D dp->next) + if ((dp->ref_from & phony) !=3D 0) + fprintf (fp, "\n%s:\n", dp->path); + + fclose (fp); +} + #ifdef DEBUG_INCL =20 static void MAYBE_UNUSED </pre> </body> </html> --------------CPX3LGelT46DInmb58R6Q6HY-- --===============3538069760711764656== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Content-Disposition: inline X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KTTQtcGF0Y2hl cyBtYWlsaW5nIGxpc3QKTTQtcGF0Y2hlc0BnbnUub3JnCmh0dHBzOi8vbGlzdHMuZ251Lm9yZy9t YWlsbWFuL2xpc3RpbmZvL200LXBhdGNoZXMK --===============3538069760711764656==--