bison: add command line option to map file prefixes

Teaches bison about a new command line option, --file-prefix-map OLD=NEW
(based on the -ffile-prefix-map option from GCC) which causes it to
replace and file path of OLD in the text of the output file with NEW,
mainly for header guards and comments. The primary use of this is to
make builds reproducible with different input paths, and in particular
the debugging information produced when the source code is compiled. For
example, a distro may know that the bison source code will be located at
"/usr/src/bison" and thus can generate bison files that are reproducible
with the following command:

    bison --output=/build/bison/parse.c -d --file-prefix-map=/build/bison/=/usr/src/bison/ parse.y

Importantly, this will change the header guards and #line directives
from:

    #ifndef YY_BUILD_BISON_PARSE_H
    #line 100 "/build/bison/parse.h"

to

    #ifndef YY_USR_SRC_BISON_PARSE_H
    #line 100 "/usr/src/bison/parse.h"

which is reproducible.

See https://lists.gnu.org/r/bison-patches/2020-05/msg00016.html
Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>

* src/files.h, src/files.c (spec_mapped_header_file)
(mapped_dir_prefix, map_file_name, add_prefix_map): New.
* src/getargs.c (-M, --file-prefix-map): New option.
* src/output.c (prepare): Define b4_mapped_dir_prefix and
b4_spec_header_file.
* src/scan-skel.l (@ofile@): Output the mapped file name.
* data/skeletons/glr.c, data/skeletons/glr.cc,
* data/skeletons/lalr1.cc, data/skeletons/location.cc,
* data/skeletons/yacc.c:
Adjust.
* doc/bison.texi: Document.
* tests/input.at, tests/output.at: Check.
This commit is contained in:
Joshua Watt
2020-05-23 20:26:17 -05:00
committed by Akim Demaille
parent 6ed813c122
commit dd878d1851
14 changed files with 294 additions and 28 deletions

View File

@@ -25,6 +25,8 @@
#include <dirname.h>
#include <error.h>
#include <get-errno.h>
#include <gl_array_list.h>
#include <gl_xlist.h>
#include <quote.h>
#include <quotearg.h>
#include <relocatable.h> /* relocate2 */
@@ -55,6 +57,7 @@ char *spec_verbose_file = NULL; /* for --verbose. */
char *spec_graph_file = NULL; /* for -g. */
char *spec_xml_file = NULL; /* for -x. */
char *spec_header_file = NULL; /* for --defines. */
char *spec_mapped_header_file = NULL;
char *parser_file_name;
/* All computed output file names. */
@@ -91,11 +94,20 @@ uniqstr grammar_file = NULL;
char *all_but_ext;
static char *all_but_tab_ext;
char *dir_prefix;
char *mapped_dir_prefix;
/* C source file extension (the parser source). */
static char *src_extension = NULL;
/* Header file extension (if option '`-d'' is specified). */
static char *header_extension = NULL;
struct prefix_map
{
char *oldprefix;
char *newprefix;
};
static gl_list_t prefix_maps = NULL;
/*-----------------------------------------------------------------.
| Return a newly allocated string composed of the concatenation of |
@@ -159,6 +171,70 @@ xfdopen (int fd, char const *mode)
return res;
}
/* Given an input file path, returns a dynamically allocated string that
contains the path with the file prefix mapping rules applied, or NULL if
the input was NULL. */
char *
map_file_name (char const *filename)
{
if (!filename)
return NULL;
struct prefix_map const *p = NULL;
if (prefix_maps)
{
void const *ptr;
gl_list_iterator_t iter = gl_list_iterator (prefix_maps);
while (gl_list_iterator_next (&iter, &ptr, NULL))
{
p = ptr;
if (strncmp (p->oldprefix, filename, strlen (p->oldprefix)) == 0)
break;
p = NULL;
}
gl_list_iterator_free (&iter);
}
if (!p)
return xstrdup (filename);
size_t oldprefix_len = strlen (p->oldprefix);
size_t newprefix_len = strlen (p->newprefix);
char *s = xmalloc (newprefix_len + strlen (filename) - oldprefix_len + 1);
char *end = stpcpy (s, p->newprefix);
stpcpy (end, filename + oldprefix_len);
return s;
}
static void
prefix_map_free (struct prefix_map *p)
{
free (p->oldprefix);
free (p->newprefix);
free (p);
}
/* Adds a new file prefix mapping. If a file path starts with oldprefix, it
will be replaced with newprefix */
void
add_prefix_map(char const* oldprefix, char const* newprefix)
{
if (!prefix_maps)
prefix_maps = gl_list_create_empty (GL_ARRAY_LIST,
/* equals */ NULL,
/* hashcode */ NULL,
(gl_listelement_dispose_fn) prefix_map_free,
true);
struct prefix_map *p = xmalloc (sizeof (*p));
p->oldprefix = xstrdup (oldprefix);
p->newprefix = xstrdup (newprefix);
gl_list_add_last (prefix_maps, p);
}
/*------------------------------------------------------------------.
| Compute ALL_BUT_EXT, ALL_BUT_TAB_EXT and output files extensions. |
`------------------------------------------------------------------*/
@@ -363,6 +439,9 @@ compute_output_file_names (void)
output_file_name_check (&spec_verbose_file, false);
}
spec_mapped_header_file = map_file_name (spec_header_file);
mapped_dir_prefix = map_file_name (dir_prefix);
free (all_but_tab_ext);
free (src_extension);
free (header_extension);
@@ -449,10 +528,15 @@ output_file_names_free (void)
free (spec_graph_file);
free (spec_xml_file);
free (spec_header_file);
free (spec_mapped_header_file);
free (parser_file_name);
free (dir_prefix);
free (mapped_dir_prefix);
for (int i = 0; i < generated_files_size; i++)
free (generated_files[i].name);
free (generated_files);
free (relocate_buffer);
if (prefix_maps)
gl_list_free (prefix_maps);
}

