ChangeLog | 17 ++ NEWS | 2 THANKS | 2 doc/m4.texinfo | 116 +++++++++++++++ m4/m4.c | 53 ++++++ m4/m4module.h | 22 ++ m4/m4private.h | 49 ++++++ m4/path.c | 142 ++++++++++++++++++ modules/m4.c | 19 +- src/main.c | 113 ++++++++++++++ tests/makedep.at | 407 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/testsuite.at | 3 12 files changed, 938 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index d5f2ad7..f7ef3f3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2011-02-10 David Warme + Lorenzo Di Gregorio + + Add ability to generate make dependency rules. + * doc/m4.texinfo: Document new options. + * m4/m4.c: Create/delete context->makedep. Add module + interfaces to new makedep info. + * m4/m4module.h: Add new makedep interfaces. + * m4/m4private.h: Added context->makedep stuff. + * m4/path.c: Add new interfaces to record dependencies and + generate the dependency rules. + * modules/m4.c: Record dependencies for include() / sinclude(). + * src/main.c: Add new options. Record dependencies for files + specified on the command line. Generate dependencies at end. + * NEWS: Added a blurb for this new feature. + * THANKS: Added us to the list. + 2010-03-02 Giuseppe Scrivano (tiny change) Support bootstrap --gnulib-srcdir=DIR. diff --git a/NEWS b/NEWS index 142e1e5..d951ed9 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ GNU M4 NEWS - History of user-visible changes. -*- outline -*- NOTE - there are still a number of FIXMEs to resolve before this can be promoted to 2.0. +** Add new capability to automatically generate make dependency rules. + ** Building M4 *** The build environment has been updated to modern GNU practices, diff --git a/THANKS b/THANKS index 7bd8b0a..fc8ce02 100644 --- a/THANKS +++ b/THANKS @@ -39,6 +39,7 @@ Dan Jacobson address@hidden Daniel Richard G. address@hidden David J. MacKenzie address@hidden David Perlin address@hidden +David Warme address@hidden Elbert Pol address@hidden Elias Benali address@hidden Erez Zadok address@hidden @@ -88,6 +89,7 @@ Konrad Schwarz address@hidden Kristine Lund address@hidden Krste Asanovic address@hidden Lawson Chan address@hidden +Lorenzo Di Gregorio address@hidden M. Levinson address@hidden Marcus Daniels address@hidden Marion Hakanson address@hidden diff --git a/doc/m4.texinfo b/doc/m4.texinfo index fa0c6fd..b5283c2 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -162,6 +162,7 @@ Invoking @code{m4} * 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 @@ -602,6 +603,7 @@ version of @code{m4} introduces an option named @option{--default}). * 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 @@ -988,6 +990,120 @@ frozen @var{file}. The options @option{-D}, @option{-U}, @option{-t}, after state is reloaded, but before the input files are read. @end table address@hidden Make dependency generation address@hidden Command line options for generating Makefile dependency rules + +Makefile dependency rules can be automatically generated by specifying +both the @address@hidden and address@hidden@var{target} options. + address@hidden @code address@hidden address@hidden +Causes @code{m4} to generate a dependency rule into the specified address@hidden Macro expansion output is still written to stdout as +normal. This option is analogous to the @code{-MF} option of address@hidden + address@hidden address@hidden +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 address@hidden to be specified multiple times). address@hidden table + +Note that the @address@hidden and address@hidden@var{target} options must either (a) both be +specified, or (b) neither be specified. They cannot be used +independently of each other. + +The following additional options can also be used when dependency rules +are being generated (these options are only valid when both address@hidden@var{file} and @address@hidden have +also been specified): + address@hidden @code address@hidden --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. address@hidden 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 address@hidden@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. + address@hidden --makedep-gen-missing-include +Causes @code{m4} to assume that any file included via the address@hidden()} 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 address@hidden()} and is not modified by any @address@hidden +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}. + address@hidden --makedep-gen-missing-sinclude +Causes @code{m4} to assume that any file included via the address@hidden()} 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 address@hidden()} and is not modified by any @address@hidden +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. + address@hidden --makedep-gen-missing-all +This option is equivalent to specifying all three options address@hidden, address@hidden and address@hidden address@hidden 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. + +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 address@hidden to match. Dependencies that match one or more of these +classes cause a single dummy rule to be generated for them: + address@hidden @code address@hidden --makedep-phony-argfiles +Causes @code{m4} to generate a ``phony'' target for each file that is +specified on the command line. + address@hidden --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}. + address@hidden --makedep-phony-sinclude +Causes @code{m4} to generate a ``phony'' target for each file that is +the subject of an @code{sinclude()} macro. + address@hidden --makedep-phony-all +is equivalent to specifying all three options: address@hidden address@hidden address@hidden address@hidden table + @node Debugging options @section Command line options for debugging diff --git a/m4/m4.c b/m4/m4.c index 1ba5a6c..7ac78f7 100644 --- a/m4/m4.c +++ b/m4/m4.c @@ -44,6 +44,10 @@ m4_create (void) (m4__search_path_info *) xzalloc (sizeof *context->search_path); m4__include_init (context); + context->makedep = + (m4__makedep_info *) xzalloc (sizeof *context->makedep); + m4__makedep_init (context); + return context; } @@ -80,6 +84,8 @@ m4_delete (m4 *context) free (context->search_path); } + m4__makedep_delete (context); + for (i = 0; i < context->stacks_count; i++) { assert (context->arg_stacks[i].refcount == 0 @@ -142,3 +148,50 @@ m4_context_opt_bit_table } m4_context_opt_bit_table #undef M4OPT_BIT + +/* Define the makedep accessor / manipulator functions. */ + +int (CONC(m4_makedep_, gen_missing_opt_enabled)) (m4 *context, int mask) +{ + return (context->makedep->gen_missing & mask); +} + +const char * (CONC(m4_makedep_, get_path)) (m4 * context) +{ + return (context->makedep->path); +} + +void (CONC(m4_makedep_, set_path)) (m4 * context, const char *path) +{ + context->makedep->path = path; +} + +const char * (CONC(m4_makedep_, get_target)) (m4 * context) +{ + return (context->makedep->target); +} + +void (CONC(m4_makedep_, set_target)) (m4 * context, const char *target) +{ + context->makedep->target = target; +} + +int (CONC(m4_makedep_, get_gen_missing_opt_bits)) (m4 * context) +{ + return (context->makedep->gen_missing); +} + +void (CONC(m4_makedep_, set_gen_missing_opt_bits)) (m4 * context, int mask) +{ + context->makedep->gen_missing |= mask; +} + +int (CONC(m4_makedep_, get_phony_opt_bits)) (m4 * context) +{ + return (context->makedep->phony); +} + +void (CONC(m4_makedep_, set_phony_opt_bits)) (m4 * context, int mask) +{ + context->makedep->phony |= mask; +} diff --git a/m4/m4module.h b/m4/m4module.h index fa245ec..f68ced9 100644 --- a/m4/m4module.h +++ b/m4/m4module.h @@ -560,6 +560,28 @@ extern FILE * m4_path_search (m4 *, const char *, char **); +/* -- MAKEDEP MANAGEMENT --- */ + +/* 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 */ + +extern int (m4_makedep_gen_missing_opt_enabled) (m4 *, int); +extern const char * (m4_makedep_get_path) (m4 *); +extern void (m4_makedep_set_path) (m4 *, const char *); +extern const char * (m4_makedep_get_target) (m4 *); +extern void (m4_makedep_set_target) (m4 *, const char *); +extern int (m4_makedep_get_gen_missing_opt_bits) (m4 *); +extern void (m4_makedep_set_gen_missing_opt_bits) (m4 *, int); +extern int (m4_makedep_get_phony_opt_bits) (m4 *); +extern void (m4_makedep_set_phony_opt_bits) (m4 *, int); +extern void m4_makedep_env_init (m4 *); +extern void m4_record_dependency (m4 *, const char *, int); +extern void m4_generate_make_dependencies (m4 *); + #define obstack_chunk_alloc xmalloc #define obstack_chunk_free free diff --git a/m4/m4private.h b/m4/m4private.h index f7b47f8..2eb87ab 100644 --- a/m4/m4private.h +++ b/m4/m4private.h @@ -30,6 +30,7 @@ typedef struct m4__search_path_info m4__search_path_info; typedef struct m4__macro_arg_stacks m4__macro_arg_stacks; +typedef struct m4__makedep_info m4__makedep_info; typedef struct m4__symbol_chain m4__symbol_chain; typedef enum { @@ -81,6 +82,7 @@ struct m4 { m4__macro_arg_stacks *arg_stacks; /* Array of current argv refs. */ size_t stacks_count; /* Size of arg_stacks. */ size_t expansion_level;/* Macro call nesting level. */ + m4__makedep_info *makedep; /* Dependency info. */ }; #define M4_OPT_PREFIX_BUILTINS_BIT (1 << 0) /* -P */ @@ -149,6 +151,7 @@ struct m4 { /* Accessors for private fields of m4, which have no function version exported in m4module.h. */ #define m4__get_search_path(C) ((C)->search_path) +#define m4__get_makedep_info(C) ((C)->makedep) /* --- BUILTIN MANAGEMENT --- */ @@ -606,6 +609,52 @@ struct m4__search_path_info { extern void m4__include_init (m4 *); +/* --- MAKEDEP MANAGEMENT --- */ + +typedef struct m4__dependency m4__dependency; + +struct m4__dependency { + m4__dependency *next; /* next in list of dependencies */ + int ref_from; /* bit mask: places file referenced from */ + char path [1]; /* pathname of this dependency */ +}; + +struct m4__makedep_info { + m4__dependency *dep_list; /* the list of dependencies */ + m4__dependency *dep_list_end; /* the end of same */ + const char *path; /* pathname of dependency rule being made */ + /* (--makedep=PATH) */ + const char *target; /* target for dependency rule being made */ + /* (--makedep-target=TARGET) */ + int gen_missing; /* Bitmask of places that assume non- */ + /* existent files are actually generated, */ + /* and so a dependency should be listed */ + /* regardless (--makedep-gen-missing-*) */ + int phony; /* Bitmask of which places files are */ + /* referenced from that will trigger phony */ + /* rules to be generated (--makedep-phony-*) */ +}; + +#define m4_makedep_gen_missing_opt_enabled(C, MASK) \ + ((m4__get_makedep_info(C)->gen_missing & (MASK)) != 0) + +#define m4_makedep_get_path(C) (m4__get_makedep_info(C)->path) +#define m4_makedep_set_path(C,PATH) (m4__get_makedep_info(C)->path = (PATH)) +#define m4_makedep_get_target(C) (m4__get_makedep_info(C)->target) +#define m4_makedep_set_target(C,TGT) (m4__get_makedep_info(C)->target = (TGT)) +#define m4_makedep_get_gen_missing_opt_bits(C) \ + (m4__get_makedep_info(C)->gen_missing) +#define m4_makedep_set_gen_missing_opt_bits(C, MASK) \ + (m4__get_makedep_info(C)->gen_missing |= (MASK)) +#define m4_makedep_get_phony_opt_bits(C) \ + (m4__get_makedep_info(C)->phony) +#define m4_makedep_set_phony_opt_bits(C, MASK) \ + (m4__get_makedep_info(C)->phony |= (MASK)) + +extern void m4__makedep_init (m4 *); +extern void m4__makedep_delete (m4 *); + + /* Debugging the memory allocator. */ #if WITH_DMALLOC diff --git a/m4/path.c b/m4/path.c index 8ba1caa..d55f851 100644 --- a/m4/path.c +++ b/m4/path.c @@ -227,6 +227,148 @@ m4__include_init (m4 *context) } + +void +m4__makedep_init (m4 *context) +{ + m4__makedep_info *info = m4__get_makedep_info (context); + + assert (info); + info->gen_missing = REF_NONE; + info->phony = REF_NONE; +} + +void +m4_makedep_env_init (m4 *context) +{ + m4__makedep_info *info = m4__get_makedep_info (context); + + assert (info); + + /* Verify mutual consistency of makedep options. */ + if ((info->path == NULL) && (info->target == NULL)) + { + /* Makedep mode is NOT active. */ + if (info->gen_missing != REF_NONE) + m4_error (context, EXIT_FAILURE, 0, NULL, + _("--makedep-gen-missing-* requires --makedep and --makedep-target")); + if (info->phony != REF_NONE) + m4_error (context, EXIT_FAILURE, 0, NULL, + _("--makedep-phony-* requires --makedep and --makedep-target")); + if ((info->gen_missing | info->phony) != REF_NONE) + exit (EXIT_FAILURE); + } + else if ((info->path != NULL) && (info->target != NULL)) + { + /* Makedep mode is active. */ + } + else + { + m4_error (context, EXIT_FAILURE, 0, NULL, + _("--makedep and --makedep-target cannot be used independently.")); + exit (EXIT_FAILURE); + } +} + +void +m4_record_dependency (m4 *context, const char * path, int ref_from) +{ + m4__makedep_info *info = m4__get_makedep_info (context); + m4__dependency *dp; + size_t len, nbytes; + assert (info); + for (dp = info->dep_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; + } + + len = strlen (path); + nbytes = offsetof(m4__dependency, path[0]) + len + 1; + dp = xmalloc (nbytes); + dp->next = NULL; + dp->ref_from = ref_from; + strcpy (dp->path, path); + + if (info->dep_list_end == NULL) + info->dep_list = dp; + else + info->dep_list_end->next = dp; + info->dep_list_end = dp; +} + +void +m4_generate_make_dependencies (m4 *context) +{ + m4__makedep_info *info = m4__get_makedep_info (context); + FILE *fp; + int col, len, maxcol; + m4__dependency *dp; + + assert (info); + + if (info->path == NULL) + return; /* --makedep mode is not active. */ + assert (info->target); + + fp = fopen (info->path, "w"); + if (fp == NULL) + { + m4_error (context, 0, errno, NULL, _("Unable to open %s"), + quotearg_style (locale_quoting_style, info->path)); + return; + } + + /* Generate the main dependency rule. */ + maxcol = 78; + fprintf (fp, "%s:", info->target); + col = strlen (info->target) + 1; + for (dp = info->dep_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 (info->phony != 0) + for (dp = info->dep_list; dp != NULL; dp = dp->next) + if ((dp->ref_from & info->phony) != 0) + fprintf (fp, "\n%s:\n", dp->path); + + fclose (fp); +} + +void +m4__makedep_delete (m4 *context) +{ + m4__makedep_info *info = m4__get_makedep_info (context); + m4__dependency *dp; + + if (!info) + return; + + while ((dp = info->dep_list) != NULL) + { + info->dep_list = dp->next; + free (dp); + } + if (info->path != NULL) + DELETE (info->path); + if (info->target != NULL) + DELETE (info->target); + free (info); +} + + #ifdef DEBUG_INCL static void M4_GNUC_UNUSED diff --git a/modules/m4.c b/modules/m4.c index 9091d81..da0029a 100644 --- a/modules/m4.c +++ b/modules/m4.c @@ -99,7 +99,7 @@ typedef intmax_t number; typedef uintmax_t unumber; static void include (m4 *context, int argc, m4_macro_args *argv, - bool silent); + bool silent, int ref_from); static int dumpdef_cmp_CB (const void *s1, const void *s2); static void * dump_symbol_CB (m4_symbol_table *, const char *, size_t, m4_symbol *symbol, void *userdata); @@ -664,7 +664,7 @@ M4BUILTIN_HANDLER (changecom) argument, if it exists. Complain about inaccessible files iff SILENT is false. */ static void -include (m4 *context, int argc, m4_macro_args *argv, bool silent) +include (m4 *context, int argc, m4_macro_args *argv, bool silent, int ref_from) { FILE *fp; char *name = NULL; @@ -678,12 +678,21 @@ include (m4 *context, int argc, m4_macro_args *argv, bool silent) fp = m4_path_search (context, arg, &name); if (fp == NULL) { - if (!silent) + if (m4_makedep_gen_missing_opt_enabled (context, ref_from)) + { + m4_record_dependency (context, M4ARG (1), ref_from); + /* Give same message, but do not affect exit status. */ + if (!silent) + m4_warn (context, errno, m4_arg_info (argv), _("cannot open %s"), + quotearg_style (locale_quoting_style, arg)); + } + else if (!silent) m4_error (context, 0, errno, m4_arg_info (argv), _("cannot open %s"), quotearg_style (locale_quoting_style, arg)); return; } + m4_record_dependency (context, name, ref_from); m4_push_file (context, fp, name, true); free (name); } @@ -691,13 +700,13 @@ include (m4 *context, int argc, m4_macro_args *argv, bool silent) /* Include a file, complaining in case of errors. */ M4BUILTIN_HANDLER (include) { - include (context, argc, argv, false); + include (context, argc, argv, false, REF_INCLUDE); } /* Include a file, ignoring errors. */ M4BUILTIN_HANDLER (sinclude) { - include (context, argc, argv, true); + include (context, argc, argv, true, REF_SINCLUDE); } diff --git a/src/main.c b/src/main.c index 240e241..4437636 100644 --- a/src/main.c +++ b/src/main.c @@ -146,6 +146,36 @@ Frozen state files:\n\ -R, --reload-state=FILE reload a frozen state from FILE at start\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); fputs (_("\ Debugging:\n\ -d, --debug[=[-|+]FLAGS], --debugmode[=[-|+]FLAGS]\n\ @@ -212,6 +242,16 @@ enum TRACEOFF_OPTION, /* no short opt */ UNLOAD_MODULE_OPTION, /* no short opt */ WORD_REGEXP_OPTION, /* deprecated, used to be -W */ + 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 */ @@ -260,6 +300,20 @@ static const struct option long_options[] = {"traceoff", required_argument, NULL, TRACEOFF_OPTION}, {"unload-module", required_argument, NULL, UNLOAD_MODULE_OPTION}, {"word-regexp", required_argument, NULL, WORD_REGEXP_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}, @@ -311,10 +365,19 @@ process_file (m4 *context, const char *name) FILE *fp = m4_path_search (context, name, &full_name); if (fp == NULL) { - m4_error (context, 0, errno, NULL, _("cannot open %s"), - quotearg_style (locale_quoting_style, name)); + if (m4_makedep_gen_missing_opt_enabled (context, REF_CMD_LINE)) + { + m4_record_dependency (context, name, REF_CMD_LINE); + /* Same message, but warning only. */ + m4_warn (context, errno, NULL, _("cannot open %s"), + quotearg_style (locale_quoting_style, name)); + } + else + m4_error (context, 0, errno, NULL, _("cannot open %s"), + quotearg_style (locale_quoting_style, name)); return; } + m4_record_dependency (context, full_name, REF_CMD_LINE); m4_push_file (context, fp, full_name, true); free (full_name); } @@ -592,6 +655,50 @@ main (int argc, char *const *argv, char *const *envp) m4_set_safer_opt (context, true); break; + case MAKEDEP_OPTION: + if (m4_makedep_get_path (context) != NULL) + usage (EXIT_FAILURE); + m4_makedep_set_path (context, xstrdup (optarg)); + break; + + case MAKEDEP_TARGET_OPTION: + if (m4_makedep_get_target (context) != NULL) + usage (EXIT_FAILURE); + m4_makedep_set_target (context, xstrdup (optarg)); + break; + + case MAKEDEP_GEN_MISSING_ARGFILES_OPTION: + m4_makedep_set_gen_missing_opt_bits (context, REF_CMD_LINE); + break; + + case MAKEDEP_GEN_MISSING_INCLUDE_OPTION: + m4_makedep_set_gen_missing_opt_bits (context, REF_INCLUDE); + break; + + case MAKEDEP_GEN_MISSING_SINCLUDE_OPTION: + m4_makedep_set_gen_missing_opt_bits (context, REF_SINCLUDE); + break; + + case MAKEDEP_GEN_MISSING_ALL_OPTION: + m4_makedep_set_gen_missing_opt_bits (context, REF_ALL); + break; + + case MAKEDEP_PHONY_ARGFILES_OPTION: + m4_makedep_set_phony_opt_bits (context, REF_CMD_LINE); + break; + + case MAKEDEP_PHONY_INCLUDE_OPTION: + m4_makedep_set_phony_opt_bits (context, REF_INCLUDE); + break; + + case MAKEDEP_PHONY_SINCLUDE_OPTION: + m4_makedep_set_phony_opt_bits (context, REF_SINCLUDE); + break; + + case MAKEDEP_PHONY_ALL_OPTION: + m4_makedep_set_phony_opt_bits (context, REF_ALL); + break; + case VERSION_OPTION: version_etc (stdout, PACKAGE, PACKAGE_NAME, VERSION, AUTHORS, NULL); exit (EXIT_SUCCESS); @@ -628,6 +735,7 @@ main (int argc, char *const *argv, char *const *envp) m4_input_init (context); m4_output_init (context); m4_include_env_init (context); + m4_makedep_env_init (context); if (frozen_file_to_read) reload_frozen_state (context, frozen_file_to_read); @@ -794,6 +902,7 @@ main (int argc, char *const *argv, char *const *envp) m4__module_exit (context); m4_output_exit (); m4_input_exit (); + m4_generate_make_dependencies (context); /* Change debug stream back to stderr, to force flushing the debug stream and detect any errors it might have encountered. The diff --git a/tests/makedep.at b/tests/makedep.at new file mode 100644 index 0000000..cf82ea9 --- /dev/null +++ b/tests/makedep.at @@ -0,0 +1,407 @@ +# Hand crafted tests for GNU M4. -*- Autotest -*- +# Copyright (C) 2001, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, +# Inc. + +# This file is part of GNU M4. +# +# GNU M4 is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# GNU M4 is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +AT_BANNER([Makedep.]) + + +# Generate the following subdir and input files: +# tmp/ tmp/f1 tmp/f2 tmp/f3 tmp/f4 tmp/f5 tmp/f6 tmp/f7. +# The tmp/f1 file includes the others in various ways. +m4_define([AT_MAKEDEP_CREATE_INPUT_FILES], +[dnl lots of data to set up. +AT_CHECK([mkdir tmp]) +AT_DATA([[tmp/f1]], [[File 1 +include(`f2')dnl +include(`f3')dnl +sinclude(`f4')dnl +sinclude(`f5')dnl +include(`f6')dnl +sinclude(`f6')dnl +include(`f7')dnl +sinclude(`f7')dnl +]]) +AT_DATA([[tmp/f2]], [[File 2 +]]) +AT_DATA([[tmp/f3]], [[File 3 +]]) +AT_DATA([[tmp/f4]], [[File 4 +]]) +AT_DATA([[tmp/f5]], [[File 5 +]]) +AT_DATA([[tmp/f6]], [[File 6 +]]) +AT_DATA([[tmp/f7]], [[File 7 +]])]) + +# The standard -I option and input files we invoke m4 with. +m4_define([AT_MAKEDEP_INFILES], [-Itmp tmp/f1 tmp/f3 tmp/f5 tmp/f7]) + +# The standard arguments we invoke m4 with to exercise --makedep mode. +m4_define([AT_MAKEDEP_ARGS], +[--makedep=dep --makedep-target="t1 t2" AT_MAKEDEP_INFILES]) + +# The standard output we expect to get. +m4_define([AT_MAKEDEP_EXPECTED_OUTPUT], [File 1 +File 2 +File 3 +File 4 +File 5 +File 6 +File 6 +File 7 +File 7 +File 3 +File 5 +File 7 +]) + +# The standard dependency rule we expect to get. +m4_define([AT_MAKEDEP_EXPECTED_DEP], + [t1 t2: tmp/f1 tmp/f2 tmp/f3 tmp/f4 tmp/f5 tmp/f6 tmp/f7]) + +# Verify that the m4 stdout is correct. +m4_define([AT_MKDEP_CHECK_OUTPUT], +[AT_CHECK([echo -n "AT_MAKEDEP_EXPECTED_OUTPUT()" | cmp -s stdout -])]) + +# Verify that the m4 generated dependency is correct. +m4_define([AT_MKDEP_CHECK_DEP], +[AT_CHECK([echo "AT_MAKEDEP_EXPECTED_DEP()$1" | cmp -s dep -])]) + + +## --------------- ## +## no makedep args ## +## --------------- ## + +AT_SETUP([no makedep args]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([AT_MAKEDEP_INFILES], [0], +[AT_MAKEDEP_EXPECTED_OUTPUT]) + +AT_CLEANUP + + +## ------------------------------ ## +## makedep without makedep-target ## +## ------------------------------ ## + +AT_SETUP([makedep without makedep-target]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([--makedep=dep AT_MAKEDEP_INFILES], [1], [], +[m4: --makedep and --makedep-target cannot be used independently. +]) + +AT_CLEANUP + + +## ------------------------------ ## +## makedep-target without makedep ## +## ------------------------------ ## + +AT_SETUP([makedep-target without makedep]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([--makedep-target="t1 t2" AT_MAKEDEP_INFILES], [1], [], +[m4: --makedep and --makedep-target cannot be used independently. +]) + +AT_CLEANUP + + +## --------------------------- ## +## basic makedep functionality ## +## --------------------------- ## + +AT_SETUP([basic makedep functionality]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([AT_MAKEDEP_ARGS], [0], [stdout]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP() + +AT_CLEANUP + + +## -------------- ## +## phony argfiles ## +## -------------- ## + +AT_SETUP([phony argfiles]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([--makedep-phony-argfiles AT_MAKEDEP_ARGS], [0], [stdout]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP([ + +tmp/f1: + +tmp/f3: + +tmp/f5: + +tmp/f7:]) + +AT_CLEANUP + + +## ------------- ## +## phony include ## +## ------------- ## + +AT_SETUP([phony include]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([--makedep-phony-include AT_MAKEDEP_ARGS], [0], [stdout]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP([ + +tmp/f2: + +tmp/f3: + +tmp/f6: + +tmp/f7:]) + +AT_CLEANUP + + +## -------------- ## +## phony sinclude ## +## -------------- ## + +AT_SETUP([phony sinclude]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([--makedep-phony-sinclude AT_MAKEDEP_ARGS], [0], [stdout]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP([ + +tmp/f4: + +tmp/f5: + +tmp/f6: + +tmp/f7:]) + +AT_CLEANUP + + +## ---------------------- ## +## phony argfiles+include ## +## ---------------------- ## + +AT_SETUP([phony argfiles+include]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([--makedep-phony-argfiles --makedep-phony-include AT_MAKEDEP_ARGS], [0], [stdout]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP([ + +tmp/f1: + +tmp/f2: + +tmp/f3: + +tmp/f5: + +tmp/f6: + +tmp/f7:]) + +AT_CLEANUP + + +## ----------------------- ## +## phony argfiles+sinclude ## +## ----------------------- ## + +AT_SETUP([phony argfiles+sinclude]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([--makedep-phony-argfiles --makedep-phony-sinclude AT_MAKEDEP_ARGS], [0], [stdout]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP([ + +tmp/f1: + +tmp/f3: + +tmp/f4: + +tmp/f5: + +tmp/f6: + +tmp/f7:]) + +AT_CLEANUP + + +## ---------------------- ## +## phony include+sinclude ## +## ---------------------- ## + +AT_SETUP([phony include+sinclude]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([--makedep-phony-include --makedep-phony-sinclude AT_MAKEDEP_ARGS], [0], [stdout]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP([ + +tmp/f2: + +tmp/f3: + +tmp/f4: + +tmp/f5: + +tmp/f6: + +tmp/f7:]) + +AT_CLEANUP + + +## ------------------------------- ## +## phony argfiles+include+sinclude ## +## ------------------------------- ## + +AT_SETUP([phony argfiles+include+sinclude]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([--makedep-phony-argfiles --makedep-phony-include --makedep-phony-sinclude AT_MAKEDEP_ARGS], [0], [stdout]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP([ + +tmp/f1: + +tmp/f2: + +tmp/f3: + +tmp/f4: + +tmp/f5: + +tmp/f6: + +tmp/f7:]) + +AT_CLEANUP + + +## --------- ## +## phony all ## +## --------- ## + +AT_SETUP([phony all]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([--makedep-phony-all AT_MAKEDEP_ARGS], [0], [stdout]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP([ + +tmp/f1: + +tmp/f2: + +tmp/f3: + +tmp/f4: + +tmp/f5: + +tmp/f6: + +tmp/f7:]) + +AT_CLEANUP + + +## -------------------- ## +## gen missing argfiles ## +## -------------------- ## + +AT_SETUP([gen missing argfiles]) + +AT_MAKEDEP_CREATE_INPUT_FILES + +AT_CHECK_M4([--makedep-gen-missing-argfiles AT_MAKEDEP_ARGS missing], + [0], [stdout], +[m4: warning: cannot open `missing': No such file or directory +]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP([ missing]) + +AT_CLEANUP + + +## ------------------- ## +## gen missing include ## +## ------------------- ## + +AT_SETUP([gen missing include]) + +AT_MAKEDEP_CREATE_INPUT_FILES +AT_DATA([[tmp/f8]], [[include(`missing')dnl +]]) + +AT_CHECK_M4([--makedep-gen-missing-include AT_MAKEDEP_ARGS tmp/f8], + [0], [stdout], +[m4:tmp/f8:1: warning: include: cannot open `missing': No such file or directory +]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP([ tmp/f8 missing]) + +AT_CLEANUP + + +## -------------------- ## +## gen missing sinclude ## +## -------------------- ## + +AT_SETUP([gen missing sinclude]) + +AT_MAKEDEP_CREATE_INPUT_FILES +AT_DATA([[tmp/f8]], [[sinclude(`missing')dnl +]]) + +AT_CHECK_M4([--makedep-gen-missing-sinclude AT_MAKEDEP_ARGS tmp/f8], + [0], [stdout]) +AT_MKDEP_CHECK_OUTPUT() +AT_MKDEP_CHECK_DEP([ tmp/f8 missing]) + +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index 0d65042..3bba0a7 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -181,6 +181,9 @@ m4_include([options.at]) # Frozen files. m4_include([freeze.at]) +# Makedep options. +m4_include([makedep.at]) + # Hand crafted tests. m4_include([others.at])