On Mon, Apr 21, 2025 at 10:25:47AM -0500, Eric Blake via M4-patches wrote:
> From: David Warme <[email protected]>
> 
> Add ability to generate make dependency rules.
> * doc/m4.texinfo: Document new options.
> * 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 references, 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.
> Co-authored-by: Lorenzo Di Gregorio <[email protected]>
> ---
> 
> This is NOT yet ready for inclusion - I insist that we NEED to have
> something that tests the functionality during 'make check' to ensure
> it doesn't regress.  And there are probably other nitpicks that I will
> clean up (such as ChangeLog style, error message tweaks, figuring out
> the preferred way to do the flexible allocation of the file name in
> storage alongside 'struct dependency', and so forth).  However, I'm
> reposting David's original patch as tentatively rebased to branch-1.6,
> to make it easier to continue to get this ready for inclusion before I
> cut the next beta release in preparation for an eventual 1.6.

Among other things, 'make syntax-check' fails:

> +++ b/doc/m4.texi
> @@ -145,6 +145,7 @@ Top
> 
>  * Operation modes::             Command line options for operation modes
>  * Preprocessor features::       Command line options for preprocessor 
> features
> +* Make dependency generation::  Generating make depencency rules

Typo: s/depencency/dependency/

> +++ b/src/builtin.c
> @@ -1472,13 +1472,14 @@ m4_changecom (struct obstack *obs MAYBE_UNUSED, int 
> argc,
>     argument, if it exists.  Complain about inaccessible files iff
>     SILENT is false.  */
>  static void
> -include (int argc, macro_arguments *argv, bool silent)
> +include (int argc, macro_arguments *argv, bool silent, int ref_from)

Should ref_from be an enum instead of int argument, since there are
only a finite number of callers?

> +++ b/src/m4.c

> @@ -365,6 +421,21 @@ static const struct option long_options[] = {
>    {"warn-macro-sequence", optional_argument, NULL,
>     WARN_MACRO_SEQUENCE_OPTION},
> 
> +  {"makedep", required_argument, NULL, MAKEDEP_OPTION},
> +  {"makedep-target", required_argument, NULL, MAKEDEP_TARGET_OPTION},
> +  {"makedep-gen-missing-argfiles", no_argument, NULL,
> +     MAKEDEP_GEN_MISSING_ARGFILES_OPTION},
> +  {"makedep-gen-missing-include", no_argument, NULL,
> +     MAKEDEP_GEN_MISSING_INCLUDE_OPTION},
> +  {"makedep-gen-missing-sinclude", no_argument, NULL,
> +     MAKEDEP_GEN_MISSING_SINCLUDE_OPTION},
> +  {"makedep-gen-missing-all", no_argument, NULL,
> +     MAKEDEP_GEN_MISSING_ALL_OPTION},
> +  {"makedep-phony-argfiles", no_argument, NULL, 
> MAKEDEP_PHONY_ARGFILES_OPTION},
> +  {"makedep-phony-include", no_argument, NULL, MAKEDEP_PHONY_INCLUDE_OPTION},
> +  {"makedep-phony-sinclude", no_argument, NULL, 
> MAKEDEP_PHONY_SINCLUDE_OPTION},

Long lines need wrapping.  'make syntax-check' runs GNU indent which
wants to fit things in 80 columns; I'm inclined to accept its
suggestions, except that:

> +  else if (!makedep_path != !makedep_target)
> +    m4_error (EXIT_FAILURE, 0, NULL,
> +              _("--makedep and --makedep-target cannot be used 
> independently"));

here it suggested:

     m4_error (EXIT_FAILURE, 0, NULL,
-              _("--makedep and --makedep-target cannot be used 
independently"));
+              _
+              ("--makedep and --makedep-target cannot be used independently"));

which is wrong.  The workaround there may be a shorter message:

_("--makedep must be used with --makedep-target")

> +++ 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 [1];                /* pathname of this dependency */

I still need to research the best way to do this; but if we keep it
with [1], GNU indent does not like the space before [.

> +};
> +
> +typedef struct dependency dependency;
> +
> +static dependency *dependency_list;     /* the list of dependencies */
> +static dependency *dependency_list_end; /* the end of same */
>  
> 
>  void
> @@ -194,6 +206,74 @@ m4_path_search (const char *file, bool binary, char 
> **result)
>    return fp;
>  }
> 
> +void
> +record_dependency (const char * path, int ref_from)

const char *path

> +{
> +  dependency *dp;
> +  size_t len, nbytes;
> +  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;
> +      }
> +
> +  len = strlen (path);
> +  nbytes = offsetof(dependency, path[0]) + len + 1;

offsetof (dependency

> +  dp = xmalloc (nbytes);
> +  dp->next = NULL;
> +  dp->ref_from = ref_from;
> +  strcpy (dp->path, 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;
> +  int col, len, maxcol;

Anything populated with strlen() should probably be size_t, not int.

> +  dependency *dp;
> +
> +  fp = fopen (path, "w");
> +  if (!fp)
> +    {
> +      m4_error (0, errno, NULL, "unable to open %s",
> +                quotearg_style_mem (locale_quoting_style, path, strlen 
> (path)));

Long line.

> +      return;
> +    }
> +
> +  /* 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);

Should we store len in 'struct dependency' so we aren't recomputing
strlen() each time a new lookup is done?

> +      if (col + len + 2 > maxcol) /* +2 is for trailing space/backslash */
> +        {
> +          fprintf (fp," \\\n ");

fp, " \\\n "

Using fprintf to print something without % is overkill; fputs is
better.  Why is the next line starting with a space here?

> +          col = 1;
> +        }
> +      fprintf (fp, " %s", dp->path);
> +      col += len;
> +    }
> +  fputs ("\n", fp);
> +
> +  /* Generate phony targets for user-specified subset of dependencies. */
> +  if (phony != 0)
> +    for (dp = dependency_list; dp != NULL; dp = dp->next)
> +      if ((dp->ref_from & phony) != 0)
> +        fprintf (fp, "\n%s:\n", dp->path);
> +
> +  fclose (fp);

I really have not yet inpsected the output files to make sure they
look sane; should they include any header (such as "generated by ...")
to make it obvious they were computed by a run of m4?  This is why I
insist that there be testsuite coverage.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org


_______________________________________________
M4-patches mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/m4-patches

Reply via email to