View File

@@ -50,9 +50,15 @@ extern char *spec_xml_file;
/* File name specified with --defines. */
extern char *spec_header_file;
/* File name specified with --defines, adjusted for mapped prefixes. */
extern char *spec_mapped_header_file;
/* Directory prefix of output file names. */
extern char *dir_prefix;
/* Directory prefix of output file name, adjusted for mapped prefixes. */
extern char *mapped_dir_prefix;
/* The file name as given on the command line.
Not named "input_file" because Flex uses this name for an argument,
and therefore GCC warns about a name clash. */
@@ -85,4 +91,7 @@ FILE *xfopen (const char *name, char const *mode);
void xfclose (FILE *ptr);
FILE *xfdopen (int fd, char const *mode);
char *map_file_name (char const *filename);
void add_prefix_map(char const* oldprefix, char const* newprefix);
#endif /* !FILES_H_ */

View File

@@ -423,15 +423,17 @@ Tuning the Parser:\n\
* won't assume that -d also takes an argument. */
fputs (_("\
Output Files:\n\
--defines[=FILE] also produce a header file\n\
-d likewise but cannot specify FILE (for POSIX Yacc)\n\
-r, --report=THINGS also produce details on the automaton\n\
--report-file=FILE write report to FILE\n\
-v, --verbose same as '--report=state'\n\
-b, --file-prefix=PREFIX specify a PREFIX for output files\n\
-o, --output=FILE leave output to FILE\n\
-g, --graph[=FILE] also output a graph of the automaton\n\
-x, --xml[=FILE] also output an XML report of the automaton\n\
--defines[=FILE] also produce a header file\n\
-d likewise but cannot specify FILE (for POSIX Yacc)\n\
-r, --report=THINGS also produce details on the automaton\n\
--report-file=FILE write report to FILE\n\
-v, --verbose same as '--report=state'\n\
-b, --file-prefix=PREFIX specify a PREFIX for output files\n\
-o, --output=FILE leave output to FILE\n\
-g, --graph[=FILE] also output a graph of the automaton\n\
-x, --xml[=FILE] also output an XML report of the automaton\n\
-M, --file-prefix-map=OLD=NEW replace prefix OLD with NEW when writing file paths\n\
in output files\n\
"), stdout);
putc ('\n', stdout);
@@ -552,6 +554,7 @@ static char const short_options[] =
"h"
"k"
"l"
"M:"
"o:"
"p:"
"r:"
@@ -603,14 +606,15 @@ static struct option const long_options[] =
{ "yacc", no_argument, 0, 'y' },
/* Output Files. */
{ "defines", optional_argument, 0, 'd' },
{ "report", required_argument, 0, 'r' },
{ "report-file", required_argument, 0, REPORT_FILE_OPTION },
{ "verbose", no_argument, 0, 'v' },
{ "file-prefix", required_argument, 0, 'b' },
{ "output", required_argument, 0, 'o' },
{ "graph", optional_argument, 0, 'g' },
{ "xml", optional_argument, 0, 'x' },
{ "defines", optional_argument, 0, 'd' },
{ "report", required_argument, 0, 'r' },
{ "report-file", required_argument, 0, REPORT_FILE_OPTION },
{ "verbose", no_argument, 0, 'v' },
{ "file-prefix", required_argument, 0, 'b' },
{ "output", required_argument, 0, 'o' },
{ "graph", optional_argument, 0, 'g' },
{ "xml", optional_argument, 0, 'x' },
{ "file-prefix-map", required_argument, 0, 'M' },
/* Hidden. */
{ "fixed-output-files", no_argument, 0, FIXED_OUTPUT_FILES_OPTION },
@@ -716,6 +720,22 @@ getargs (int argc, char *argv[])
language_argmatch (optarg, command_line_prio, loc);
break;
case 'M': // -MOLDPREFIX=NEWPREFIX
{
char *newprefix = strchr (optarg, '=');
if (newprefix)
{
*newprefix = '\0';
add_prefix_map (optarg, newprefix + 1);
}
else
{
complain (&loc, complaint, _("invalid argument for %s: %s"),
quote ("--file-prefix-map"), quotearg_n (1, optarg));
}
}
break;
case 'S':
skeleton_arg (optarg, command_line_prio, loc);
break;

View File

@@ -805,8 +805,10 @@ prepare (void)
#define DEFINE(Name) MUSCLE_INSERT_STRING (#Name, Name ? Name : "")
DEFINE (dir_prefix);
DEFINE (mapped_dir_prefix);
DEFINE (parser_file_name);
DEFINE (spec_header_file);
DEFINE (spec_mapped_header_file);
DEFINE (spec_file_prefix);
DEFINE (spec_graph_file);
DEFINE (spec_name_prefix);

View File

@@ -50,6 +50,7 @@ static void at_output (int argc, char *argv[], char **name, int *lineno);
static void fail_for_at_directive_too_many_args (char const *at_directive_name);
static void fail_for_at_directive_too_few_args (char const *at_directive_name);
static void fail_for_invalid_at (char const *at);
static void output_mapped_file (char const *name);
%}
%x SC_AT_DIRECTIVE_ARGS
@@ -77,7 +78,7 @@ static void fail_for_invalid_at (char const *at);
@\n continue;
"@oline@" fprintf (yyout, "%d", out_lineno + 1);
"@ofile@" fputs (quotearg_style (c_quoting_style, out_name), yyout);
"@ofile@" output_mapped_file (out_name);
"@basename(" at_init (&argc, argv, &at_ptr, &at_basename);
"@complain(" at_init (&argc, argv, &at_ptr, &at_complain);
@@ -264,3 +265,11 @@ fail_for_invalid_at (char const *at)
{
complain (NULL, fatal, "invalid @ in skeleton: %s", at);
}
static void
output_mapped_file (char const *name)
{
char *f = map_file_name (name);
fputs (quotearg_style (c_quoting_style, f), yyout);
free (f);
}