diff mbox series

Ability to remap file names in __FILE__, etc (PR other/70268)

Message ID boris.20171117114816@codesynthesis.com
State New
Headers show
Series Ability to remap file names in __FILE__, etc (PR other/70268) | expand

Commit Message

Boris Kolpackov Nov. 17, 2017, 10:10 a.m. UTC
The below patch adds the -fmacro-prefix-map option that allows remapping
of file names in __FILE__, __BASE_FILE__, and __builtin_FILE(), similar
to how -fdebug-prefix-map allows to do the same for debug information.

Additionally, the patch adds -ffile-prefix-map which can be used to
specify both mappings with a single option (and, should we need to
add more -f*-prefix-map options in the future, those as well).

There is PR other/70268 with more details/motivation:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70268

Note that a patch has been proposed earlier by Hongxu Jia. This patch
offers a significantly different implementation though I used the
original patch as a reference to make sure I've covered all the areas
(e.g., __builtin_FILE()).

Copyright assignment is on file.

libcpp/ChangeLog:

2017-11-17  Boris Kolpackov  <boris@codesynthesis.com>

	PR other/70268
	* include/cpplib.h (cpp_callbacks::remap_filename): New callback.
	* libcpp/macro.c (_cpp_builtin_macro_text): Call remap_filename for
	__FILE__ and __BASE_FILE__.


gcc/ChangeLog:

2017-11-17  Boris Kolpackov  <boris@codesynthesis.com>

	PR other/70268
	* common.opt: (-ffile-prefix-map): New option.
        * opts.c (common_handle_option): Deffer it.
        * opts-global.c (handle_common_deferred_options): Handle it.
	* debug.h (remap_debug_filename, add_debug_prefix_map): Move to...
	* file-prefix-map.h: New file.
	(remap_debug_filename, add_debug_prefix_map): ...here.
	(add_macro_prefix_map, add_file_prefix_map, remap_macro_filename): New.
	* final.c (debug_prefix_map, add_debug_prefix_map
	remap_debug_filename): Move to...
	* file-prefix-map.c: New file.
	(file_prefix_map, add_prefix_map, remap_filename) ...here and rename,
	generalize, get rid of alloca() call.
	(add_macro_prefix_map, add_debug_prefix_map, add_file_prefix_map):
	Implement in terms of add_prefix_map().
	(remap_macro_filename, remap_debug_filename): Implement in term of
	remap_filename().
	* Makefile.in (OBJS, PLUGIN_HEADERS): Add new files.
        * builtins.c (fold_builtin_FILE): Call remap_macro_filename().
        * dbxout.c: Include file-prefix-map.h.
	* varasm.c: Likewise.
        * vmsdbgout.c: Likewise.
        * xcoffout.c: Likewise.
	* dwarf2out.c: Likewise plus omit new options from DW_AT_producer.
	* doc/cppopts.texi: Document -fmacro-prefix-map.
        * doc/invoke.texi: Document -ffile-prefix-map.


gcc/c-family/ChangeLog:

2017-11-17  Boris Kolpackov  <boris@codesynthesis.com>

	PR other/70268
	* c-family/c.opt (-fmacro-prefix-map): New option.
	* c-family/c-opts.c (c_common_handle_option): Handle it.
	* c-family/c-lex.c (init_c_lex): Set remap_filename cpp callback.
        * c-family/c-ppoutput.c (init_pp_output): Likewise.


gcc/testsuite/ChangeLog:

2017-11-17  Boris Kolpackov  <boris@codesynthesis.com>

	PR other/70268
	* c-c++-common/ffile-prefix-map.c: New test.
        * c-c++-common/fmacro-prefix-map.c: New test.
	* c-c++-common/cpp/ffile-prefix-map.c: New test.
        * c-c++-common/cpp/fmacro-prefix-map.c: New test.

Comments

David Malcolm Dec. 6, 2017, 3:58 p.m. UTC | #1
On Fri, 2017-11-17 at 12:10 +0200, Boris Kolpackov wrote:
> The below patch adds the -fmacro-prefix-map option that allows
> remapping
> of file names in __FILE__, __BASE_FILE__, and __builtin_FILE(),
> similar
> to how -fdebug-prefix-map allows to do the same for debug
> information.
> 
> Additionally, the patch adds -ffile-prefix-map which can be used to
> specify both mappings with a single option (and, should we need to
> add more -f*-prefix-map options in the future, those as well).
> 
> There is PR other/70268 with more details/motivation:
> 
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70268
> 
> Note that a patch has been proposed earlier by Hongxu Jia. This patch
> offers a significantly different implementation though I used the
> original patch as a reference to make sure I've covered all the areas
> (e.g., __builtin_FILE()).
> 
> Copyright assignment is on file.

Thanks for posting this patch.

To my naive eyes this seems like a useful addition, but I'm hoping
someone with more knowledge of the standards around the preprocessor
can comment.

Does any other compiler implement something similar?

Some notes inline follow...

> libcpp/ChangeLog:
> 
> 2017-11-17  Boris Kolpackov  <boris@codesynthesis.com>
> 
> 	PR other/70268
> 	* include/cpplib.h (cpp_callbacks::remap_filename): New
> callback.
> 	* libcpp/macro.c (_cpp_builtin_macro_text): Call remap_filename
> for
> 	__FILE__ and __BASE_FILE__.
> 
> 
> gcc/ChangeLog:
> 
> 2017-11-17  Boris Kolpackov  <boris@codesynthesis.com>
> 
> 	PR other/70268
> 	* common.opt: (-ffile-prefix-map): New option.
>         * opts.c (common_handle_option): Deffer it.

(spelling of "Defer")

>         * opts-global.c (handle_common_deferred_options): Handle it.
> 	* debug.h (remap_debug_filename, add_debug_prefix_map): Move
> to...
> 	* file-prefix-map.h: New file.
> 	(remap_debug_filename, add_debug_prefix_map): ...here.
> 	(add_macro_prefix_map, add_file_prefix_map,
> remap_macro_filename): New.
> 	* final.c (debug_prefix_map, add_debug_prefix_map
> 	remap_debug_filename): Move to...
> 	* file-prefix-map.c: New file.

IIRC, new files should have a .cc suffix.

> 	(file_prefix_map, add_prefix_map, remap_filename) ...here and
> rename,
> 	generalize, get rid of alloca() call.
> 	(add_macro_prefix_map, add_debug_prefix_map,
> add_file_prefix_map):
> 	Implement in terms of add_prefix_map().
> 	(remap_macro_filename, remap_debug_filename): Implement in term
> of
> 	remap_filename().
> 	* Makefile.in (OBJS, PLUGIN_HEADERS): Add new files.
>         * builtins.c (fold_builtin_FILE): Call
> remap_macro_filename().
>         * dbxout.c: Include file-prefix-map.h.

Are there some spaces vs tabs issues in the ChangeLog?  (it might be
just my email client though).

> 	* varasm.c: Likewise.
>         * vmsdbgout.c: Likewise.
>         * xcoffout.c: Likewise.
> 	* dwarf2out.c: Likewise plus omit new options from
> DW_AT_producer.
> 	* doc/cppopts.texi: Document -fmacro-prefix-map.
>         * doc/invoke.texi: Document -ffile-prefix-map.
> 
> 
> gcc/c-family/ChangeLog:
> 
> 2017-11-17  Boris Kolpackov  <boris@codesynthesis.com>
> 
> 	PR other/70268
> 	* c-family/c.opt (-fmacro-prefix-map): New option.
> 	* c-family/c-opts.c (c_common_handle_option): Handle it.
> 	* c-family/c-lex.c (init_c_lex): Set remap_filename cpp
> callback.
>         * c-family/c-ppoutput.c (init_pp_output): Likewise.
> 
> 
> gcc/testsuite/ChangeLog:
> 
> 2017-11-17  Boris Kolpackov  <boris@codesynthesis.com>
> 
> 	PR other/70268
> 	* c-c++-common/ffile-prefix-map.c: New test.
>         * c-c++-common/fmacro-prefix-map.c: New test.
> 	* c-c++-common/cpp/ffile-prefix-map.c: New test.
>         * c-c++-common/cpp/fmacro-prefix-map.c: New test.
> 
> 
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 5db78558c0c..ba09b747b02 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1280,6 +1280,7 @@ OBJS = \
>  	expmed.o \
>  	expr.o \
>  	fibonacci_heap.o \
> +        file-prefix-map.o \

I believe you used spaces rather than a tab here.

>  	final.o \
>  	fixed-value.o \
>  	fold-const.o \
> @@ -3486,8 +3487,8 @@ PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H)
> $(SYSTEM_H) coretypes.h $(TM_H) \
>    tree-ssa-loop-niter.h tree-ssa-ter.h tree-ssa-threadedge.h \
>    tree-ssa-threadupdate.h inchash.h wide-int.h signop.h hash-map.h \
>    hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h
> cfgcleanup.h \
> -  lcm.h cfgloopmanip.h builtins.def chkp-builtins.def pass-
> instances.def \
> -  params.list
> +  lcm.h cfgloopmanip.h file-prefix-map.h builtins.def chkp-
> builtins.def \
> +  pass-instances.def params.list
>  
>  # generate the 'build fragment' b-header-vars
>  s-header-vars: Makefile
> diff --git a/gcc/builtins.c b/gcc/builtins.c
> index 650de0d9aca..14016fe6312 100644
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -70,6 +70,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "case-cfn-macros.h"
>  #include "gimple-fold.h"
>  #include "intl.h"
> +#include "file-prefix-map.h" /* remap_macro_filename()  */
>  
>  struct target_builtins default_target_builtins;
>  #if SWITCHABLE_TARGET
> @@ -8784,7 +8785,13 @@ static inline tree
>  fold_builtin_FILE (location_t loc)
>  {
>    if (const char *fname = LOCATION_FILE (loc))
> +  {
> +    /* The documentation says this builtin is equivalent to the
> preprocessor
> +       __FILE__ macro so it appears appropriate to use the same file
> prefix
> +       mappings.  */
> +    fname = remap_macro_filename (fname);
>      return build_string_literal (strlen (fname) + 1, fname);
> +  }

Formatting nit: should be:

  if ()
    {
      body
    }

rather than:

  if ()
  {
    body
  }

What "owns" the memory returned by remap_macro_filename?

I see later on that this calls remap_filename, and hence the result is
either ggc-allocated, or is the input pointer, and hence "fname" is
temporary memory that goes away when the GC runs; build_string_literal
takes a copy.   (This is different from Hongxu Jia's patch attached to
PR 70268, which calls xstrdup *or* returns the input pointer).

>  
>    return build_string_literal (1, "");
>  }
> diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
> index 8342800303a..9d61abe545d 100644
> --- a/gcc/c-family/c-lex.c
> +++ b/gcc/c-family/c-lex.c
> @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "stor-layout.h"
>  #include "c-pragma.h"
>  #include "debug.h"
> +#include "file-prefix-map.h" /* remap_macro_filename()  */
>  
>  #include "attribs.h"
>  
> @@ -82,6 +83,7 @@ init_c_lex (void)
>    cb->has_attribute = c_common_has_attribute;
>    cb->get_source_date_epoch = cb_get_source_date_epoch;
>    cb->get_suggestion = cb_get_suggestion;
> +  cb->remap_filename = remap_macro_filename;
>  
>    /* Set the debug callbacks if we can use them.  */
>    if ((debug_info_level == DINFO_LEVEL_VERBOSE
> diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
> index cead15e7a63..04b8c6cd40a 100644
> --- a/gcc/c-family/c-opts.c
> +++ b/gcc/c-family/c-opts.c
> @@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "plugin.h"		/* For PLUGIN_INCLUDE_FILE
> event.  */
>  #include "mkdeps.h"
>  #include "dumpfile.h"
> +#include "file-prefix-map.h"    /* add_*_prefix_map()  */
>  
>  #ifndef DOLLARS_IN_IDENTIFIERS
>  # define DOLLARS_IN_IDENTIFIERS true
> @@ -448,6 +449,10 @@ c_common_handle_option (size_t scode, const char
> *arg, int value,
>        cpp_opts->dollars_in_ident = value;
>        break;
>  
> +    case OPT_fmacro_prefix_map_:
> +      add_macro_prefix_map (arg);
> +      break;
> +
>      case OPT_ffreestanding:
>        value = !value;
>        /* Fall through.  */
> diff --git a/gcc/c-family/c-ppoutput.c b/gcc/c-family/c-ppoutput.c
> index d1c92379f62..949a4955923 100644
> --- a/gcc/c-family/c-ppoutput.c
> +++ b/gcc/c-family/c-ppoutput.c
> @@ -22,6 +22,7 @@
>  #include "c-common.h"		/* For flags.  */
>  #include "../libcpp/internal.h"
>  #include "c-pragma.h"		/* For parse_in.  */
> +#include "file-prefix-map.h"    /* remap_macro_filename()  */
>  
>  /* Encapsulates state used to convert a stream of tokens into a text
>     file.  */
> @@ -151,6 +152,7 @@ init_pp_output (FILE *out_stream)
>  
>    cb->has_attribute = c_common_has_attribute;
>    cb->get_source_date_epoch = cb_get_source_date_epoch;
> +  cb->remap_filename = remap_macro_filename;
>  
>    /* Initialize the print structure.  */
>    print.src_line = 1;
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 479ae63bb0e..9c6b8113ebf 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -1386,6 +1386,10 @@ fdollars-in-identifiers
>  C ObjC C++ ObjC++
>  Permit '$' as an identifier character.
>  
> +fmacro-prefix-map=
> +C ObjC C++ ObjC++ Joined RejectNegative
> +-fmacro-prefix-map=<old>=<new> Map one directory name to another in
> __FILE__, __BASE_FILE__, and __builtin_FILE().
> +
>  fdump-ada-spec
>  C ObjC C++ ObjC++ RejectNegative Var(flag_dump_ada_spec)
>  Write all declarations as Ada code transitively.
> diff --git a/gcc/common.opt b/gcc/common.opt
> index f8f2ed3db8a..31524e1e4fd 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1175,7 +1175,11 @@ Common RejectNegative Joined
> Var(common_deferred_options) Defer
>  
>  fdebug-prefix-map=
>  Common Joined RejectNegative Var(common_deferred_options) Defer
> -Map one directory name to another in debug information.
> +-fdebug-prefix-map=<old>=<new> Map one directory name to another in
> debug information.
> +
> +ffile-prefix-map=
> +Common Joined RejectNegative Var(common_deferred_options) Defer
> +-ffile-prefix-map=<old>=<new> Map one directory name to another in
> compilation result.




>  fdebug-types-section
>  Common Report Var(flag_debug_types_section) Init(0)
> diff --git a/gcc/dbxout.c b/gcc/dbxout.c
> index 49a858339f8..e4aa16f635a 100644
> --- a/gcc/dbxout.c
> +++ b/gcc/dbxout.c
> @@ -91,6 +91,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "common/common-target.h"
>  #include "langhooks.h"
>  #include "expr.h"
> +#include "file-prefix-map.h" /* remap_debug_filename()  */
>  
>  #ifdef XCOFF_DEBUGGING_INFO
>  #include "xcoffout.h"
> diff --git a/gcc/debug.h b/gcc/debug.h
> index 277d990c20f..22fc7e7ab20 100644
> --- a/gcc/debug.h
> +++ b/gcc/debug.h
> @@ -246,9 +246,6 @@ extern bool dwarf2out_do_frame (void);
>  extern bool dwarf2out_do_cfi_asm (void);
>  extern void dwarf2out_switch_text_section (void);
>  
> -const char *remap_debug_filename (const char *);
> -void add_debug_prefix_map (const char *);
> -
>  /* For -fdump-go-spec.  */
>  
>  extern const struct gcc_debug_hooks *
> diff --git a/gcc/doc/cppopts.texi b/gcc/doc/cppopts.texi
> index 16bf22a6f38..09eb88fff97 100644
> --- a/gcc/doc/cppopts.texi
> +++ b/gcc/doc/cppopts.texi
> @@ -287,6 +287,16 @@ When this option is given no argument, the
> default parameter value is
>  
>  Note that @code{-ftrack-macro-expansion=2} is activated by default.
>  
> +@item -fmacro-prefix-map=@var{old}=@var{new}
> +@opindex fmacro-prefix-map
> +When preprocessing files in directory @file{@var{old}}, expand the
> +@code{__FILE__} and @code{__BASE_FILE__} macros as in @file{@var{new
> }}
> +instead.  This can be used to change an absolute path to a relative
> +path by using @file{.} for @var{new} which can result in more
> +reproducible builds that are location independent.  This option also
> +affects @code{__builtin_FILE()} during compilation.  See also
> +@option{-ffile-prefix-map}.

I'm not a fan of the name "-fmacro-prefix-map" as it makes me wonder
"what macros are affected?"; it doesn't immediately suggest __FILE__ to
me (and __BASE_FILE__).

I wonder if "-f__FILE__-prefix-map" is sane and implementable?  (sorry
to "bikeshed" this).

Do other compilers implement similar options?

>  @item -fexec-charset=@var{charset}
>  @opindex fexec-charset
>  @cindex character set, execution
> @@ -534,4 +544,3 @@ token in the output is preceded by the dump of
> the map its location
>  belongs to.
>  
>  When used from GCC without @option{-E}, this option has no effect.
> -
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 85c980bdfc9..a5edd9fa092 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -172,7 +172,8 @@ in the following sections.
>  @gccoptlist{-c  -S  -E  -o @var{file}  -x @var{language}  @gol
>  -v  -###  --help@r{[}=@var{class}@r{[},@dots{}@r{]]}  --target-
> help  --version @gol
>  -pass-exit-codes  -pipe  -specs=@var{file}  -wrapper  @gol
> -@@@var{file}  -fplugin=@var{file}  -fplugin-arg-@var{name}=@var{arg}
>   @gol
> +@@@var{file}  -ffile-prefix-map=@var{old}=@var{new}  @gol
> +-fplugin=@var{file}  -fplugin-arg-@var{name}=@var{arg}  @gol
>  -fdump-ada-spec@r{[}-slim@r{]}  -fada-spec-parent=@var{unit} -fdump-
> go-spec=@var{file}}
>  
>  @item C Language Options
> @@ -480,9 +481,9 @@ Objective-C and Objective-C++ Dialects}.
>  -dD  -dI  -dM  -dN  -dU @gol
>  -fdebug-cpp  -fdirectives-only  -fdollars-in-identifiers  @gol
>  -fexec-charset=@var{charset}  -fextended-identifiers  @gol
> --finput-charset=@var{charset}  -fno-canonical-system-headers @gol
> --fpch-deps  -fpch-preprocess  -fpreprocessed @gol
> --ftabstop=@var{width}  -ftrack-macro-expansion  @gol
> +-finput-charset=@var{charset} -fmacro-prefix-map=@var{old}=@var{new}
>   @gol
> +-fno-canonical-system-headers @gol -fpch-deps  -fpch-
> preprocess  @gol
> +-fpreprocessed -ftabstop=@var{width}  -ftrack-macro-expansion  @gol
>  -fwide-exec-charset=@var{charset}  -fworking-directory @gol
>  -H  -imacros @var{file}  -include @var{file} @gol
>  -M  -MD  -MF  -MG  -MM  -MMD  -MP  -MQ  -MT @gol
> @@ -1652,6 +1653,15 @@ This invokes all subprograms of @command{gcc}
> under
>  @samp{gdb --args}, thus the invocation of @command{cc1} is
>  @samp{gdb --args cc1 @dots{}}.
>  
> +@item -ffile-prefix-map=@var{old}=@var{new}
> +@opindex ffile-prefix-map
> +When compiling files in directory @file{@var{old}}, record any
> references
> +to them in the result of the compilation as in @file{@var{new}}
> instead.
> +Specifying this option is equivalent to specifying all the
> individual
> +@option{-f*-prefix-map} options. This can be used to make
> reproducible

You say "all the individual...options"; am I right in thinking there
are just two of them: -fmacro-prefix-map and -fdebug-prefix-map?

If so, wouldn't it better to say "both of the options" and list them
there.

Similar comments apply to the .opt text for this above.

> +builds that are location independent.  See also @option{-fmacro-
> prefix-map}
> +and @option{-fdebug-prefix-map}.
> +
>  @item -fplugin=@var{name}.so
>  @opindex fplugin
>  Load the plugin code in file @var{name}.so, assumed to be a
> @@ -7059,7 +7069,7 @@ used to replace a build-time path with an
> install-time path in the debug info.
>  It can also be used to change an absolute path to a relative path by
> using
>  @file{.} for @var{new}.  This can give more reproducible builds,
> which are
>  location independent, but may require an extra command to tell GDB
> where to
> -find the source files.
> +find the source files. See also @option{-ffile-prefix-map}.
>  
>  @item -fvar-tracking
>  @opindex fvar-tracking
> diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
> index 76a538f1ff9..83aec7d7f3d 100644
> --- a/gcc/dwarf2out.c
> +++ b/gcc/dwarf2out.c
> @@ -94,6 +94,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "rtl-iter.h"
>  #include "stringpool.h"
>  #include "attribs.h"
> +#include "file-prefix-map.h" /* remap_debug_filename()  */
>  
>  static void dwarf2out_source_line (unsigned int, unsigned int, const
> char *,
>  				   int, bool);
> @@ -23408,6 +23409,8 @@ gen_producer_string (void)
>        case OPT_fltrans_output_list_:
>        case OPT_fresolution_:
>        case OPT_fdebug_prefix_map_:
> +      case OPT_fmacro_prefix_map_:
> +      case OPT_ffile_prefix_map_:
>        case OPT_fcompare_debug:
>  	/* Ignore these.  */
>  	continue;
> diff --git a/gcc/file-prefix-map.c b/gcc/file-prefix-map.c
> new file mode 100644
> index 00000000000..c8b6af51d40
> --- /dev/null
> +++ b/gcc/file-prefix-map.c
> @@ -0,0 +1,127 @@
> +/* Implementation of file prefix remapping support (-f*-prefix-map
> options).
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +
> +   This program 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, or (at your option)
> any
> +   later version.
> +
> +   This program 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; see the file COPYING3.  If not see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "diagnostic.h"
> +#include "file-prefix-map.h"
> +
> +/* Structure recording the mapping from source file and directory
> names at
> +   compile time to those to be embedded in the compilation result
> (debug
> +   information, the __FILE__ macro expansion, etc).  */
> +struct file_prefix_map
> +{
> +  const char *old_prefix;
> +  const char *new_prefix;
> +  size_t old_len;
> +  size_t new_len;
> +  struct file_prefix_map *next;
> +};
> +
> +/* Record a file prefix mapping in the specified map.  ARG is the
> argument to
> +   -f*-prefix-map and must be of the form OLD=NEW.  OPT is the
> option name
> +   for diagnostics.  */
> +static void
> +add_prefix_map (file_prefix_map *&maps, const char *arg, const char
> *opt)
> +{
> +  file_prefix_map *map;
> +  const char *p;
> +
> +  p = strchr (arg, '=');
> +  if (!p)
> +    {
> +      error ("invalid argument %qs to %s", arg, opt);

I think both of these should be %qs, so that the option is quoted (the
old code in final.c didn't do that, but I think it should have).

> +      return;
> +    }
> +  map = XNEW (file_prefix_map);
> +  map->old_prefix = xstrndup (arg, p - arg);
> +  map->old_len = p - arg;
> +  p++;
> +  map->new_prefix = xstrdup (p);
> +  map->new_len = strlen (p);
> +  map->next = maps;
> +  maps = map;
> +}

Anytime I see an XNEW I wonder if this ought to be a constructor, and
the struct ought to become a class, but given that this code is
basically just moved from final.c, maybe leave that for a separate
cleanup to help preserve history.

> +/* Perform user-specified mapping of filename prefixes.  Return the
> new name
> +   corresponding to FILENAME.  */

> +static const char *
> +remap_filename (file_prefix_map *maps, const char *filename)
> +{
> +  file_prefix_map *map;
> +  char *s;
> +  const char *name;
> +  size_t name_len;
> +
> +  for (map = maps; map; map = map->next)
> +    if (filename_ncmp (filename, map->old_prefix, map->old_len) ==
> 0)
> +      break;
> +  if (!map)
> +    return filename;
> +  name = filename + map->old_len;
> +  name_len = strlen (name) + 1;
> +
> +  s = (char *) ggc_alloc_atomic (name_len + map->new_len);
> +  memcpy (s, map->new_prefix, map->new_len);
> +  memcpy (s + map->new_len, name, name_len);
> +  return s;
> +}

You've moved this code from final.c, where the memory is allocated on
stack and then copied via ggc_strdup, but with an early exit for :

> -  s = (char *) alloca (name_len + map->new_len);
> -  memcpy (s, map->new_prefix, map->new_len);
> -  memcpy (s + map->new_len, name, name_len);
> -  return ggc_strdup (s);

so I guess the ggc_alloc_atomic avoids a strlen.

Please can you add a note to the function's header documenting that the
return value is either a pointer to a GC-allocated buffer, or is
FILENAME (when no remapping occurs).


> +/* NOTE: if adding another -f*-prefix-map option then don't forget
> to
> +   ignore it in DW_AT_producer (dwarf2out.c).  */
> +
> +/* Linked lists of file_prefix_map structures.  */
> +static file_prefix_map *macro_prefix_maps; /* -fmacro-prefix-map  */
> +static file_prefix_map *debug_prefix_maps; /* -fdebug-prefix-map  */
> +
> +/* Record a file prefix mapping for -fmacro-prefix-map.  */
> +void
> +add_macro_prefix_map (const char *arg)
> +{
> +  add_prefix_map (macro_prefix_maps, arg, "-fmacro-prefix-map");
> +}
> +
> +/* Record a file prefix mapping for -fdebug-prefix-map.  */
> +void
> +add_debug_prefix_map (const char *arg)
> +{
> +  add_prefix_map (debug_prefix_maps, arg, "-fdebug-prefix-map");
> +}
> +
> +/* Record a file prefix mapping for all -f*-prefix-map.  */
> +void
> +add_file_prefix_map (const char *arg)
> +{
> +  add_prefix_map (macro_prefix_maps, arg, "-ffile-prefix-map");
> +  add_prefix_map (debug_prefix_maps, arg, "-ffile-prefix-map");
> +}
> +
> +/* Remap using -fmacro-prefix-map.  */

Please can you add a note to this comment documenting the "ownership"
of the buffer (as above).

> +const char *
> +remap_macro_filename (const char *filename)
> +{
> +  return remap_filename (macro_prefix_maps, filename);
> +}
> +
> +/* Remap using -fdebug-prefix-map.  */

Likewise.

> +const char *
> +remap_debug_filename (const char *filename)
> +{
> +  return remap_filename (debug_prefix_maps, filename);
> +}
> diff --git a/gcc/file-prefix-map.h b/gcc/file-prefix-map.h
> new file mode 100644
> index 00000000000..3309eeed646
> --- /dev/null
> +++ b/gcc/file-prefix-map.h
> @@ -0,0 +1,28 @@
> +/* Declarations for file prefix remapping support (-f*-prefix-map
> options).
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +
> +   This program 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, or (at your option)
> any
> +   later version.
> +
> +   This program 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; see the file COPYING3.  If not see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GCC_FILE_PREFIX_MAP_H
> +#define GCC_FILE_PREFIX_MAP_H
> +
> +void add_macro_prefix_map (const char *);
> +void add_debug_prefix_map (const char *);
> +void add_file_prefix_map (const char *);
> +
> +const char *remap_macro_filename (const char *);
> +const char *remap_debug_filename (const char *);
> +
> +#endif /* !GCC_FILE_PREFIX_MAP_H  */
> diff --git a/gcc/final.c b/gcc/final.c
> index fe35a36dbbf..edba0d42697 100644
> --- a/gcc/final.c
> +++ b/gcc/final.c
> @@ -1501,71 +1501,6 @@ asm_str_count (const char *templ)
>    return count;
>  }
>  
> -/* ??? This is probably the wrong place for these.  */
> -/* Structure recording the mapping from source file and directory
> -   names at compile time to those to be embedded in debug
> -   information.  */
> -struct debug_prefix_map
> -{
> -  const char *old_prefix;
> -  const char *new_prefix;
> -  size_t old_len;
> -  size_t new_len;
> -  struct debug_prefix_map *next;
> -};
> -
> -/* Linked list of such structures.  */
> -static debug_prefix_map *debug_prefix_maps;
> -
> -
> -/* Record a debug file prefix mapping.  ARG is the argument to
> -   -fdebug-prefix-map and must be of the form OLD=NEW.  */
> -
> -void
> -add_debug_prefix_map (const char *arg)
> -{
> -  debug_prefix_map *map;
> -  const char *p;
> -
> -  p = strchr (arg, '=');
> -  if (!p)
> -    {
> -      error ("invalid argument %qs to -fdebug-prefix-map", arg);
> -      return;
> -    }
> -  map = XNEW (debug_prefix_map);
> -  map->old_prefix = xstrndup (arg, p - arg);
> -  map->old_len = p - arg;
> -  p++;
> -  map->new_prefix = xstrdup (p);
> -  map->new_len = strlen (p);
> -  map->next = debug_prefix_maps;
> -  debug_prefix_maps = map;
> -}
> -
> -/* Perform user-specified mapping of debug filename
> prefixes.  Return
> -   the new name corresponding to FILENAME.  */
> -
> -const char *
> -remap_debug_filename (const char *filename)
> -{
> -  debug_prefix_map *map;
> -  char *s;
> -  const char *name;
> -  size_t name_len;
> -
> -  for (map = debug_prefix_maps; map; map = map->next)
> -    if (filename_ncmp (filename, map->old_prefix, map->old_len) ==
> 0)
> -      break;
> -  if (!map)
> -    return filename;
> -  name = filename + map->old_len;
> -  name_len = strlen (name) + 1;
> -  s = (char *) alloca (name_len + map->new_len);
> -  memcpy (s, map->new_prefix, map->new_len);
> -  memcpy (s + map->new_len, name, name_len);
> -  return ggc_strdup (s);
> -}
>  
>  /* Return true if DWARF2 debug info can be emitted for DECL.  */
>  
> diff --git a/gcc/opts-global.c b/gcc/opts-global.c
> index 343dbd3ac2c..aadc2ef880b 100644
> --- a/gcc/opts-global.c
> +++ b/gcc/opts-global.c
> @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "stringpool.h"
>  #include "attribs.h"
>  #include "asan.h"
> +#include "file-prefix-map.h" /* add_*_prefix_map()  */
>  
>  typedef const char *const_char_p; /* For DEF_VEC_P.  */
>  
> @@ -365,6 +366,10 @@ handle_common_deferred_options (void)
>  	  add_debug_prefix_map (opt->arg);
>  	  break;
>  
> +	case OPT_ffile_prefix_map_:
> +	  add_file_prefix_map (opt->arg);
> +	  break;
> +
>  	case OPT_fdump_:
>  	  if (!g->get_dumps ()->dump_switch_p (opt->arg))
>  	    error ("unrecognized command line option %<-fdump-%s%>", 
> opt->arg);
> diff --git a/gcc/opts.c b/gcc/opts.c
> index ac383d48ec1..9b9a9e2e734 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -2075,6 +2075,7 @@ common_handle_option (struct gcc_options *opts,
>        break;
>  
>      case OPT_fdebug_prefix_map_:
> +    case OPT_ffile_prefix_map_:
>        /* Deferred.  */
>        break;
>  
> diff --git a/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
> b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
> new file mode 100644
> index 00000000000..cf14de84a0d
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
> @@ -0,0 +1,5 @@
> +/* { dg-do compile } */
> +/* { dg-options "-ffile-prefix-map==FILE-PREFIX" } */

What's up with this "=="? (as opposed to "=").

Is this being interpreted as an argument of "=FILE-PREFIX" to the
option?  If so what does this mean?  (and if it's a meaningful special-
case, please can you document this in the .texi)

> +#pragma message __FILE__       /* { dg-message "FILE-PREFIX" } */
> +#pragma message __BASE_FILE__  /* { dg-message "FILE-PREFIX" } */

Please add some before/after text to the #pragma message and to the dg-
message so that the dg-message is verifying the exact value of
__FILE__, and not just a substring match.

> diff --git a/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c
> b/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c
> new file mode 100644
> index 00000000000..2618146247b
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c
> @@ -0,0 +1,5 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fmacro-prefix-map==MACRO-PREFIX" } */
> +
> +#pragma message __FILE__       /* { dg-message "MACRO-PREFIX" } */
> +#pragma message __BASE_FILE__  /* { dg-message "MACRO-PREFIX" } */

(likewise, obviously)

> diff --git a/gcc/testsuite/c-c++-common/ffile-prefix-map.c
> b/gcc/testsuite/c-c++-common/ffile-prefix-map.c
> new file mode 100644
> index 00000000000..9b22eb6f06a
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/ffile-prefix-map.c
> @@ -0,0 +1,12 @@
> +/* Test __builtin_FILE(). */
> +/* { dg-do run } */
> +/* { dg-options "-ffile-prefix-map==FILE-PREFIX" } */
> +
> +#include <stdio.h>
> +
> +int main ()
> +{
> +  printf ("%s\n", __builtin_FILE ());
> +}
> +
> +/* { dg-output "FILE-PREFIX" } */

Likewise to the printf, e.g.:

  printf ("__builtin_FILE is '%s'\n", __builtin_FILE ());

and:

/* { dg-output "__builtin_FILE is 'FILE-PREFIX'" } */

> diff --git a/gcc/testsuite/c-c++-common/fmacro-prefix-map.c
> b/gcc/testsuite/c-c++-common/fmacro-prefix-map.c
> new file mode 100644
> index 00000000000..e3dee68d8a7
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/fmacro-prefix-map.c
> @@ -0,0 +1,12 @@
> +/* Test __builtin_FILE(). */
> +/* { dg-do run } */
> +/* { dg-options "-fmacro-prefix-map==MACRO-PREFIX" } */
> +
> +#include <stdio.h>
> +
> +int main ()
> +{
> +  printf ("%s\n", __builtin_FILE ());
> +}
> +
> +/* { dg-output "MACRO-PREFIX" } */

Likewise.

> diff --git a/gcc/varasm.c b/gcc/varasm.c
> index 1b35a99d688..cac338711ee 100644
> --- a/gcc/varasm.c
> +++ b/gcc/varasm.c
> @@ -55,6 +55,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "attribs.h"
>  #include "asan.h"
>  #include "rtl-iter.h"
> +#include "file-prefix-map.h" /* remap_debug_filename()  */
>  
>  #ifdef XCOFF_DEBUGGING_INFO
>  #include "xcoffout.h"		/* Needed for external data
> declarations.  */
> diff --git a/gcc/vmsdbgout.c b/gcc/vmsdbgout.c
> index 580dd284016..a0958e0f971 100644
> --- a/gcc/vmsdbgout.c
> +++ b/gcc/vmsdbgout.c
> @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "langhooks.h"
>  #include "function.h"
>  #include "target.h"
> +#include "file-prefix-map.h" /* remap_debug_filename()  */
>  
>  /* Difference in seconds between the VMS Epoch and the Unix Epoch */
>  static const long long vms_epoch_offset = 3506716800ll;
> diff --git a/gcc/xcoffout.c b/gcc/xcoffout.c
> index cf2064d5ba5..afda2f83e82 100644
> --- a/gcc/xcoffout.c
> +++ b/gcc/xcoffout.c
> @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "varasm.h"
>  #include "output.h"
>  #include "debug.h"
> +#include "file-prefix-map.h" /* remap_debug_filename()  */
>  
>  #ifdef XCOFF_DEBUGGING_INFO
>  
> diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
> index 101b33aef48..ea3bb36fd1b 100644
> --- a/libcpp/include/cpplib.h
> +++ b/libcpp/include/cpplib.h
> @@ -622,6 +622,10 @@ struct cpp_callbacks
>       C++-style comments it does not include the terminating
> newline.  */
>    void (*comment) (cpp_reader *, source_location, const unsigned
> char *,
>  		   size_t);
> +
> +  /* Callback for filename remapping in __FILE__ and __BASE_FILE__
> macro
> +     expansions.  */
> +  const char *(*remap_filename) (const char*);
>  };
>  
>  #ifdef VMS
> diff --git a/libcpp/macro.c b/libcpp/macro.c
> index bf473eae358..35c98aaf4c9 100644
> --- a/libcpp/macro.c
> +++ b/libcpp/macro.c
> @@ -450,6 +450,8 @@ _cpp_builtin_macro_text (cpp_reader *pfile,
> cpp_hashnode *node,
>  	    if (!name)
>  	      abort ();
>  	  }
> +	if (pfile->cb.remap_filename)
> +	  name = pfile->cb.remap_filename (name);
>  	len = strlen (name);
>  	buf = _cpp_unaligned_alloc (pfile, len * 2 + 3);
>  	result = buf;

This made me nervous, since "name" is now GC-allocated (or is the old
value of "name", which IIRC is owned by the linemap).  However, the
code effectively takes a copy in:

	buf = cpp_quote_string (buf + 1, (const unsigned char *) name, len);

so that's OK and "name" falls out of scope, with the buffer eventually
to be GC-ed, which seems reasonable given this only happens for
__FILE__ and __BASE_FILE__.


Thanks for the patch; hope this is constructive
Dave
Boris Kolpackov Dec. 7, 2017, 12:29 p.m. UTC | #2
Thanks for the review. Second revision of the patch attached (also
rebased on the current trunk). Issues that are not commented on
below have been resolved as suggested.


David Malcolm <dmalcolm@redhat.com> writes:

> To my naive eyes this seems like a useful addition, but I'm hoping
> someone with more knowledge of the standards around the preprocessor
> can comment.

This would definitely be a non-standard extension to any preprocessor
standard there might be. Since it's not enabled by default I don't see
any issues though.


> Does any other compiler implement something similar?

Not that I am aware of.


> > 	* file-prefix-map.c: New file.
> 
> IIRC, new files should have a .cc suffix.

Hm, grepping changelogs for "New file" appear to contradict this. Can
someone confirm?


> What "owns" the memory returned by remap_macro_filename?
> 
> I see later on that this calls remap_filename, and hence the result is
> either ggc-allocated, or is the input pointer, and hence "fname" is
> temporary memory that goes away when the GC runs; build_string_literal
> takes a copy. (This is different from Hongxu Jia's patch attached to
> PR 70268, which calls xstrdup *or* returns the input pointer).

Yes. Do you see a problem with this or is this just a note? (I do see
a problem with Hongxu Jia's approach).


> I'm not a fan of the name "-fmacro-prefix-map" as it makes me wonder
> "what macros are affected?"; it doesn't immediately suggest __FILE__ to
> me (and __BASE_FILE__).
> 
> I wonder if "-f__FILE__-prefix-map" is sane and implementable?  (sorry
> to "bikeshed" this).

Yeah, "-f__FILE__-prefix-map" looks rather insane to me ;-).

I've spent some time picking the name. -fmacro-prefix-map is at least
consistent with (rather generic) -fdebug-prefix-map:

-fdebug-prefix-map - remap in 'debug' (information)
-fmacro-prefix-map - remap in 'macro' (expansions)


> You say "all the individual...options"; am I right in thinking there
> are just two of them: -fmacro-prefix-map and -fdebug-prefix-map?
> 
> If so, wouldn't it better to say "both of the options" and list them
> there.

While currently there are two, it is plausible there could be more in
the future. Specific areas that could require this:

1. C++ source_location:

   http://en.cppreference.com/w/cpp/experimental/source_location

2. C++ Modules TS support (that Nathan is working on) involves
   binary module interfaces that could embed file references.
   
This is also the reason for -ffile-prefix-map which will cover all
future such options.


> > +      error ("invalid argument %qs to %s", arg, opt);
> 
> I think both of these should be %qs, so that the option is quoted (the
> old code in final.c didn't do that, but I think it should have).

I personally disagree since an option like -ffile-prefix-map is not
easy to confuse with a language word. But I defer to you judgment.


> > +  s = (char *) ggc_alloc_atomic (name_len + map->new_len);
> > +  memcpy (s, map->new_prefix, map->new_len);
> > +  memcpy (s + map->new_len, name, name_len);
> > +  return s;
> > +}
> 
> You've moved this code from final.c, where the memory is allocated on
> stack and then copied via ggc_strdup, but with an early exit for :
> 
> > -  s = (char *) alloca (name_len + map->new_len);
> > -  memcpy (s, map->new_prefix, map->new_len);
> > -  memcpy (s + map->new_len, name, name_len);
> > -  return ggc_strdup (s);
> 
> so I guess the ggc_alloc_atomic avoids a strlen.

My code avoids the call to alloca() (which was an issue raised with
Hongxu Jia's patch). ggc_strdup() is implemented in terms of
ggc_alloc_atomic().


> > diff --git a/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
> > b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
> > new file mode 100644
> > index 00000000000..cf14de84a0d
> > --- /dev/null
> > +++ b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
> > @@ -0,0 +1,5 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-ffile-prefix-map==FILE-PREFIX" } */
> 
> What's up with this "=="? (as opposed to "=").

Since I cannot predict the actual path, I am remapping empty prefix
to FILE-PREFIX which effectively adds FILE-PREFIX to any path.


> Is this being interpreted as an argument of "=FILE-PREFIX" to the
> option?  If so what does this mean?  (and if it's a meaningful special-
> case, please can you document this in the .texi)

It's not really a special case (an empty prefix is a prefix of any path)
and is not very useful in practice. Are you sure it's a good idea to have
this noise seeing that I will have to do it for all three options?


> > +#pragma message __FILE__       /* { dg-message "FILE-PREFIX" } */
> > +#pragma message __BASE_FILE__  /* { dg-message "FILE-PREFIX" } */
> 
> Please add some before/after text to the #pragma message and to the dg-
> message so that the dg-message is verifying the exact value of
> __FILE__, and not just a substring match.

I've added a prefix but I am not sure there is much value in suffix
since I cannot predict the exact tail of __FILE__.

Revision 2 of the patch:

libcpp/ChangeLog:

2017-12-07  Boris Kolpackov  <boris@codesynthesis.com>

        PR other/70268
        * include/cpplib.h (cpp_callbacks::remap_filename): New callback.
        * libcpp/macro.c (_cpp_builtin_macro_text): Call remap_filename for
        __FILE__ and __BASE_FILE__.


gcc/ChangeLog:

2017-12-07  Boris Kolpackov  <boris@codesynthesis.com>

        PR other/70268
        * common.opt: (-ffile-prefix-map): New option.
        * opts.c (common_handle_option): Defer it.
        * opts-global.c (handle_common_deferred_options): Handle it.
        * debug.h (remap_debug_filename, add_debug_prefix_map): Move to...
        * file-prefix-map.h: New file.
        (remap_debug_filename, add_debug_prefix_map): ...here.
        (add_macro_prefix_map, add_file_prefix_map, remap_macro_filename): New.
        * final.c (debug_prefix_map, add_debug_prefix_map
        remap_debug_filename): Move to...
        * file-prefix-map.c: New file.
        (file_prefix_map, add_prefix_map, remap_filename) ...here and rename,
        generalize, get rid of alloca() call.
        (add_macro_prefix_map, add_debug_prefix_map, add_file_prefix_map):
        Implement in terms of add_prefix_map().
        (remap_macro_filename, remap_debug_filename): Implement in term of
        remap_filename().
        * Makefile.in (OBJS, PLUGIN_HEADERS): Add new files.
        * builtins.c (fold_builtin_FILE): Call remap_macro_filename().
        * dbxout.c: Include file-prefix-map.h.
        * varasm.c: Likewise.
        * vmsdbgout.c: Likewise.
        * xcoffout.c: Likewise.
        * dwarf2out.c: Likewise plus omit new options from DW_AT_producer.
        * doc/cppopts.texi: Document -fmacro-prefix-map.
        * doc/invoke.texi: Document -ffile-prefix-map.


gcc/c-family/ChangeLog:

2017-12-07  Boris Kolpackov  <boris@codesynthesis.com>

        PR other/70268
        * c-family/c.opt (-fmacro-prefix-map): New option.
        * c-family/c-opts.c (c_common_handle_option): Handle it.
        * c-family/c-lex.c (init_c_lex): Set remap_filename cpp callback.
        * c-family/c-ppoutput.c (init_pp_output): Likewise.


gcc/testsuite/ChangeLog:

2017-12-07  Boris Kolpackov  <boris@codesynthesis.com>

        PR other/70268
        * c-c++-common/ffile-prefix-map.c: New test.
        * c-c++-common/fmacro-prefix-map.c: New test.
        * c-c++-common/cpp/ffile-prefix-map.c: New test.
        * c-c++-common/cpp/fmacro-prefix-map.c: New test.


diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 6874f94e7f6..3237bf289df 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1284,6 +1284,7 @@ OBJS = \
 	expmed.o \
 	expr.o \
 	fibonacci_heap.o \
+	file-prefix-map.o \
 	final.o \
 	fixed-value.o \
 	fold-const.o \
@@ -3498,8 +3499,8 @@ PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
   tree-ssa-loop-niter.h tree-ssa-ter.h tree-ssa-threadedge.h \
   tree-ssa-threadupdate.h inchash.h wide-int.h signop.h hash-map.h \
   hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h cfgcleanup.h \
-  lcm.h cfgloopmanip.h builtins.def chkp-builtins.def pass-instances.def \
-  params.list
+  lcm.h cfgloopmanip.h file-prefix-map.h builtins.def chkp-builtins.def \
+  pass-instances.def params.list
 
 # generate the 'build fragment' b-header-vars
 s-header-vars: Makefile
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 6b25253950c..65f5c43d770 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -69,6 +69,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "case-cfn-macros.h"
 #include "gimple-fold.h"
 #include "intl.h"
+#include "file-prefix-map.h" /* remap_macro_filename()  */
 
 struct target_builtins default_target_builtins;
 #if SWITCHABLE_TARGET
@@ -8776,7 +8777,13 @@ static inline tree
 fold_builtin_FILE (location_t loc)
 {
   if (const char *fname = LOCATION_FILE (loc))
+    {
+      /* The documentation says this builtin is equivalent to the preprocessor
+	 __FILE__ macro so it appears appropriate to use the same file prefix
+	 mappings.  */
+      fname = remap_macro_filename (fname);
       return build_string_literal (strlen (fname) + 1, fname);
+    }
 
   return build_string_literal (1, "");
 }
diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index 8342800303a..9d61abe545d 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "c-pragma.h"
 #include "debug.h"
+#include "file-prefix-map.h" /* remap_macro_filename()  */
 
 #include "attribs.h"
 
@@ -82,6 +83,7 @@ init_c_lex (void)
   cb->has_attribute = c_common_has_attribute;
   cb->get_source_date_epoch = cb_get_source_date_epoch;
   cb->get_suggestion = cb_get_suggestion;
+  cb->remap_filename = remap_macro_filename;
 
   /* Set the debug callbacks if we can use them.  */
   if ((debug_info_level == DINFO_LEVEL_VERBOSE
diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index cead15e7a63..04b8c6cd40a 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"		/* For PLUGIN_INCLUDE_FILE event.  */
 #include "mkdeps.h"
 #include "dumpfile.h"
+#include "file-prefix-map.h"    /* add_*_prefix_map()  */
 
 #ifndef DOLLARS_IN_IDENTIFIERS
 # define DOLLARS_IN_IDENTIFIERS true
@@ -448,6 +449,10 @@ c_common_handle_option (size_t scode, const char *arg, int value,
       cpp_opts->dollars_in_ident = value;
       break;
 
+    case OPT_fmacro_prefix_map_:
+      add_macro_prefix_map (arg);
+      break;
+
     case OPT_ffreestanding:
       value = !value;
       /* Fall through.  */
diff --git a/gcc/c-family/c-ppoutput.c b/gcc/c-family/c-ppoutput.c
index d1c92379f62..949a4955923 100644
--- a/gcc/c-family/c-ppoutput.c
+++ b/gcc/c-family/c-ppoutput.c
@@ -22,6 +22,7 @@
 #include "c-common.h"		/* For flags.  */
 #include "../libcpp/internal.h"
 #include "c-pragma.h"		/* For parse_in.  */
+#include "file-prefix-map.h"    /* remap_macro_filename()  */
 
 /* Encapsulates state used to convert a stream of tokens into a text
    file.  */
@@ -151,6 +152,7 @@ init_pp_output (FILE *out_stream)
 
   cb->has_attribute = c_common_has_attribute;
   cb->get_source_date_epoch = cb_get_source_date_epoch;
+  cb->remap_filename = remap_macro_filename;
 
   /* Initialize the print structure.  */
   print.src_line = 1;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index b2548105736..8515364997f 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1386,6 +1386,10 @@ fdollars-in-identifiers
 C ObjC C++ ObjC++
 Permit '$' as an identifier character.
 
+fmacro-prefix-map=
+C ObjC C++ ObjC++ Joined RejectNegative
+-fmacro-prefix-map=<old>=<new> Map one directory name to another in __FILE__, __BASE_FILE__, and __builtin_FILE().
+
 fdump-ada-spec
 C ObjC C++ ObjC++ RejectNegative Var(flag_dump_ada_spec)
 Write all declarations as Ada code transitively.
diff --git a/gcc/common.opt b/gcc/common.opt
index ffcbf850216..08e94aba140 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1175,7 +1175,11 @@ Common RejectNegative Joined Var(common_deferred_options) Defer
 
 fdebug-prefix-map=
 Common Joined RejectNegative Var(common_deferred_options) Defer
-Map one directory name to another in debug information.
+-fdebug-prefix-map=<old>=<new> Map one directory name to another in debug information.
+
+ffile-prefix-map=
+Common Joined RejectNegative Var(common_deferred_options) Defer
+-ffile-prefix-map=<old>=<new> Map one directory name to another in compilation result.
 
 fdebug-types-section
 Common Report Var(flag_debug_types_section) Init(0)
diff --git a/gcc/dbxout.c b/gcc/dbxout.c
index 290f11b3c46..fbb6a5df6c2 100644
--- a/gcc/dbxout.c
+++ b/gcc/dbxout.c
@@ -91,6 +91,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "common/common-target.h"
 #include "langhooks.h"
 #include "expr.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 #ifdef XCOFF_DEBUGGING_INFO
 #include "xcoffout.h"
diff --git a/gcc/debug.h b/gcc/debug.h
index 277d990c20f..22fc7e7ab20 100644
--- a/gcc/debug.h
+++ b/gcc/debug.h
@@ -246,9 +246,6 @@ extern bool dwarf2out_do_frame (void);
 extern bool dwarf2out_do_cfi_asm (void);
 extern void dwarf2out_switch_text_section (void);
 
-const char *remap_debug_filename (const char *);
-void add_debug_prefix_map (const char *);
-
 /* For -fdump-go-spec.  */
 
 extern const struct gcc_debug_hooks *
diff --git a/gcc/doc/cppopts.texi b/gcc/doc/cppopts.texi
index 16bf22a6f38..09eb88fff97 100644
--- a/gcc/doc/cppopts.texi
+++ b/gcc/doc/cppopts.texi
@@ -287,6 +287,16 @@ When this option is given no argument, the default parameter value is
 
 Note that @code{-ftrack-macro-expansion=2} is activated by default.
 
+@item -fmacro-prefix-map=@var{old}=@var{new}
+@opindex fmacro-prefix-map
+When preprocessing files in directory @file{@var{old}}, expand the
+@code{__FILE__} and @code{__BASE_FILE__} macros as in @file{@var{new}}
+instead.  This can be used to change an absolute path to a relative
+path by using @file{.} for @var{new} which can result in more
+reproducible builds that are location independent.  This option also
+affects @code{__builtin_FILE()} during compilation.  See also
+@option{-ffile-prefix-map}.
+
 @item -fexec-charset=@var{charset}
 @opindex fexec-charset
 @cindex character set, execution
@@ -534,4 +544,3 @@ token in the output is preceded by the dump of the map its location
 belongs to.
 
 When used from GCC without @option{-E}, this option has no effect.
-
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 3487a62ab28..eb6a6a5ced5 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -172,7 +172,8 @@ in the following sections.
 @gccoptlist{-c  -S  -E  -o @var{file}  -x @var{language}  @gol
 -v  -###  --help@r{[}=@var{class}@r{[},@dots{}@r{]]}  --target-help  --version @gol
 -pass-exit-codes  -pipe  -specs=@var{file}  -wrapper  @gol
-@@@var{file}  -fplugin=@var{file}  -fplugin-arg-@var{name}=@var{arg}  @gol
+@@@var{file}  -ffile-prefix-map=@var{old}=@var{new}  @gol
+-fplugin=@var{file}  -fplugin-arg-@var{name}=@var{arg}  @gol
 -fdump-ada-spec@r{[}-slim@r{]}  -fada-spec-parent=@var{unit} -fdump-go-spec=@var{file}}
 
 @item C Language Options
@@ -480,9 +481,9 @@ Objective-C and Objective-C++ Dialects}.
 -dD  -dI  -dM  -dN  -dU @gol
 -fdebug-cpp  -fdirectives-only  -fdollars-in-identifiers  @gol
 -fexec-charset=@var{charset}  -fextended-identifiers  @gol
--finput-charset=@var{charset}  -fno-canonical-system-headers @gol
--fpch-deps  -fpch-preprocess  -fpreprocessed @gol
--ftabstop=@var{width}  -ftrack-macro-expansion  @gol
+-finput-charset=@var{charset} -fmacro-prefix-map=@var{old}=@var{new}  @gol
+-fno-canonical-system-headers @gol -fpch-deps  -fpch-preprocess  @gol
+-fpreprocessed -ftabstop=@var{width}  -ftrack-macro-expansion  @gol
 -fwide-exec-charset=@var{charset}  -fworking-directory @gol
 -H  -imacros @var{file}  -include @var{file} @gol
 -M  -MD  -MF  -MG  -MM  -MMD  -MP  -MQ  -MT @gol
@@ -1651,6 +1652,15 @@ This invokes all subprograms of @command{gcc} under
 @samp{gdb --args}, thus the invocation of @command{cc1} is
 @samp{gdb --args cc1 @dots{}}.
 
+@item -ffile-prefix-map=@var{old}=@var{new}
+@opindex ffile-prefix-map
+When compiling files in directory @file{@var{old}}, record any references
+to them in the result of the compilation as in @file{@var{new}} instead.
+Specifying this option is equivalent to specifying all the individual
+@option{-f*-prefix-map} options. This can be used to make reproducible
+builds that are location independent.  See also @option{-fmacro-prefix-map}
+and @option{-fdebug-prefix-map}.
+
 @item -fplugin=@var{name}.so
 @opindex fplugin
 Load the plugin code in file @var{name}.so, assumed to be a
@@ -7060,7 +7070,7 @@ used to replace a build-time path with an install-time path in the debug info.
 It can also be used to change an absolute path to a relative path by using
 @file{.} for @var{new}.  This can give more reproducible builds, which are
 location independent, but may require an extra command to tell GDB where to
-find the source files.
+find the source files. See also @option{-ffile-prefix-map}.
 
 @item -fvar-tracking
 @opindex fvar-tracking
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index ae3d962526c..9d1297a087f 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -94,6 +94,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl-iter.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 static void dwarf2out_source_line (unsigned int, unsigned int, const char *,
 				   int, bool);
@@ -23417,6 +23418,8 @@ gen_producer_string (void)
       case OPT_fltrans_output_list_:
       case OPT_fresolution_:
       case OPT_fdebug_prefix_map_:
+      case OPT_fmacro_prefix_map_:
+      case OPT_ffile_prefix_map_:
       case OPT_fcompare_debug:
 	/* Ignore these.  */
 	continue;
diff --git a/gcc/file-prefix-map.c b/gcc/file-prefix-map.c
new file mode 100644
index 00000000000..563546b8632
--- /dev/null
+++ b/gcc/file-prefix-map.c
@@ -0,0 +1,130 @@
+/* Implementation of file prefix remapping support (-f*-prefix-map options).
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This program 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, or (at your option) any
+   later version.
+
+   This program 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; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "diagnostic.h"
+#include "file-prefix-map.h"
+
+/* Structure recording the mapping from source file and directory names at
+   compile time to those to be embedded in the compilation result (debug
+   information, the __FILE__ macro expansion, etc).  */
+struct file_prefix_map
+{
+  const char *old_prefix;
+  const char *new_prefix;
+  size_t old_len;
+  size_t new_len;
+  struct file_prefix_map *next;
+};
+
+/* Record a file prefix mapping in the specified map.  ARG is the argument to
+   -f*-prefix-map and must be of the form OLD=NEW.  OPT is the option name
+   for diagnostics.  */
+static void
+add_prefix_map (file_prefix_map *&maps, const char *arg, const char *opt)
+{
+  file_prefix_map *map;
+  const char *p;
+
+  p = strchr (arg, '=');
+  if (!p)
+    {
+      error ("invalid argument %qs to %qs", arg, opt);
+      return;
+    }
+  map = XNEW (file_prefix_map);
+  map->old_prefix = xstrndup (arg, p - arg);
+  map->old_len = p - arg;
+  p++;
+  map->new_prefix = xstrdup (p);
+  map->new_len = strlen (p);
+  map->next = maps;
+  maps = map;
+}
+
+/* Perform user-specified mapping of filename prefixes.  Return the
+   GC-allocated new name corresponding to FILENAME or FILENAME if no
+   remapping was performed.  */
+
+static const char *
+remap_filename (file_prefix_map *maps, const char *filename)
+{
+  file_prefix_map *map;
+  char *s;
+  const char *name;
+  size_t name_len;
+
+  for (map = maps; map; map = map->next)
+    if (filename_ncmp (filename, map->old_prefix, map->old_len) == 0)
+      break;
+  if (!map)
+    return filename;
+  name = filename + map->old_len;
+  name_len = strlen (name) + 1;
+
+  s = (char *) ggc_alloc_atomic (name_len + map->new_len);
+  memcpy (s, map->new_prefix, map->new_len);
+  memcpy (s + map->new_len, name, name_len);
+  return s;
+}
+
+/* NOTE: if adding another -f*-prefix-map option then don't forget to
+   ignore it in DW_AT_producer (dwarf2out.c).  */
+
+/* Linked lists of file_prefix_map structures.  */
+static file_prefix_map *macro_prefix_maps; /* -fmacro-prefix-map  */
+static file_prefix_map *debug_prefix_maps; /* -fdebug-prefix-map  */
+
+/* Record a file prefix mapping for -fmacro-prefix-map.  */
+void
+add_macro_prefix_map (const char *arg)
+{
+  add_prefix_map (macro_prefix_maps, arg, "-fmacro-prefix-map");
+}
+
+/* Record a file prefix mapping for -fdebug-prefix-map.  */
+void
+add_debug_prefix_map (const char *arg)
+{
+  add_prefix_map (debug_prefix_maps, arg, "-fdebug-prefix-map");
+}
+
+/* Record a file prefix mapping for all -f*-prefix-map.  */
+void
+add_file_prefix_map (const char *arg)
+{
+  add_prefix_map (macro_prefix_maps, arg, "-ffile-prefix-map");
+  add_prefix_map (debug_prefix_maps, arg, "-ffile-prefix-map");
+}
+
+/* Remap using -fmacro-prefix-map.  Return the GC-allocated new name
+   corresponding to FILENAME or FILENAME if no remapping was performed.  */
+const char *
+remap_macro_filename (const char *filename)
+{
+  return remap_filename (macro_prefix_maps, filename);
+}
+
+/* Remap using -fdebug-prefix-map.  Return the GC-allocated new name
+   corresponding to FILENAME or FILENAME if no remapping was performed.  */
+const char *
+remap_debug_filename (const char *filename)
+{
+  return remap_filename (debug_prefix_maps, filename);
+}
diff --git a/gcc/file-prefix-map.h b/gcc/file-prefix-map.h
new file mode 100644
index 00000000000..3309eeed646
--- /dev/null
+++ b/gcc/file-prefix-map.h
@@ -0,0 +1,28 @@
+/* Declarations for file prefix remapping support (-f*-prefix-map options).
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This program 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, or (at your option) any
+   later version.
+
+   This program 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; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_FILE_PREFIX_MAP_H
+#define GCC_FILE_PREFIX_MAP_H
+
+void add_macro_prefix_map (const char *);
+void add_debug_prefix_map (const char *);
+void add_file_prefix_map (const char *);
+
+const char *remap_macro_filename (const char *);
+const char *remap_debug_filename (const char *);
+
+#endif /* !GCC_FILE_PREFIX_MAP_H  */
diff --git a/gcc/final.c b/gcc/final.c
index afb6906997d..c9bd26059f2 100644
--- a/gcc/final.c
+++ b/gcc/final.c
@@ -1507,71 +1507,6 @@ asm_str_count (const char *templ)
   return count;
 }
 
-/* ??? This is probably the wrong place for these.  */
-/* Structure recording the mapping from source file and directory
-   names at compile time to those to be embedded in debug
-   information.  */
-struct debug_prefix_map
-{
-  const char *old_prefix;
-  const char *new_prefix;
-  size_t old_len;
-  size_t new_len;
-  struct debug_prefix_map *next;
-};
-
-/* Linked list of such structures.  */
-static debug_prefix_map *debug_prefix_maps;
-
-
-/* Record a debug file prefix mapping.  ARG is the argument to
-   -fdebug-prefix-map and must be of the form OLD=NEW.  */
-
-void
-add_debug_prefix_map (const char *arg)
-{
-  debug_prefix_map *map;
-  const char *p;
-
-  p = strchr (arg, '=');
-  if (!p)
-    {
-      error ("invalid argument %qs to -fdebug-prefix-map", arg);
-      return;
-    }
-  map = XNEW (debug_prefix_map);
-  map->old_prefix = xstrndup (arg, p - arg);
-  map->old_len = p - arg;
-  p++;
-  map->new_prefix = xstrdup (p);
-  map->new_len = strlen (p);
-  map->next = debug_prefix_maps;
-  debug_prefix_maps = map;
-}
-
-/* Perform user-specified mapping of debug filename prefixes.  Return
-   the new name corresponding to FILENAME.  */
-
-const char *
-remap_debug_filename (const char *filename)
-{
-  debug_prefix_map *map;
-  char *s;
-  const char *name;
-  size_t name_len;
-
-  for (map = debug_prefix_maps; map; map = map->next)
-    if (filename_ncmp (filename, map->old_prefix, map->old_len) == 0)
-      break;
-  if (!map)
-    return filename;
-  name = filename + map->old_len;
-  name_len = strlen (name) + 1;
-  s = (char *) alloca (name_len + map->new_len);
-  memcpy (s, map->new_prefix, map->new_len);
-  memcpy (s + map->new_len, name, name_len);
-  return ggc_strdup (s);
-}
 
 /* Return true if DWARF2 debug info can be emitted for DECL.  */
 
diff --git a/gcc/opts-global.c b/gcc/opts-global.c
index 343dbd3ac2c..aadc2ef880b 100644
--- a/gcc/opts-global.c
+++ b/gcc/opts-global.c
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "file-prefix-map.h" /* add_*_prefix_map()  */
 
 typedef const char *const_char_p; /* For DEF_VEC_P.  */
 
@@ -365,6 +366,10 @@ handle_common_deferred_options (void)
 	  add_debug_prefix_map (opt->arg);
 	  break;
 
+	case OPT_ffile_prefix_map_:
+	  add_file_prefix_map (opt->arg);
+	  break;
+
 	case OPT_fdump_:
 	  if (!g->get_dumps ()->dump_switch_p (opt->arg))
 	    error ("unrecognized command line option %<-fdump-%s%>", opt->arg);
diff --git a/gcc/opts.c b/gcc/opts.c
index 17579e72373..8c8ad3e06a5 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -2091,6 +2091,7 @@ common_handle_option (struct gcc_options *opts,
       break;
 
     case OPT_fdebug_prefix_map_:
+    case OPT_ffile_prefix_map_:
       /* Deferred.  */
       break;
 
diff --git a/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
new file mode 100644
index 00000000000..294875b717d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+/* { dg-options "-ffile-prefix-map==FILE-PREFIX" } */
+
+#pragma message "FILE starts with " __FILE__       /* { dg-message "FILE starts with FILE-PREFIX" } */
+#pragma message "BASE_FILE starts with " __BASE_FILE__  /* { dg-message "BASE_FILE starts with FILE-PREFIX" } */
diff --git a/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c b/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c
new file mode 100644
index 00000000000..43dc4bb287e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+/* { dg-options "-fmacro-prefix-map==MACRO-PREFIX" } */
+
+#pragma message "FILE starts with " __FILE__       /* { dg-message "FILE starts with MACRO-PREFIX" } */
+#pragma message "BASE_FILE starts with " __BASE_FILE__  /* { dg-message "BASE_FILE starts with MACRO-PREFIX" } */
diff --git a/gcc/testsuite/c-c++-common/ffile-prefix-map.c b/gcc/testsuite/c-c++-common/ffile-prefix-map.c
new file mode 100644
index 00000000000..88c29ca829b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ffile-prefix-map.c
@@ -0,0 +1,12 @@
+/* Test __builtin_FILE(). */
+/* { dg-do run } */
+/* { dg-options "-ffile-prefix-map==FILE-PREFIX" } */
+
+#include <stdio.h>
+
+int main ()
+{
+  printf ("__builtin_FILE starts with %s\n", __builtin_FILE ());
+}
+
+/* { dg-output "__builtin_FILE starts with FILE-PREFIX" } */
diff --git a/gcc/testsuite/c-c++-common/fmacro-prefix-map.c b/gcc/testsuite/c-c++-common/fmacro-prefix-map.c
new file mode 100644
index 00000000000..db51587cb4f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fmacro-prefix-map.c
@@ -0,0 +1,12 @@
+/* Test __builtin_FILE(). */
+/* { dg-do run } */
+/* { dg-options "-fmacro-prefix-map==MACRO-PREFIX" } */
+
+#include <stdio.h>
+
+int main ()
+{
+  printf ("__builtin_FILE starts with %s\n", __builtin_FILE ());
+}
+
+/* { dg-output "__builtin_FILE starts with MACRO-PREFIX" } */
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 392ac443f14..34d225a96cb 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -55,6 +55,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "asan.h"
 #include "rtl-iter.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 #ifdef XCOFF_DEBUGGING_INFO
 #include "xcoffout.h"		/* Needed for external data declarations.  */
diff --git a/gcc/vmsdbgout.c b/gcc/vmsdbgout.c
index 91dcd2e3910..908ab9aa849 100644
--- a/gcc/vmsdbgout.c
+++ b/gcc/vmsdbgout.c
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "function.h"
 #include "target.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 /* Difference in seconds between the VMS Epoch and the Unix Epoch */
 static const long long vms_epoch_offset = 3506716800ll;
diff --git a/gcc/xcoffout.c b/gcc/xcoffout.c
index cf2064d5ba5..afda2f83e82 100644
--- a/gcc/xcoffout.c
+++ b/gcc/xcoffout.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "output.h"
 #include "debug.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 #ifdef XCOFF_DEBUGGING_INFO
 
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 4d04a48a0e4..a4e8ac6f8d3 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -622,6 +622,10 @@ struct cpp_callbacks
      C++-style comments it does not include the terminating newline.  */
   void (*comment) (cpp_reader *, source_location, const unsigned char *,
 		   size_t);
+
+  /* Callback for filename remapping in __FILE__ and __BASE_FILE__ macro
+     expansions.  */
+  const char *(*remap_filename) (const char*);
 };
 
 #ifdef VMS
diff --git a/libcpp/macro.c b/libcpp/macro.c
index 791817af8db..46407b6425e 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -450,6 +450,8 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node,
 	    if (!name)
 	      abort ();
 	  }
+	if (pfile->cb.remap_filename)
+	  name = pfile->cb.remap_filename (name);
 	len = strlen (name);
 	buf = _cpp_unaligned_alloc (pfile, len * 2 + 3);
 	result = buf;
Martin Sebor Dec. 7, 2017, 5:33 p.m. UTC | #3
On 12/07/2017 05:29 AM, Boris Kolpackov wrote:
> Thanks for the review. Second revision of the patch attached (also
> rebased on the current trunk). Issues that are not commented on
> below have been resolved as suggested.
>
>
> David Malcolm <dmalcolm@redhat.com> writes:
>
>> To my naive eyes this seems like a useful addition, but I'm hoping
>> someone with more knowledge of the standards around the preprocessor
>> can comment.
>
> This would definitely be a non-standard extension to any preprocessor
> standard there might be. Since it's not enabled by default I don't see
> any issues though.

FWIW, I agree, though the final word is of course Joseph and
Marek's as the C front end maintainers.

>>> +      error ("invalid argument %qs to %s", arg, opt);
>>
>> I think both of these should be %qs, so that the option is quoted (the
>> old code in final.c didn't do that, but I think it should have).
>
> I personally disagree since an option like -ffile-prefix-map is not
> easy to confuse with a language word. But I defer to you judgment.

We have been making an effort to add quoting around options
in diagnostics.  There are still many unquoted options left
but the (so far only informally agreed upon, AFAIK) goal is
to eventually quote them all.

I think adding options to the list in the Quoting section on
the DiagnosticsGuidelines Wiki would be appropriate.  Do you
agree, David?

>>> diff --git a/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
>>> b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
>>> new file mode 100644
>>> index 00000000000..cf14de84a0d
>>> --- /dev/null
>>> +++ b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
>>> @@ -0,0 +1,5 @@
>>> +/* { dg-do compile } */
>>> +/* { dg-options "-ffile-prefix-map==FILE-PREFIX" } */
>>
>> What's up with this "=="? (as opposed to "=").
>
> Since I cannot predict the actual path, I am remapping empty prefix
> to FILE-PREFIX which effectively adds FILE-PREFIX to any path.

IIUC, it's the same as "-ffile-prefix-map=/=/FILE-PREFIX", correct?
That seems useful to me, but I'm not sure it's as useful as it
could or might need to be in some cases.  E.g., if sources for
nightly builds are downloaded into a temporary directory with some
random name, this prepends a known prefix to the directory but it
doesn't make the whole path-name determinate.  Do you think it
would it be useful to add support for globbing, as a separate
enhancement?)

> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1175,7 +1175,11 @@ Common RejectNegative Joined Var(common_deferred_options) Defer
>
>  fdebug-prefix-map=
>  Common Joined RejectNegative Var(common_deferred_options) Defer
> -Map one directory name to another in debug information.
> +-fdebug-prefix-map=<old>=<new> Map one directory name to another in debug information.
> +
> +ffile-prefix-map=
> +Common Joined RejectNegative Var(common_deferred_options) Defer

AFAIK, RejectNegative applies to options that take integer arguments.
This one takes a string so it probably doesn't belong here.

> --- a/gcc/doc/cppopts.texi
> +++ b/gcc/doc/cppopts.texi
> @@ -287,6 +287,16 @@ When this option is given no argument, the default parameter value is
>
>  Note that @code{-ftrack-macro-expansion=2} is activated by default.
>
> +@item -fmacro-prefix-map=@var{old}=@var{new}
> +@opindex fmacro-prefix-map
> +When preprocessing files in directory @file{@var{old}}, expand the
> +@code{__FILE__} and @code{__BASE_FILE__} macros as in @file{@var{new}}
> +instead.

This could be just me, but to my eyes this sentence can be read
one of two ways: (1) files residing in directory old, or (2)
the current working directory is old while preprocessing some
files.  To make it 100% clear which is meant, I would find it
more accurate if it were phrased like this instead:

   When preprocessing files residing in directory @file{@var{old}},
   expand the @code{__FILE__} and @code{__BASE_FILE__} macros as if
   the files resided in directory @file{@var{new}} instead.

Ditto for the other options.  (I assume you chose the wording to
be consistent with the existing -fdebug-prefix-map=old=new option.
I'd find the documentation for that option clearer if it were
reworded as well.)

Martin
Boris Kolpackov Dec. 9, 2017, 1:38 p.m. UTC | #4
Thanks for the review. Third revision of the patch attached.

Martin Sebor <msebor@gmail.com> writes:

> >>>+/* { dg-options "-ffile-prefix-map==FILE-PREFIX" } */
> >>
> >>What's up with this "=="? (as opposed to "=").
> >
> >Since I cannot predict the actual path, I am remapping empty prefix
> >to FILE-PREFIX which effectively adds FILE-PREFIX to any path.
> 
> IIUC, it's the same as "-ffile-prefix-map=/=/FILE-PREFIX", correct?

It is (probably) correct in this particular case. More generally,
this won't hold if you are on a non-POSIX platform (C:\...) or
using relative paths.


> That seems useful to me, but I'm not sure it's as useful as it
> could or might need to be in some cases.  E.g., if sources for
> nightly builds are downloaded into a temporary directory with some
> random name, this prepends a known prefix to the directory but it
> doesn't make the whole path-name determinate.  Do you think it
> would it be useful to add support for globbing, as a separate
> enhancement?)

I believe the expected use for these options is that you know where
you are building and you want to strip the varying prefix and leave
the invariant. For example, if I unpacked gcc as /tmp/gcc/... then
for a path like /tmp/gcc/libcpp/macro.c I would want to strip
/tmp/gcc/ and not /tmp/gcc/libcpp/.

Which means that for the globbing idea to work you would need to
specify some kind of a 'stop anchor', e.g., '*/libcpp=libcpp' since
just '*/=/' will strip everything all the way to the file name.

So I would suggest that we wait for a clear need and a concrete
use case.


> >+ffile-prefix-map=
> >+Common Joined RejectNegative Var(common_deferred_options) Defer
> 
> AFAIK, RejectNegative applies to options that take integer arguments.
> This one takes a string so it probably doesn't belong here.

Fixed.


> >+@item -fmacro-prefix-map=@var{old}=@var{new}
> >+@opindex fmacro-prefix-map
> >+When preprocessing files in directory @file{@var{old}}, expand the
> >+@code{__FILE__} and @code{__BASE_FILE__} macros as in @file{@var{new}}
> >+instead.
> 
> This could be just me, but to my eyes this sentence can be read
> one of two ways: (1) files residing in directory old, or (2)
> the current working directory is old while preprocessing some
> files.  To make it 100% clear which is meant, I would find it
> more accurate if it were phrased like this instead:
> 
>   When preprocessing files residing in directory @file{@var{old}},
>   expand the @code{__FILE__} and @code{__BASE_FILE__} macros as if
>   the files resided in directory @file{@var{new}} instead.
> 
> Ditto for the other options.

Fixed.


libcpp/ChangeLog:

2017-12-09  Boris Kolpackov  <boris@codesynthesis.com>

        PR other/70268
        * include/cpplib.h (cpp_callbacks::remap_filename): New callback.
        * libcpp/macro.c (_cpp_builtin_macro_text): Call remap_filename for
        __FILE__ and __BASE_FILE__.


gcc/ChangeLog:

2017-12-09  Boris Kolpackov  <boris@codesynthesis.com>

        PR other/70268
        * common.opt: (-ffile-prefix-map): New option.
        * opts.c (common_handle_option): Defer it.
        * opts-global.c (handle_common_deferred_options): Handle it.
        * debug.h (remap_debug_filename, add_debug_prefix_map): Move to...
        * file-prefix-map.h: New file.
        (remap_debug_filename, add_debug_prefix_map): ...here.
        (add_macro_prefix_map, add_file_prefix_map, remap_macro_filename): New.
        * final.c (debug_prefix_map, add_debug_prefix_map
        remap_debug_filename): Move to...
        * file-prefix-map.c: New file.
        (file_prefix_map, add_prefix_map, remap_filename) ...here and rename,
        generalize, get rid of alloca() call.
        (add_macro_prefix_map, add_debug_prefix_map, add_file_prefix_map):
        Implement in terms of add_prefix_map().
        (remap_macro_filename, remap_debug_filename): Implement in term of
        remap_filename().
        * Makefile.in (OBJS, PLUGIN_HEADERS): Add new files.
        * builtins.c (fold_builtin_FILE): Call remap_macro_filename().
        * dbxout.c: Include file-prefix-map.h.
        * varasm.c: Likewise.
        * vmsdbgout.c: Likewise.
        * xcoffout.c: Likewise.
        * dwarf2out.c: Likewise plus omit new options from DW_AT_producer.
        * doc/cppopts.texi (-fmacro-prefix-map): Document.
        * doc/invoke.texi (-ffile-prefix-map): Document.
	(-fdebug-prefix-map): Update description.


gcc/c-family/ChangeLog:

2017-12-09  Boris Kolpackov  <boris@codesynthesis.com>

        PR other/70268
        * c-family/c.opt (-fmacro-prefix-map): New option.
        * c-family/c-opts.c (c_common_handle_option): Handle it.
        * c-family/c-lex.c (init_c_lex): Set remap_filename cpp callback.
        * c-family/c-ppoutput.c (init_pp_output): Likewise.


gcc/testsuite/ChangeLog:

2017-12-09  Boris Kolpackov  <boris@codesynthesis.com>

        PR other/70268
        * c-c++-common/ffile-prefix-map.c: New test.
        * c-c++-common/fmacro-prefix-map.c: New test.
        * c-c++-common/cpp/ffile-prefix-map.c: New test.
        * c-c++-common/cpp/fmacro-prefix-map.c: New test.


diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 6874f94e7f6..3237bf289df 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1284,6 +1284,7 @@ OBJS = \
 	expmed.o \
 	expr.o \
 	fibonacci_heap.o \
+	file-prefix-map.o \
 	final.o \
 	fixed-value.o \
 	fold-const.o \
@@ -3498,8 +3499,8 @@ PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
   tree-ssa-loop-niter.h tree-ssa-ter.h tree-ssa-threadedge.h \
   tree-ssa-threadupdate.h inchash.h wide-int.h signop.h hash-map.h \
   hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h cfgcleanup.h \
-  lcm.h cfgloopmanip.h builtins.def chkp-builtins.def pass-instances.def \
-  params.list
+  lcm.h cfgloopmanip.h file-prefix-map.h builtins.def chkp-builtins.def \
+  pass-instances.def params.list
 
 # generate the 'build fragment' b-header-vars
 s-header-vars: Makefile
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 6b25253950c..65f5c43d770 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -69,6 +69,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "case-cfn-macros.h"
 #include "gimple-fold.h"
 #include "intl.h"
+#include "file-prefix-map.h" /* remap_macro_filename()  */
 
 struct target_builtins default_target_builtins;
 #if SWITCHABLE_TARGET
@@ -8776,7 +8777,13 @@ static inline tree
 fold_builtin_FILE (location_t loc)
 {
   if (const char *fname = LOCATION_FILE (loc))
+    {
+      /* The documentation says this builtin is equivalent to the preprocessor
+	 __FILE__ macro so it appears appropriate to use the same file prefix
+	 mappings.  */
+      fname = remap_macro_filename (fname);
       return build_string_literal (strlen (fname) + 1, fname);
+    }
 
   return build_string_literal (1, "");
 }
diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index 8342800303a..9d61abe545d 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "c-pragma.h"
 #include "debug.h"
+#include "file-prefix-map.h" /* remap_macro_filename()  */
 
 #include "attribs.h"
 
@@ -82,6 +83,7 @@ init_c_lex (void)
   cb->has_attribute = c_common_has_attribute;
   cb->get_source_date_epoch = cb_get_source_date_epoch;
   cb->get_suggestion = cb_get_suggestion;
+  cb->remap_filename = remap_macro_filename;
 
   /* Set the debug callbacks if we can use them.  */
   if ((debug_info_level == DINFO_LEVEL_VERBOSE
diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index cead15e7a63..04b8c6cd40a 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"		/* For PLUGIN_INCLUDE_FILE event.  */
 #include "mkdeps.h"
 #include "dumpfile.h"
+#include "file-prefix-map.h"    /* add_*_prefix_map()  */
 
 #ifndef DOLLARS_IN_IDENTIFIERS
 # define DOLLARS_IN_IDENTIFIERS true
@@ -448,6 +449,10 @@ c_common_handle_option (size_t scode, const char *arg, int value,
       cpp_opts->dollars_in_ident = value;
       break;
 
+    case OPT_fmacro_prefix_map_:
+      add_macro_prefix_map (arg);
+      break;
+
     case OPT_ffreestanding:
       value = !value;
       /* Fall through.  */
diff --git a/gcc/c-family/c-ppoutput.c b/gcc/c-family/c-ppoutput.c
index d1c92379f62..949a4955923 100644
--- a/gcc/c-family/c-ppoutput.c
+++ b/gcc/c-family/c-ppoutput.c
@@ -22,6 +22,7 @@
 #include "c-common.h"		/* For flags.  */
 #include "../libcpp/internal.h"
 #include "c-pragma.h"		/* For parse_in.  */
+#include "file-prefix-map.h"    /* remap_macro_filename()  */
 
 /* Encapsulates state used to convert a stream of tokens into a text
    file.  */
@@ -151,6 +152,7 @@ init_pp_output (FILE *out_stream)
 
   cb->has_attribute = c_common_has_attribute;
   cb->get_source_date_epoch = cb_get_source_date_epoch;
+  cb->remap_filename = remap_macro_filename;
 
   /* Initialize the print structure.  */
   print.src_line = 1;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index b2548105736..36ec7fd79a3 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1386,6 +1386,10 @@ fdollars-in-identifiers
 C ObjC C++ ObjC++
 Permit '$' as an identifier character.
 
+fmacro-prefix-map=
+C ObjC C++ ObjC++ Joined
+-fmacro-prefix-map=<old>=<new> Map one directory name to another in __FILE__, __BASE_FILE__, and __builtin_FILE().
+
 fdump-ada-spec
 C ObjC C++ ObjC++ RejectNegative Var(flag_dump_ada_spec)
 Write all declarations as Ada code transitively.
diff --git a/gcc/common.opt b/gcc/common.opt
index ffcbf850216..53dcc402e58 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1174,8 +1174,12 @@ Common RejectNegative Joined Var(common_deferred_options) Defer
 -fdbg-cnt=<counter>:<limit>[,<counter>:<limit>,...]	Set the debug counter limit.
 
 fdebug-prefix-map=
-Common Joined RejectNegative Var(common_deferred_options) Defer
-Map one directory name to another in debug information.
+Common Joined Var(common_deferred_options) Defer
+-fdebug-prefix-map=<old>=<new> Map one directory name to another in debug information.
+
+ffile-prefix-map=
+Common Joined Var(common_deferred_options) Defer
+-ffile-prefix-map=<old>=<new> Map one directory name to another in compilation result.
 
 fdebug-types-section
 Common Report Var(flag_debug_types_section) Init(0)
diff --git a/gcc/dbxout.c b/gcc/dbxout.c
index 290f11b3c46..fbb6a5df6c2 100644
--- a/gcc/dbxout.c
+++ b/gcc/dbxout.c
@@ -91,6 +91,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "common/common-target.h"
 #include "langhooks.h"
 #include "expr.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 #ifdef XCOFF_DEBUGGING_INFO
 #include "xcoffout.h"
diff --git a/gcc/debug.h b/gcc/debug.h
index 277d990c20f..22fc7e7ab20 100644
--- a/gcc/debug.h
+++ b/gcc/debug.h
@@ -246,9 +246,6 @@ extern bool dwarf2out_do_frame (void);
 extern bool dwarf2out_do_cfi_asm (void);
 extern void dwarf2out_switch_text_section (void);
 
-const char *remap_debug_filename (const char *);
-void add_debug_prefix_map (const char *);
-
 /* For -fdump-go-spec.  */
 
 extern const struct gcc_debug_hooks *
diff --git a/gcc/doc/cppopts.texi b/gcc/doc/cppopts.texi
index 16bf22a6f38..7c162463050 100644
--- a/gcc/doc/cppopts.texi
+++ b/gcc/doc/cppopts.texi
@@ -287,6 +287,17 @@ When this option is given no argument, the default parameter value is
 
 Note that @code{-ftrack-macro-expansion=2} is activated by default.
 
+@item -fmacro-prefix-map=@var{old}=@var{new}
+@opindex fmacro-prefix-map
+When preprocessing files residing in directory @file{@var{old}},
+expand the @code{__FILE__} and @code{__BASE_FILE__} macros as if the
+files resided in directory @file{@var{new}} instead.  This can be used
+to change an absolute path to a relative path by using @file{.} for
+@var{new} which can result in more reproducible builds that are
+location independent.  This option also affects
+@code{__builtin_FILE()} during compilation.  See also
+@option{-ffile-prefix-map}.
+
 @item -fexec-charset=@var{charset}
 @opindex fexec-charset
 @cindex character set, execution
@@ -534,4 +545,3 @@ token in the output is preceded by the dump of the map its location
 belongs to.
 
 When used from GCC without @option{-E}, this option has no effect.
-
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 3487a62ab28..e84b03ac1f3 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -172,7 +172,8 @@ in the following sections.
 @gccoptlist{-c  -S  -E  -o @var{file}  -x @var{language}  @gol
 -v  -###  --help@r{[}=@var{class}@r{[},@dots{}@r{]]}  --target-help  --version @gol
 -pass-exit-codes  -pipe  -specs=@var{file}  -wrapper  @gol
-@@@var{file}  -fplugin=@var{file}  -fplugin-arg-@var{name}=@var{arg}  @gol
+@@@var{file}  -ffile-prefix-map=@var{old}=@var{new}  @gol
+-fplugin=@var{file}  -fplugin-arg-@var{name}=@var{arg}  @gol
 -fdump-ada-spec@r{[}-slim@r{]}  -fada-spec-parent=@var{unit} -fdump-go-spec=@var{file}}
 
 @item C Language Options
@@ -480,9 +481,9 @@ Objective-C and Objective-C++ Dialects}.
 -dD  -dI  -dM  -dN  -dU @gol
 -fdebug-cpp  -fdirectives-only  -fdollars-in-identifiers  @gol
 -fexec-charset=@var{charset}  -fextended-identifiers  @gol
--finput-charset=@var{charset}  -fno-canonical-system-headers @gol
--fpch-deps  -fpch-preprocess  -fpreprocessed @gol
--ftabstop=@var{width}  -ftrack-macro-expansion  @gol
+-finput-charset=@var{charset} -fmacro-prefix-map=@var{old}=@var{new}  @gol
+-fno-canonical-system-headers @gol -fpch-deps  -fpch-preprocess  @gol
+-fpreprocessed -ftabstop=@var{width}  -ftrack-macro-expansion  @gol
 -fwide-exec-charset=@var{charset}  -fworking-directory @gol
 -H  -imacros @var{file}  -include @var{file} @gol
 -M  -MD  -MF  -MG  -MM  -MMD  -MP  -MQ  -MT @gol
@@ -1651,6 +1652,16 @@ This invokes all subprograms of @command{gcc} under
 @samp{gdb --args}, thus the invocation of @command{cc1} is
 @samp{gdb --args cc1 @dots{}}.
 
+@item -ffile-prefix-map=@var{old}=@var{new}
+@opindex ffile-prefix-map
+When compiling files residing in directory @file{@var{old}}, record
+any references to them in the result of the compilation as if the
+files resided in directory @file{@var{new}} instead.  Specifying this
+option is equivalent to specifying all the individual
+@option{-f*-prefix-map} options.  This can be used to make reproducible
+builds that are location independent.  See also
+@option{-fmacro-prefix-map} and @option{-fdebug-prefix-map}.
+
 @item -fplugin=@var{name}.so
 @opindex fplugin
 Load the plugin code in file @var{name}.so, assumed to be a
@@ -7054,13 +7065,14 @@ link processing time.  Merging is enabled by default.
 
 @item -fdebug-prefix-map=@var{old}=@var{new}
 @opindex fdebug-prefix-map
-When compiling files in directory @file{@var{old}}, record debugging
-information describing them as in @file{@var{new}} instead.  This can be
-used to replace a build-time path with an install-time path in the debug info.
-It can also be used to change an absolute path to a relative path by using
-@file{.} for @var{new}.  This can give more reproducible builds, which are
-location independent, but may require an extra command to tell GDB where to
-find the source files.
+When compiling files residing in directory @file{@var{old}}, record
+debugging information describing them as if the files resided in
+directory @file{@var{new}} instead.  This can be used to replace a
+build-time path with an install-time path in the debug info.  It can
+also be used to change an absolute path to a relative path by using
+@file{.} for @var{new}.  This can give more reproducible builds, which
+are location independent, but may require an extra command to tell GDB
+where to find the source files. See also @option{-ffile-prefix-map}.
 
 @item -fvar-tracking
 @opindex fvar-tracking
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index ae3d962526c..9d1297a087f 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -94,6 +94,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl-iter.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 static void dwarf2out_source_line (unsigned int, unsigned int, const char *,
 				   int, bool);
@@ -23417,6 +23418,8 @@ gen_producer_string (void)
       case OPT_fltrans_output_list_:
       case OPT_fresolution_:
       case OPT_fdebug_prefix_map_:
+      case OPT_fmacro_prefix_map_:
+      case OPT_ffile_prefix_map_:
       case OPT_fcompare_debug:
 	/* Ignore these.  */
 	continue;
diff --git a/gcc/file-prefix-map.c b/gcc/file-prefix-map.c
new file mode 100644
index 00000000000..563546b8632
--- /dev/null
+++ b/gcc/file-prefix-map.c
@@ -0,0 +1,130 @@
+/* Implementation of file prefix remapping support (-f*-prefix-map options).
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This program 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, or (at your option) any
+   later version.
+
+   This program 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; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "diagnostic.h"
+#include "file-prefix-map.h"
+
+/* Structure recording the mapping from source file and directory names at
+   compile time to those to be embedded in the compilation result (debug
+   information, the __FILE__ macro expansion, etc).  */
+struct file_prefix_map
+{
+  const char *old_prefix;
+  const char *new_prefix;
+  size_t old_len;
+  size_t new_len;
+  struct file_prefix_map *next;
+};
+
+/* Record a file prefix mapping in the specified map.  ARG is the argument to
+   -f*-prefix-map and must be of the form OLD=NEW.  OPT is the option name
+   for diagnostics.  */
+static void
+add_prefix_map (file_prefix_map *&maps, const char *arg, const char *opt)
+{
+  file_prefix_map *map;
+  const char *p;
+
+  p = strchr (arg, '=');
+  if (!p)
+    {
+      error ("invalid argument %qs to %qs", arg, opt);
+      return;
+    }
+  map = XNEW (file_prefix_map);
+  map->old_prefix = xstrndup (arg, p - arg);
+  map->old_len = p - arg;
+  p++;
+  map->new_prefix = xstrdup (p);
+  map->new_len = strlen (p);
+  map->next = maps;
+  maps = map;
+}
+
+/* Perform user-specified mapping of filename prefixes.  Return the
+   GC-allocated new name corresponding to FILENAME or FILENAME if no
+   remapping was performed.  */
+
+static const char *
+remap_filename (file_prefix_map *maps, const char *filename)
+{
+  file_prefix_map *map;
+  char *s;
+  const char *name;
+  size_t name_len;
+
+  for (map = maps; map; map = map->next)
+    if (filename_ncmp (filename, map->old_prefix, map->old_len) == 0)
+      break;
+  if (!map)
+    return filename;
+  name = filename + map->old_len;
+  name_len = strlen (name) + 1;
+
+  s = (char *) ggc_alloc_atomic (name_len + map->new_len);
+  memcpy (s, map->new_prefix, map->new_len);
+  memcpy (s + map->new_len, name, name_len);
+  return s;
+}
+
+/* NOTE: if adding another -f*-prefix-map option then don't forget to
+   ignore it in DW_AT_producer (dwarf2out.c).  */
+
+/* Linked lists of file_prefix_map structures.  */
+static file_prefix_map *macro_prefix_maps; /* -fmacro-prefix-map  */
+static file_prefix_map *debug_prefix_maps; /* -fdebug-prefix-map  */
+
+/* Record a file prefix mapping for -fmacro-prefix-map.  */
+void
+add_macro_prefix_map (const char *arg)
+{
+  add_prefix_map (macro_prefix_maps, arg, "-fmacro-prefix-map");
+}
+
+/* Record a file prefix mapping for -fdebug-prefix-map.  */
+void
+add_debug_prefix_map (const char *arg)
+{
+  add_prefix_map (debug_prefix_maps, arg, "-fdebug-prefix-map");
+}
+
+/* Record a file prefix mapping for all -f*-prefix-map.  */
+void
+add_file_prefix_map (const char *arg)
+{
+  add_prefix_map (macro_prefix_maps, arg, "-ffile-prefix-map");
+  add_prefix_map (debug_prefix_maps, arg, "-ffile-prefix-map");
+}
+
+/* Remap using -fmacro-prefix-map.  Return the GC-allocated new name
+   corresponding to FILENAME or FILENAME if no remapping was performed.  */
+const char *
+remap_macro_filename (const char *filename)
+{
+  return remap_filename (macro_prefix_maps, filename);
+}
+
+/* Remap using -fdebug-prefix-map.  Return the GC-allocated new name
+   corresponding to FILENAME or FILENAME if no remapping was performed.  */
+const char *
+remap_debug_filename (const char *filename)
+{
+  return remap_filename (debug_prefix_maps, filename);
+}
diff --git a/gcc/file-prefix-map.h b/gcc/file-prefix-map.h
new file mode 100644
index 00000000000..3309eeed646
--- /dev/null
+++ b/gcc/file-prefix-map.h
@@ -0,0 +1,28 @@
+/* Declarations for file prefix remapping support (-f*-prefix-map options).
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This program 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, or (at your option) any
+   later version.
+
+   This program 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; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_FILE_PREFIX_MAP_H
+#define GCC_FILE_PREFIX_MAP_H
+
+void add_macro_prefix_map (const char *);
+void add_debug_prefix_map (const char *);
+void add_file_prefix_map (const char *);
+
+const char *remap_macro_filename (const char *);
+const char *remap_debug_filename (const char *);
+
+#endif /* !GCC_FILE_PREFIX_MAP_H  */
diff --git a/gcc/final.c b/gcc/final.c
index afb6906997d..c9bd26059f2 100644
--- a/gcc/final.c
+++ b/gcc/final.c
@@ -1507,71 +1507,6 @@ asm_str_count (const char *templ)
   return count;
 }
 
-/* ??? This is probably the wrong place for these.  */
-/* Structure recording the mapping from source file and directory
-   names at compile time to those to be embedded in debug
-   information.  */
-struct debug_prefix_map
-{
-  const char *old_prefix;
-  const char *new_prefix;
-  size_t old_len;
-  size_t new_len;
-  struct debug_prefix_map *next;
-};
-
-/* Linked list of such structures.  */
-static debug_prefix_map *debug_prefix_maps;
-
-
-/* Record a debug file prefix mapping.  ARG is the argument to
-   -fdebug-prefix-map and must be of the form OLD=NEW.  */
-
-void
-add_debug_prefix_map (const char *arg)
-{
-  debug_prefix_map *map;
-  const char *p;
-
-  p = strchr (arg, '=');
-  if (!p)
-    {
-      error ("invalid argument %qs to -fdebug-prefix-map", arg);
-      return;
-    }
-  map = XNEW (debug_prefix_map);
-  map->old_prefix = xstrndup (arg, p - arg);
-  map->old_len = p - arg;
-  p++;
-  map->new_prefix = xstrdup (p);
-  map->new_len = strlen (p);
-  map->next = debug_prefix_maps;
-  debug_prefix_maps = map;
-}
-
-/* Perform user-specified mapping of debug filename prefixes.  Return
-   the new name corresponding to FILENAME.  */
-
-const char *
-remap_debug_filename (const char *filename)
-{
-  debug_prefix_map *map;
-  char *s;
-  const char *name;
-  size_t name_len;
-
-  for (map = debug_prefix_maps; map; map = map->next)
-    if (filename_ncmp (filename, map->old_prefix, map->old_len) == 0)
-      break;
-  if (!map)
-    return filename;
-  name = filename + map->old_len;
-  name_len = strlen (name) + 1;
-  s = (char *) alloca (name_len + map->new_len);
-  memcpy (s, map->new_prefix, map->new_len);
-  memcpy (s + map->new_len, name, name_len);
-  return ggc_strdup (s);
-}
 
 /* Return true if DWARF2 debug info can be emitted for DECL.  */
 
diff --git a/gcc/opts-global.c b/gcc/opts-global.c
index 343dbd3ac2c..aadc2ef880b 100644
--- a/gcc/opts-global.c
+++ b/gcc/opts-global.c
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "file-prefix-map.h" /* add_*_prefix_map()  */
 
 typedef const char *const_char_p; /* For DEF_VEC_P.  */
 
@@ -365,6 +366,10 @@ handle_common_deferred_options (void)
 	  add_debug_prefix_map (opt->arg);
 	  break;
 
+	case OPT_ffile_prefix_map_:
+	  add_file_prefix_map (opt->arg);
+	  break;
+
 	case OPT_fdump_:
 	  if (!g->get_dumps ()->dump_switch_p (opt->arg))
 	    error ("unrecognized command line option %<-fdump-%s%>", opt->arg);
diff --git a/gcc/opts.c b/gcc/opts.c
index 17579e72373..8c8ad3e06a5 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -2091,6 +2091,7 @@ common_handle_option (struct gcc_options *opts,
       break;
 
     case OPT_fdebug_prefix_map_:
+    case OPT_ffile_prefix_map_:
       /* Deferred.  */
       break;
 
diff --git a/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
new file mode 100644
index 00000000000..294875b717d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+/* { dg-options "-ffile-prefix-map==FILE-PREFIX" } */
+
+#pragma message "FILE starts with " __FILE__       /* { dg-message "FILE starts with FILE-PREFIX" } */
+#pragma message "BASE_FILE starts with " __BASE_FILE__  /* { dg-message "BASE_FILE starts with FILE-PREFIX" } */
diff --git a/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c b/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c
new file mode 100644
index 00000000000..43dc4bb287e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+/* { dg-options "-fmacro-prefix-map==MACRO-PREFIX" } */
+
+#pragma message "FILE starts with " __FILE__       /* { dg-message "FILE starts with MACRO-PREFIX" } */
+#pragma message "BASE_FILE starts with " __BASE_FILE__  /* { dg-message "BASE_FILE starts with MACRO-PREFIX" } */
diff --git a/gcc/testsuite/c-c++-common/ffile-prefix-map.c b/gcc/testsuite/c-c++-common/ffile-prefix-map.c
new file mode 100644
index 00000000000..88c29ca829b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ffile-prefix-map.c
@@ -0,0 +1,12 @@
+/* Test __builtin_FILE(). */
+/* { dg-do run } */
+/* { dg-options "-ffile-prefix-map==FILE-PREFIX" } */
+
+#include <stdio.h>
+
+int main ()
+{
+  printf ("__builtin_FILE starts with %s\n", __builtin_FILE ());
+}
+
+/* { dg-output "__builtin_FILE starts with FILE-PREFIX" } */
diff --git a/gcc/testsuite/c-c++-common/fmacro-prefix-map.c b/gcc/testsuite/c-c++-common/fmacro-prefix-map.c
new file mode 100644
index 00000000000..db51587cb4f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fmacro-prefix-map.c
@@ -0,0 +1,12 @@
+/* Test __builtin_FILE(). */
+/* { dg-do run } */
+/* { dg-options "-fmacro-prefix-map==MACRO-PREFIX" } */
+
+#include <stdio.h>
+
+int main ()
+{
+  printf ("__builtin_FILE starts with %s\n", __builtin_FILE ());
+}
+
+/* { dg-output "__builtin_FILE starts with MACRO-PREFIX" } */
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 392ac443f14..34d225a96cb 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -55,6 +55,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "asan.h"
 #include "rtl-iter.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 #ifdef XCOFF_DEBUGGING_INFO
 #include "xcoffout.h"		/* Needed for external data declarations.  */
diff --git a/gcc/vmsdbgout.c b/gcc/vmsdbgout.c
index 91dcd2e3910..908ab9aa849 100644
--- a/gcc/vmsdbgout.c
+++ b/gcc/vmsdbgout.c
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "function.h"
 #include "target.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 /* Difference in seconds between the VMS Epoch and the Unix Epoch */
 static const long long vms_epoch_offset = 3506716800ll;
diff --git a/gcc/xcoffout.c b/gcc/xcoffout.c
index cf2064d5ba5..afda2f83e82 100644
--- a/gcc/xcoffout.c
+++ b/gcc/xcoffout.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "output.h"
 #include "debug.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 #ifdef XCOFF_DEBUGGING_INFO
 
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 4d04a48a0e4..a4e8ac6f8d3 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -622,6 +622,10 @@ struct cpp_callbacks
      C++-style comments it does not include the terminating newline.  */
   void (*comment) (cpp_reader *, source_location, const unsigned char *,
 		   size_t);
+
+  /* Callback for filename remapping in __FILE__ and __BASE_FILE__ macro
+     expansions.  */
+  const char *(*remap_filename) (const char*);
 };
 
 #ifdef VMS
diff --git a/libcpp/macro.c b/libcpp/macro.c
index 791817af8db..46407b6425e 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -450,6 +450,8 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node,
 	    if (!name)
 	      abort ();
 	  }
+	if (pfile->cb.remap_filename)
+	  name = pfile->cb.remap_filename (name);
 	len = strlen (name);
 	buf = _cpp_unaligned_alloc (pfile, len * 2 + 3);
 	result = buf;
Ximin Luo Dec. 19, 2017, 5:42 p.m. UTC | #5
Boris Kolpackov:
> Thanks for the review. Third revision of the patch attached.
> 
> [..]
Here is a follow-up patch, meant to be applied on top of Boris' patch, to 
address some issues we found based on discussions and experiments at the 
Reproducible Builds project.

Most of this patch is re-implementing the patch from [1] originally by Daniel 
Kahn Gillmor. Back then it was rejected in favour of a simpler approach because 
its necessity was not realised. However, more experiments since then have 
revealed that its basic approach is useful in more general scenarios:

Higher-level build scripts sometimes like to save CFLAGS etc into the build 
output, making the overall build output unreproducible even if GCC is playing 
nicely. Rather than add logic to strip -f{file,debug,macro,...}-prefix-map, 
into all possible higher-level programs that might want to save CFLAGS, it is
simpler if GCC could read it from a user-specified environment variable. The
fact that the name of the environment variable is still present on the
command-line, should make it obvious what is being done, and avoid confusion
that might arise if an implicit "magic" environment variable is used.

In the patch thread (see [1] and follow-ups) there was concern that the prefix 
'$' would cause awkward escaping behaviour, so the prefix 'ENV:' was chosen. A 
concern about this conflicting with AmigaOS paths was voiced but not explored; 
of course it can be changed to something else if needed.

(NetBSD is already carrying this patch in their system GCC [2], presumably for
the same reason mentioned above.)

[1] https://gcc.gnu.org/ml/gcc-patches/2015-12/msg01168.html
[2] http://cvsweb.netbsd.org/bsdweb.cgi/src/external/gpl3/gcc/dist/gcc/final.c?rev=1.2&content-type=text/x-cvsweb-markup

The other thing that this patch does, is to split the old and new prefixs by
the last '=' sign rather than the first. This allows arbitrary old prefixes to
be mapped, which would be more flexible than mapping to arbitrary new prefixes.
The rust compiler is adopting this strategy too: [3].

[3] https://github.com/rust-lang/rust/issues/41555#issuecomment-321078174

ChangeLogs
----------

gcc/ChangeLog:

2017-12-19  Ximin Luo  <infinity0@pwned.gg>

	PR other/70268
	* file-prefix-map.c (add_prefix_map): Support reading old prefix from the
	environment. Split old and new parts by the last '=' rather than the first.
	* file-prefix-map.h: Define macros for the ENV: prefix.

gcc/testsuite/ChangeLog:

2017-12-19  Ximin Luo  <infinity0@pwned.gg>

	PR other/70268
	* c-c++-common/ffile-prefix-map-env.c: New test.

Index: gcc-8-20171217/gcc/file-prefix-map.c
===================================================================
--- gcc-8-20171217.orig/gcc/file-prefix-map.c
+++ gcc-8-20171217/gcc/file-prefix-map.c
@@ -41,16 +41,40 @@ add_prefix_map (file_prefix_map *&maps,
 {
   file_prefix_map *map;
   const char *p;
+  char *env;
+  const char *old;
+  size_t oldlen;
 
-  p = strchr (arg, '=');
+  p = strrchr (arg, '=');
   if (!p)
     {
       error ("invalid argument %qs to %qs", arg, opt);
       return;
     }
+  if (0 == strncmp(FILE_PREFIX_MAP_ENV_PREFIX, arg, FILE_PREFIX_MAP_ENV_PREFIX_OFFSET))
+    {
+      const char *arg_offset = arg + FILE_PREFIX_MAP_ENV_PREFIX_OFFSET;
+      env = xstrndup (arg_offset, p - arg_offset);
+      old = getenv (env);
+      if (!old)
+	{
+	  warning (0, "environment variable %qs not set in argument to %s",
+		   env, opt);
+	  free (env);
+	  return;
+	}
+      oldlen = strlen (old);
+      free (env);
+    }
+  else
+    {
+      old = xstrndup (arg, p - arg);
+      oldlen = p - arg;
+    }
+
   map = XNEW (file_prefix_map);
-  map->old_prefix = xstrndup (arg, p - arg);
-  map->old_len = p - arg;
+  map->old_prefix = old;
+  map->old_len = oldlen;
   p++;
   map->new_prefix = xstrdup (p);
   map->new_len = strlen (p);
Index: gcc-8-20171217/gcc/file-prefix-map.h
===================================================================
--- gcc-8-20171217.orig/gcc/file-prefix-map.h
+++ gcc-8-20171217/gcc/file-prefix-map.h
@@ -18,6 +18,9 @@
 #ifndef GCC_FILE_PREFIX_MAP_H
 #define GCC_FILE_PREFIX_MAP_H
 
+#define FILE_PREFIX_MAP_ENV_PREFIX "ENV:"
+#define FILE_PREFIX_MAP_ENV_PREFIX_OFFSET (sizeof(FILE_PREFIX_MAP_ENV_PREFIX) - 1)
+
 void add_macro_prefix_map (const char *);
 void add_debug_prefix_map (const char *);
 void add_file_prefix_map (const char *);
Index: gcc-8-20171217/gcc/testsuite/c-c++-common/ffile-prefix-map-env.c
===================================================================
--- /dev/null
+++ gcc-8-20171217/gcc/testsuite/c-c++-common/ffile-prefix-map-env.c
@@ -0,0 +1,13 @@
+/* Test __builtin_FILE() with ENV: prefix-map. */
+/* { dg-do run } */
+/* { dg-set-compiler-env-var SRCDIR "$srcdir" } */
+/* { dg-options "-ffile-prefix-map=ENV:SRCDIR=xxx" } */
+
+#include <stdio.h>
+
+int main ()
+{
+  printf ("__builtin_FILE starts with %s\n", __builtin_FILE ());
+}
+
+/* { dg-output "__builtin_FILE starts with xxx" } */
Ximin Luo Jan. 18, 2018, 1:46 p.m. UTC | #6
Hello, I see Boris' patch was accepted with some further changes.

Any chance my additions for reproducible builds can be included as well? They are described below.

It should still apply on top, minus the strchr -> strrchr change which was folded into Boris' accepted version.

Ximin

Ximin Luo:
> Boris Kolpackov:
>> Thanks for the review. Third revision of the patch attached.
>>
>> [..]
> Here is a follow-up patch, meant to be applied on top of Boris' patch, to 
> address some issues we found based on discussions and experiments at the 
> Reproducible Builds project.
> 
> Most of this patch is re-implementing the patch from [1] originally by Daniel 
> Kahn Gillmor. Back then it was rejected in favour of a simpler approach because 
> its necessity was not realised. However, more experiments since then have 
> revealed that its basic approach is useful in more general scenarios:
> 
> Higher-level build scripts sometimes like to save CFLAGS etc into the build 
> output, making the overall build output unreproducible even if GCC is playing 
> nicely. Rather than add logic to strip -f{file,debug,macro,...}-prefix-map, 
> into all possible higher-level programs that might want to save CFLAGS, it is
> simpler if GCC could read it from a user-specified environment variable. The
> fact that the name of the environment variable is still present on the
> command-line, should make it obvious what is being done, and avoid confusion
> that might arise if an implicit "magic" environment variable is used.
> 
> In the patch thread (see [1] and follow-ups) there was concern that the prefix 
> '$' would cause awkward escaping behaviour, so the prefix 'ENV:' was chosen. A 
> concern about this conflicting with AmigaOS paths was voiced but not explored; 
> of course it can be changed to something else if needed.
> 
> (NetBSD is already carrying this patch in their system GCC [2], presumably for
> the same reason mentioned above.)
> 
> [1] https://gcc.gnu.org/ml/gcc-patches/2015-12/msg01168.html
> [2] http://cvsweb.netbsd.org/bsdweb.cgi/src/external/gpl3/gcc/dist/gcc/final.c?rev=1.2&content-type=text/x-cvsweb-markup
> 
> The other thing that this patch does, is to split the old and new prefixs by
> the last '=' sign rather than the first. This allows arbitrary old prefixes to
> be mapped, which would be more flexible than mapping to arbitrary new prefixes.
> The rust compiler is adopting this strategy too: [3].
> 
> [3] https://github.com/rust-lang/rust/issues/41555#issuecomment-321078174
> 
> ChangeLogs
> ----------
> 
> gcc/ChangeLog:
> 
> 2017-12-19  Ximin Luo  <infinity0@pwned.gg>
> 
> 	PR other/70268
> 	* file-prefix-map.c (add_prefix_map): Support reading old prefix from the
> 	environment. Split old and new parts by the last '=' rather than the first.
> 	* file-prefix-map.h: Define macros for the ENV: prefix.
> 
> gcc/testsuite/ChangeLog:
> 
> 2017-12-19  Ximin Luo  <infinity0@pwned.gg>
> 
> 	PR other/70268
> 	* c-c++-common/ffile-prefix-map-env.c: New test.
> 
> Index: gcc-8-20171217/gcc/file-prefix-map.c
> ===================================================================
> --- gcc-8-20171217.orig/gcc/file-prefix-map.c
> +++ gcc-8-20171217/gcc/file-prefix-map.c
> @@ -41,16 +41,40 @@ add_prefix_map (file_prefix_map *&maps,
>  {
>    file_prefix_map *map;
>    const char *p;
> +  char *env;
> +  const char *old;
> +  size_t oldlen;
>  
> -  p = strchr (arg, '=');
> +  p = strrchr (arg, '=');
>    if (!p)
>      {
>        error ("invalid argument %qs to %qs", arg, opt);
>        return;
>      }
> +  if (0 == strncmp(FILE_PREFIX_MAP_ENV_PREFIX, arg, FILE_PREFIX_MAP_ENV_PREFIX_OFFSET))
> +    {
> +      const char *arg_offset = arg + FILE_PREFIX_MAP_ENV_PREFIX_OFFSET;
> +      env = xstrndup (arg_offset, p - arg_offset);
> +      old = getenv (env);
> +      if (!old)
> +	{
> +	  warning (0, "environment variable %qs not set in argument to %s",
> +		   env, opt);
> +	  free (env);
> +	  return;
> +	}
> +      oldlen = strlen (old);
> +      free (env);
> +    }
> +  else
> +    {
> +      old = xstrndup (arg, p - arg);
> +      oldlen = p - arg;
> +    }
> +
>    map = XNEW (file_prefix_map);
> -  map->old_prefix = xstrndup (arg, p - arg);
> -  map->old_len = p - arg;
> +  map->old_prefix = old;
> +  map->old_len = oldlen;
>    p++;
>    map->new_prefix = xstrdup (p);
>    map->new_len = strlen (p);
> Index: gcc-8-20171217/gcc/file-prefix-map.h
> ===================================================================
> --- gcc-8-20171217.orig/gcc/file-prefix-map.h
> +++ gcc-8-20171217/gcc/file-prefix-map.h
> @@ -18,6 +18,9 @@
>  #ifndef GCC_FILE_PREFIX_MAP_H
>  #define GCC_FILE_PREFIX_MAP_H
>  
> +#define FILE_PREFIX_MAP_ENV_PREFIX "ENV:"
> +#define FILE_PREFIX_MAP_ENV_PREFIX_OFFSET (sizeof(FILE_PREFIX_MAP_ENV_PREFIX) - 1)
> +
>  void add_macro_prefix_map (const char *);
>  void add_debug_prefix_map (const char *);
>  void add_file_prefix_map (const char *);
> Index: gcc-8-20171217/gcc/testsuite/c-c++-common/ffile-prefix-map-env.c
> ===================================================================
> --- /dev/null
> +++ gcc-8-20171217/gcc/testsuite/c-c++-common/ffile-prefix-map-env.c
> @@ -0,0 +1,13 @@
> +/* Test __builtin_FILE() with ENV: prefix-map. */
> +/* { dg-do run } */
> +/* { dg-set-compiler-env-var SRCDIR "$srcdir" } */
> +/* { dg-options "-ffile-prefix-map=ENV:SRCDIR=xxx" } */
> +
> +#include <stdio.h>
> +
> +int main ()
> +{
> +  printf ("__builtin_FILE starts with %s\n", __builtin_FILE ());
> +}
> +
> +/* { dg-output "__builtin_FILE starts with xxx" } */
>
Boris Kolpackov Jan. 19, 2018, 7:46 a.m. UTC | #7
Ximin Luo <infinity0@pwned.gg> writes:

> Higher-level build scripts sometimes like to save CFLAGS etc into the build 
> output, making the overall build output unreproducible even if GCC is playing 
> nicely. Rather than add logic to strip -f{file,debug,macro,...}-prefix-map, 
> into all possible higher-level programs that might want to save CFLAGS, it is
> simpler if GCC could read it from a user-specified environment variable.

This does feel like we are trying to fix the issue in the wrong place.
Also, won't such broken packages normally store all options (including
-I) rather than just CFLAGS? And if the answer is no, then perhaps you
could put -ffile-prefix-map into CPPFLAGS instead of CFLAGS? Kind of
even feels right seeing that it affects the preprocessor.

Boris
Ximin Luo Jan. 20, 2018, 10:53 a.m. UTC | #8
Boris Kolpackov:
> Ximin Luo <infinity0@pwned.gg> writes:
> 
>> Higher-level build scripts sometimes like to save CFLAGS etc into the build 
>> output, making the overall build output unreproducible even if GCC is playing 
>> nicely. Rather than add logic to strip -f{file,debug,macro,...}-prefix-map, 
>> into all possible higher-level programs that might want to save CFLAGS, it is
>> simpler if GCC could read it from a user-specified environment variable.
> 
> This does feel like we are trying to fix the issue in the wrong place.
> Also, won't such broken packages normally store all options (including
> -I) rather than just CFLAGS? And if the answer is no, then perhaps you
> could put -ffile-prefix-map into CPPFLAGS instead of CFLAGS? Kind of
> even feels right seeing that it affects the preprocessor.
> 

The broad issue affects -fdebug-prefix-map as well, not just __FILE__.

Such packages are not "broken"; GCC itself stores command-line arguments in DW_AT_producer and as mentioned there was a bug to special-case filtering -fdebug-prefix-map out of that.

Now we'll have to add exceptions for -ffile-prefix-map and all the other flags you added, and everything else that might be added in future.

The same goes for all possible buildsystems / Makefile / etc that are affected by the issue.

Therefore, I concluded that the best and most appropriate solution was to fix it at the source. That is, allow it to be read from an environment variable, to "cancel out" the information that is already read from the filesystem - rather than forcing this build-specific non-deterministic information to be added to the command-line.

X
Boris Kolpackov Jan. 20, 2018, 2:20 p.m. UTC | #9
Ximin Luo <infinity0@pwned.gg> writes:

> Boris Kolpackov:
>
> > This does feel like we are trying to fix the issue in the wrong place.
> > Also, won't such broken packages normally store all options (including
> > -I) rather than just CFLAGS? And if the answer is no, then perhaps you
> > could put -ffile-prefix-map into CPPFLAGS instead of CFLAGS? Kind of
> > even feels right seeing that it affects the preprocessor.
> > 
> 
> The broad issue affects -fdebug-prefix-map as well, not just __FILE__.

-ffile-prefix-map is the "common" option that (currently)
takes care of both debug and macro remapping (and should we
add any new -f*-prefix-map, those as well).


> Such packages are not "broken"; GCC itself stores command-line
> arguments in DW_AT_producer and as mentioned there was a bug to
> special-case filtering -fdebug-prefix-map out of that.
> 
> Now we'll have to add exceptions for -ffile-prefix-map and all
> the other flags you added, and everything else that might be
> added in future.

-ffile-prefix-map and -fmacro-prefix-map are already excluded.
And there is a note at the appropriate place in source code to
exclude any new -f*-prefix-map should they be added.

You also haven't answered my question about -I: don't the
projects that embed CFLAGS also embed CPPFLAGS (and thus
-I's with absolute/varying paths)? And if the answer is
no, then can't you use the same approach to make
-ffile-prefix-map a non-issue?

Boris
Ximin Luo Jan. 20, 2018, 5:14 p.m. UTC | #10
Boris Kolpackov:
> Ximin Luo <infinity0@pwned.gg> writes:
>> Boris Kolpackov:
>>
>>> This does feel like we are trying to fix the issue in the wrong place.
>>> Also, won't such broken packages normally store all options (including
>>> -I) rather than just CFLAGS? And if the answer is no, then perhaps you
>>> could put -ffile-prefix-map into CPPFLAGS instead of CFLAGS? Kind of
>>> even feels right seeing that it affects the preprocessor.
>>>
>>
>> The broad issue affects -fdebug-prefix-map as well, not just __FILE__.
> 
> -ffile-prefix-map is the "common" option that (currently)
> takes care of both debug and macro remapping (and should we
> add any new -f*-prefix-map, those as well).
> 
>> Such packages are not "broken"; GCC itself stores command-line
>> arguments in DW_AT_producer and as mentioned there was a bug to
>> special-case filtering -fdebug-prefix-map out of that.
>>
>> Now we'll have to add exceptions for -ffile-prefix-map and all
>> the other flags you added, and everything else that might be
>> added in future.
> 
> -ffile-prefix-map and -fmacro-prefix-map are already excluded.
> And there is a note at the appropriate place in source code to
> exclude any new -f*-prefix-map should they be added.
> 

Fair enough, I didn't look at your entire patch. But don't you think stripping the whole value is unnecessary? One advantage of the ENV: approach is that we can remove this filtering, and then the =$to part can be present in DW_AT_producer, indicating the fact that the flag was indeed used, and what the $to value was. That would be helpful for debugging actually.

> You also haven't answered my question about -I: don't the
> projects that embed CFLAGS also embed CPPFLAGS (and thus
> -I's with absolute/varying paths)? And if the answer is
> no, then can't you use the same approach to make
> -ffile-prefix-map a non-issue?
> 

-I to an absolute path is not that common for system / distro-built stuff. In the cases that it occurs, indeed it could and should be fixed by the package buildsystem, e.g. by stripping a prefix when they add -I flags to CFLAGS. But that's a separate issue from what we're talking about here.

One major use-case for this flag, is to be set for all builds by the distribution. (Setting it per-package would be much much more work.) In this case, even if the build is using relative paths for -I and setting $PWD to an relative path for __FILE__ (and this behaviour is not explicitly mentioned in any standard), as long as they save CFLAGS/CPPFLAGS their build would not be reproducible.

I don't think it would be fair to impose this behaviour on packages, even if they are doing *everything else correctly* for reproducibility purposes.

For example this one, the unreproducibility diff exists because we temporarily reverted to an unpatched dpkg (i.e. it sets -fdebug-prefix-map rather than using the envvar supported by our patched GCC):

https://tests.reproducible-builds.org/debian/rb-pkg/unstable/amd64/diffoscope-results/freeradius.html

(Then there are other programs that like to put cflags/cppflags in their --help text for debugging purposes, etc.)

It does *everything else right* but the presence of that system-wide flag causes it to be unreproducible. If GCC does not support an envvar or other similar method to pass in this flag, then these packages are the ones that suffer.

X
Boris Kolpackov Jan. 21, 2018, 2:43 p.m. UTC | #11
Ximin Luo <infinity0@pwned.gg> writes:

> -I to an absolute path is not that common for system / distro-built
> stuff.

Ok, thanks for clarifying.


> In the cases that it occurs, indeed it could and should be fixed
> by the package buildsystem, e.g. by stripping a prefix when they
> add -I flags to CFLAGS. But that's a separate issue from what
> we're talking about here.

I believe it is the same issue: any package that blindly embeds
information about how it was built into the result of the build
does not care about reproducible builds.

If others do agree that this should be "fixed" in GCC, then I
would suggest that you add a separate option for the environment
variable case (e.g., -ffile-prefix-map-env) and sidestep the
whole "how to portably distinguish a path from an environment
variable" issue.

Boris
Ximin Luo Jan. 22, 2018, 2:01 p.m. UTC | #12
Boris Kolpackov:
> Ximin Luo <infinity0@pwned.gg> writes:
> 
>> -I to an absolute path is not that common for system / distro-built
>> stuff.
> 
> Ok, thanks for clarifying.
> 
> 
>> In the cases that it occurs, indeed it could and should be fixed
>> by the package buildsystem, e.g. by stripping a prefix when they
>> add -I flags to CFLAGS. But that's a separate issue from what
>> we're talking about here.
> 
> I believe it is the same issue: any package that blindly embeds
> information about how it was built into the result of the build
> does not care about reproducible builds.
> 

I don't think that's realistic position to take, and it seems like a fairly arbitrary assertion made merely to "brush this issue under the carpet". I think this because of the "implicit contract" of how CFLAGS is generally used:

CFLAGS (and other flags) is a way to pass information from multiple layers of programs down to GCC. It is necessary to "blindly embed" it if you want to save it *at all*, otherwise you are forced to violate the separation-of-concerns between layers in your buildsystem stack.

- Program A sets CFLAGS += --flags_A, then calls program B
- Program B sets CFLAGS += --flags_B, then calls program C
- Program C sets CFLAGS += --flags_C, then calls Make
- Make reads CFLAGS then calls $(CC) $(CFLAGS) [etc]

In general, you are not supposed to edit what was already in CFLAGS, you are supposed to append to it. That is why it's good practise to add "--no" options for various command-line flags in gcc (and other tools).

So each program X has its own flags_X that it is "responsible for". If all programs in the stack makes sure their own flags_X contain reproducible values, then the whole build will be reproducible even when recording CFLAGS.

However, this breaks in the case of -f*-prefix-map. If Program A sets this flag in flags_A, then this interferes with Program C, even though flags_C was fully reproducible.

Note that program B and C don't even know that they are being used with GCC, and so really have no business filtering -f*-prefix-map out of the CFLAGS that program A already set.

My proposal would allow all the flags_{A,B,C} to be reproducible, and avoid different programs having to embed layer-violating logic into themselves.

> If others do agree that this should be "fixed" in GCC, then I
> would suggest that you add a separate option for the environment
> variable case (e.g., -ffile-prefix-map-env) and sidestep the
> whole "how to portably distinguish a path from an environment
> variable" issue.
> 

That would also be fine for me sure.

X
diff mbox series

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 5db78558c0c..ba09b747b02 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1280,6 +1280,7 @@  OBJS = \
 	expmed.o \
 	expr.o \
 	fibonacci_heap.o \
+        file-prefix-map.o \
 	final.o \
 	fixed-value.o \
 	fold-const.o \
@@ -3486,8 +3487,8 @@  PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
   tree-ssa-loop-niter.h tree-ssa-ter.h tree-ssa-threadedge.h \
   tree-ssa-threadupdate.h inchash.h wide-int.h signop.h hash-map.h \
   hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h cfgcleanup.h \
-  lcm.h cfgloopmanip.h builtins.def chkp-builtins.def pass-instances.def \
-  params.list
+  lcm.h cfgloopmanip.h file-prefix-map.h builtins.def chkp-builtins.def \
+  pass-instances.def params.list
 
 # generate the 'build fragment' b-header-vars
 s-header-vars: Makefile
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 650de0d9aca..14016fe6312 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -70,6 +70,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "case-cfn-macros.h"
 #include "gimple-fold.h"
 #include "intl.h"
+#include "file-prefix-map.h" /* remap_macro_filename()  */
 
 struct target_builtins default_target_builtins;
 #if SWITCHABLE_TARGET
@@ -8784,7 +8785,13 @@  static inline tree
 fold_builtin_FILE (location_t loc)
 {
   if (const char *fname = LOCATION_FILE (loc))
+  {
+    /* The documentation says this builtin is equivalent to the preprocessor
+       __FILE__ macro so it appears appropriate to use the same file prefix
+       mappings.  */
+    fname = remap_macro_filename (fname);
     return build_string_literal (strlen (fname) + 1, fname);
+  }
 
   return build_string_literal (1, "");
 }
diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index 8342800303a..9d61abe545d 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -27,6 +27,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "c-pragma.h"
 #include "debug.h"
+#include "file-prefix-map.h" /* remap_macro_filename()  */
 
 #include "attribs.h"
 
@@ -82,6 +83,7 @@  init_c_lex (void)
   cb->has_attribute = c_common_has_attribute;
   cb->get_source_date_epoch = cb_get_source_date_epoch;
   cb->get_suggestion = cb_get_suggestion;
+  cb->remap_filename = remap_macro_filename;
 
   /* Set the debug callbacks if we can use them.  */
   if ((debug_info_level == DINFO_LEVEL_VERBOSE
diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index cead15e7a63..04b8c6cd40a 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -40,6 +40,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"		/* For PLUGIN_INCLUDE_FILE event.  */
 #include "mkdeps.h"
 #include "dumpfile.h"
+#include "file-prefix-map.h"    /* add_*_prefix_map()  */
 
 #ifndef DOLLARS_IN_IDENTIFIERS
 # define DOLLARS_IN_IDENTIFIERS true
@@ -448,6 +449,10 @@  c_common_handle_option (size_t scode, const char *arg, int value,
       cpp_opts->dollars_in_ident = value;
       break;
 
+    case OPT_fmacro_prefix_map_:
+      add_macro_prefix_map (arg);
+      break;
+
     case OPT_ffreestanding:
       value = !value;
       /* Fall through.  */
diff --git a/gcc/c-family/c-ppoutput.c b/gcc/c-family/c-ppoutput.c
index d1c92379f62..949a4955923 100644
--- a/gcc/c-family/c-ppoutput.c
+++ b/gcc/c-family/c-ppoutput.c
@@ -22,6 +22,7 @@ 
 #include "c-common.h"		/* For flags.  */
 #include "../libcpp/internal.h"
 #include "c-pragma.h"		/* For parse_in.  */
+#include "file-prefix-map.h"    /* remap_macro_filename()  */
 
 /* Encapsulates state used to convert a stream of tokens into a text
    file.  */
@@ -151,6 +152,7 @@  init_pp_output (FILE *out_stream)
 
   cb->has_attribute = c_common_has_attribute;
   cb->get_source_date_epoch = cb_get_source_date_epoch;
+  cb->remap_filename = remap_macro_filename;
 
   /* Initialize the print structure.  */
   print.src_line = 1;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 479ae63bb0e..9c6b8113ebf 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1386,6 +1386,10 @@  fdollars-in-identifiers
 C ObjC C++ ObjC++
 Permit '$' as an identifier character.
 
+fmacro-prefix-map=
+C ObjC C++ ObjC++ Joined RejectNegative
+-fmacro-prefix-map=<old>=<new> Map one directory name to another in __FILE__, __BASE_FILE__, and __builtin_FILE().
+
 fdump-ada-spec
 C ObjC C++ ObjC++ RejectNegative Var(flag_dump_ada_spec)
 Write all declarations as Ada code transitively.
diff --git a/gcc/common.opt b/gcc/common.opt
index f8f2ed3db8a..31524e1e4fd 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1175,7 +1175,11 @@  Common RejectNegative Joined Var(common_deferred_options) Defer
 
 fdebug-prefix-map=
 Common Joined RejectNegative Var(common_deferred_options) Defer
-Map one directory name to another in debug information.
+-fdebug-prefix-map=<old>=<new> Map one directory name to another in debug information.
+
+ffile-prefix-map=
+Common Joined RejectNegative Var(common_deferred_options) Defer
+-ffile-prefix-map=<old>=<new> Map one directory name to another in compilation result.
 
 fdebug-types-section
 Common Report Var(flag_debug_types_section) Init(0)
diff --git a/gcc/dbxout.c b/gcc/dbxout.c
index 49a858339f8..e4aa16f635a 100644
--- a/gcc/dbxout.c
+++ b/gcc/dbxout.c
@@ -91,6 +91,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "common/common-target.h"
 #include "langhooks.h"
 #include "expr.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 #ifdef XCOFF_DEBUGGING_INFO
 #include "xcoffout.h"
diff --git a/gcc/debug.h b/gcc/debug.h
index 277d990c20f..22fc7e7ab20 100644
--- a/gcc/debug.h
+++ b/gcc/debug.h
@@ -246,9 +246,6 @@  extern bool dwarf2out_do_frame (void);
 extern bool dwarf2out_do_cfi_asm (void);
 extern void dwarf2out_switch_text_section (void);
 
-const char *remap_debug_filename (const char *);
-void add_debug_prefix_map (const char *);
-
 /* For -fdump-go-spec.  */
 
 extern const struct gcc_debug_hooks *
diff --git a/gcc/doc/cppopts.texi b/gcc/doc/cppopts.texi
index 16bf22a6f38..09eb88fff97 100644
--- a/gcc/doc/cppopts.texi
+++ b/gcc/doc/cppopts.texi
@@ -287,6 +287,16 @@  When this option is given no argument, the default parameter value is
 
 Note that @code{-ftrack-macro-expansion=2} is activated by default.
 
+@item -fmacro-prefix-map=@var{old}=@var{new}
+@opindex fmacro-prefix-map
+When preprocessing files in directory @file{@var{old}}, expand the
+@code{__FILE__} and @code{__BASE_FILE__} macros as in @file{@var{new}}
+instead.  This can be used to change an absolute path to a relative
+path by using @file{.} for @var{new} which can result in more
+reproducible builds that are location independent.  This option also
+affects @code{__builtin_FILE()} during compilation.  See also
+@option{-ffile-prefix-map}.
+
 @item -fexec-charset=@var{charset}
 @opindex fexec-charset
 @cindex character set, execution
@@ -534,4 +544,3 @@  token in the output is preceded by the dump of the map its location
 belongs to.
 
 When used from GCC without @option{-E}, this option has no effect.
-
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 85c980bdfc9..a5edd9fa092 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -172,7 +172,8 @@  in the following sections.
 @gccoptlist{-c  -S  -E  -o @var{file}  -x @var{language}  @gol
 -v  -###  --help@r{[}=@var{class}@r{[},@dots{}@r{]]}  --target-help  --version @gol
 -pass-exit-codes  -pipe  -specs=@var{file}  -wrapper  @gol
-@@@var{file}  -fplugin=@var{file}  -fplugin-arg-@var{name}=@var{arg}  @gol
+@@@var{file}  -ffile-prefix-map=@var{old}=@var{new}  @gol
+-fplugin=@var{file}  -fplugin-arg-@var{name}=@var{arg}  @gol
 -fdump-ada-spec@r{[}-slim@r{]}  -fada-spec-parent=@var{unit} -fdump-go-spec=@var{file}}
 
 @item C Language Options
@@ -480,9 +481,9 @@  Objective-C and Objective-C++ Dialects}.
 -dD  -dI  -dM  -dN  -dU @gol
 -fdebug-cpp  -fdirectives-only  -fdollars-in-identifiers  @gol
 -fexec-charset=@var{charset}  -fextended-identifiers  @gol
--finput-charset=@var{charset}  -fno-canonical-system-headers @gol
--fpch-deps  -fpch-preprocess  -fpreprocessed @gol
--ftabstop=@var{width}  -ftrack-macro-expansion  @gol
+-finput-charset=@var{charset} -fmacro-prefix-map=@var{old}=@var{new}  @gol
+-fno-canonical-system-headers @gol -fpch-deps  -fpch-preprocess  @gol
+-fpreprocessed -ftabstop=@var{width}  -ftrack-macro-expansion  @gol
 -fwide-exec-charset=@var{charset}  -fworking-directory @gol
 -H  -imacros @var{file}  -include @var{file} @gol
 -M  -MD  -MF  -MG  -MM  -MMD  -MP  -MQ  -MT @gol
@@ -1652,6 +1653,15 @@  This invokes all subprograms of @command{gcc} under
 @samp{gdb --args}, thus the invocation of @command{cc1} is
 @samp{gdb --args cc1 @dots{}}.
 
+@item -ffile-prefix-map=@var{old}=@var{new}
+@opindex ffile-prefix-map
+When compiling files in directory @file{@var{old}}, record any references
+to them in the result of the compilation as in @file{@var{new}} instead.
+Specifying this option is equivalent to specifying all the individual
+@option{-f*-prefix-map} options. This can be used to make reproducible
+builds that are location independent.  See also @option{-fmacro-prefix-map}
+and @option{-fdebug-prefix-map}.
+
 @item -fplugin=@var{name}.so
 @opindex fplugin
 Load the plugin code in file @var{name}.so, assumed to be a
@@ -7059,7 +7069,7 @@  used to replace a build-time path with an install-time path in the debug info.
 It can also be used to change an absolute path to a relative path by using
 @file{.} for @var{new}.  This can give more reproducible builds, which are
 location independent, but may require an extra command to tell GDB where to
-find the source files.
+find the source files. See also @option{-ffile-prefix-map}.
 
 @item -fvar-tracking
 @opindex fvar-tracking
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 76a538f1ff9..83aec7d7f3d 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -94,6 +94,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "rtl-iter.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 static void dwarf2out_source_line (unsigned int, unsigned int, const char *,
 				   int, bool);
@@ -23408,6 +23409,8 @@  gen_producer_string (void)
       case OPT_fltrans_output_list_:
       case OPT_fresolution_:
       case OPT_fdebug_prefix_map_:
+      case OPT_fmacro_prefix_map_:
+      case OPT_ffile_prefix_map_:
       case OPT_fcompare_debug:
 	/* Ignore these.  */
 	continue;
diff --git a/gcc/file-prefix-map.c b/gcc/file-prefix-map.c
new file mode 100644
index 00000000000..c8b6af51d40
--- /dev/null
+++ b/gcc/file-prefix-map.c
@@ -0,0 +1,127 @@ 
+/* Implementation of file prefix remapping support (-f*-prefix-map options).
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This program 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, or (at your option) any
+   later version.
+
+   This program 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; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "diagnostic.h"
+#include "file-prefix-map.h"
+
+/* Structure recording the mapping from source file and directory names at
+   compile time to those to be embedded in the compilation result (debug
+   information, the __FILE__ macro expansion, etc).  */
+struct file_prefix_map
+{
+  const char *old_prefix;
+  const char *new_prefix;
+  size_t old_len;
+  size_t new_len;
+  struct file_prefix_map *next;
+};
+
+/* Record a file prefix mapping in the specified map.  ARG is the argument to
+   -f*-prefix-map and must be of the form OLD=NEW.  OPT is the option name
+   for diagnostics.  */
+static void
+add_prefix_map (file_prefix_map *&maps, const char *arg, const char *opt)
+{
+  file_prefix_map *map;
+  const char *p;
+
+  p = strchr (arg, '=');
+  if (!p)
+    {
+      error ("invalid argument %qs to %s", arg, opt);
+      return;
+    }
+  map = XNEW (file_prefix_map);
+  map->old_prefix = xstrndup (arg, p - arg);
+  map->old_len = p - arg;
+  p++;
+  map->new_prefix = xstrdup (p);
+  map->new_len = strlen (p);
+  map->next = maps;
+  maps = map;
+}
+
+/* Perform user-specified mapping of filename prefixes.  Return the new name
+   corresponding to FILENAME.  */
+
+static const char *
+remap_filename (file_prefix_map *maps, const char *filename)
+{
+  file_prefix_map *map;
+  char *s;
+  const char *name;
+  size_t name_len;
+
+  for (map = maps; map; map = map->next)
+    if (filename_ncmp (filename, map->old_prefix, map->old_len) == 0)
+      break;
+  if (!map)
+    return filename;
+  name = filename + map->old_len;
+  name_len = strlen (name) + 1;
+
+  s = (char *) ggc_alloc_atomic (name_len + map->new_len);
+  memcpy (s, map->new_prefix, map->new_len);
+  memcpy (s + map->new_len, name, name_len);
+  return s;
+}
+
+/* NOTE: if adding another -f*-prefix-map option then don't forget to
+   ignore it in DW_AT_producer (dwarf2out.c).  */
+
+/* Linked lists of file_prefix_map structures.  */
+static file_prefix_map *macro_prefix_maps; /* -fmacro-prefix-map  */
+static file_prefix_map *debug_prefix_maps; /* -fdebug-prefix-map  */
+
+/* Record a file prefix mapping for -fmacro-prefix-map.  */
+void
+add_macro_prefix_map (const char *arg)
+{
+  add_prefix_map (macro_prefix_maps, arg, "-fmacro-prefix-map");
+}
+
+/* Record a file prefix mapping for -fdebug-prefix-map.  */
+void
+add_debug_prefix_map (const char *arg)
+{
+  add_prefix_map (debug_prefix_maps, arg, "-fdebug-prefix-map");
+}
+
+/* Record a file prefix mapping for all -f*-prefix-map.  */
+void
+add_file_prefix_map (const char *arg)
+{
+  add_prefix_map (macro_prefix_maps, arg, "-ffile-prefix-map");
+  add_prefix_map (debug_prefix_maps, arg, "-ffile-prefix-map");
+}
+
+/* Remap using -fmacro-prefix-map.  */
+const char *
+remap_macro_filename (const char *filename)
+{
+  return remap_filename (macro_prefix_maps, filename);
+}
+
+/* Remap using -fdebug-prefix-map.  */
+const char *
+remap_debug_filename (const char *filename)
+{
+  return remap_filename (debug_prefix_maps, filename);
+}
diff --git a/gcc/file-prefix-map.h b/gcc/file-prefix-map.h
new file mode 100644
index 00000000000..3309eeed646
--- /dev/null
+++ b/gcc/file-prefix-map.h
@@ -0,0 +1,28 @@ 
+/* Declarations for file prefix remapping support (-f*-prefix-map options).
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This program 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, or (at your option) any
+   later version.
+
+   This program 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; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_FILE_PREFIX_MAP_H
+#define GCC_FILE_PREFIX_MAP_H
+
+void add_macro_prefix_map (const char *);
+void add_debug_prefix_map (const char *);
+void add_file_prefix_map (const char *);
+
+const char *remap_macro_filename (const char *);
+const char *remap_debug_filename (const char *);
+
+#endif /* !GCC_FILE_PREFIX_MAP_H  */
diff --git a/gcc/final.c b/gcc/final.c
index fe35a36dbbf..edba0d42697 100644
--- a/gcc/final.c
+++ b/gcc/final.c
@@ -1501,71 +1501,6 @@  asm_str_count (const char *templ)
   return count;
 }
 
-/* ??? This is probably the wrong place for these.  */
-/* Structure recording the mapping from source file and directory
-   names at compile time to those to be embedded in debug
-   information.  */
-struct debug_prefix_map
-{
-  const char *old_prefix;
-  const char *new_prefix;
-  size_t old_len;
-  size_t new_len;
-  struct debug_prefix_map *next;
-};
-
-/* Linked list of such structures.  */
-static debug_prefix_map *debug_prefix_maps;
-
-
-/* Record a debug file prefix mapping.  ARG is the argument to
-   -fdebug-prefix-map and must be of the form OLD=NEW.  */
-
-void
-add_debug_prefix_map (const char *arg)
-{
-  debug_prefix_map *map;
-  const char *p;
-
-  p = strchr (arg, '=');
-  if (!p)
-    {
-      error ("invalid argument %qs to -fdebug-prefix-map", arg);
-      return;
-    }
-  map = XNEW (debug_prefix_map);
-  map->old_prefix = xstrndup (arg, p - arg);
-  map->old_len = p - arg;
-  p++;
-  map->new_prefix = xstrdup (p);
-  map->new_len = strlen (p);
-  map->next = debug_prefix_maps;
-  debug_prefix_maps = map;
-}
-
-/* Perform user-specified mapping of debug filename prefixes.  Return
-   the new name corresponding to FILENAME.  */
-
-const char *
-remap_debug_filename (const char *filename)
-{
-  debug_prefix_map *map;
-  char *s;
-  const char *name;
-  size_t name_len;
-
-  for (map = debug_prefix_maps; map; map = map->next)
-    if (filename_ncmp (filename, map->old_prefix, map->old_len) == 0)
-      break;
-  if (!map)
-    return filename;
-  name = filename + map->old_len;
-  name_len = strlen (name) + 1;
-  s = (char *) alloca (name_len + map->new_len);
-  memcpy (s, map->new_prefix, map->new_len);
-  memcpy (s + map->new_len, name, name_len);
-  return ggc_strdup (s);
-}
 
 /* Return true if DWARF2 debug info can be emitted for DECL.  */
 
diff --git a/gcc/opts-global.c b/gcc/opts-global.c
index 343dbd3ac2c..aadc2ef880b 100644
--- a/gcc/opts-global.c
+++ b/gcc/opts-global.c
@@ -38,6 +38,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "file-prefix-map.h" /* add_*_prefix_map()  */
 
 typedef const char *const_char_p; /* For DEF_VEC_P.  */
 
@@ -365,6 +366,10 @@  handle_common_deferred_options (void)
 	  add_debug_prefix_map (opt->arg);
 	  break;
 
+	case OPT_ffile_prefix_map_:
+	  add_file_prefix_map (opt->arg);
+	  break;
+
 	case OPT_fdump_:
 	  if (!g->get_dumps ()->dump_switch_p (opt->arg))
 	    error ("unrecognized command line option %<-fdump-%s%>", opt->arg);
diff --git a/gcc/opts.c b/gcc/opts.c
index ac383d48ec1..9b9a9e2e734 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -2075,6 +2075,7 @@  common_handle_option (struct gcc_options *opts,
       break;
 
     case OPT_fdebug_prefix_map_:
+    case OPT_ffile_prefix_map_:
       /* Deferred.  */
       break;
 
diff --git a/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
new file mode 100644
index 00000000000..cf14de84a0d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/ffile-prefix-map.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile } */
+/* { dg-options "-ffile-prefix-map==FILE-PREFIX" } */
+
+#pragma message __FILE__       /* { dg-message "FILE-PREFIX" } */
+#pragma message __BASE_FILE__  /* { dg-message "FILE-PREFIX" } */
diff --git a/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c b/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c
new file mode 100644
index 00000000000..2618146247b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/fmacro-prefix-map.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fmacro-prefix-map==MACRO-PREFIX" } */
+
+#pragma message __FILE__       /* { dg-message "MACRO-PREFIX" } */
+#pragma message __BASE_FILE__  /* { dg-message "MACRO-PREFIX" } */
diff --git a/gcc/testsuite/c-c++-common/ffile-prefix-map.c b/gcc/testsuite/c-c++-common/ffile-prefix-map.c
new file mode 100644
index 00000000000..9b22eb6f06a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ffile-prefix-map.c
@@ -0,0 +1,12 @@ 
+/* Test __builtin_FILE(). */
+/* { dg-do run } */
+/* { dg-options "-ffile-prefix-map==FILE-PREFIX" } */
+
+#include <stdio.h>
+
+int main ()
+{
+  printf ("%s\n", __builtin_FILE ());
+}
+
+/* { dg-output "FILE-PREFIX" } */
diff --git a/gcc/testsuite/c-c++-common/fmacro-prefix-map.c b/gcc/testsuite/c-c++-common/fmacro-prefix-map.c
new file mode 100644
index 00000000000..e3dee68d8a7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/fmacro-prefix-map.c
@@ -0,0 +1,12 @@ 
+/* Test __builtin_FILE(). */
+/* { dg-do run } */
+/* { dg-options "-fmacro-prefix-map==MACRO-PREFIX" } */
+
+#include <stdio.h>
+
+int main ()
+{
+  printf ("%s\n", __builtin_FILE ());
+}
+
+/* { dg-output "MACRO-PREFIX" } */
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 1b35a99d688..cac338711ee 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -55,6 +55,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "asan.h"
 #include "rtl-iter.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 #ifdef XCOFF_DEBUGGING_INFO
 #include "xcoffout.h"		/* Needed for external data declarations.  */
diff --git a/gcc/vmsdbgout.c b/gcc/vmsdbgout.c
index 580dd284016..a0958e0f971 100644
--- a/gcc/vmsdbgout.c
+++ b/gcc/vmsdbgout.c
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "function.h"
 #include "target.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 /* Difference in seconds between the VMS Epoch and the Unix Epoch */
 static const long long vms_epoch_offset = 3506716800ll;
diff --git a/gcc/xcoffout.c b/gcc/xcoffout.c
index cf2064d5ba5..afda2f83e82 100644
--- a/gcc/xcoffout.c
+++ b/gcc/xcoffout.c
@@ -32,6 +32,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "output.h"
 #include "debug.h"
+#include "file-prefix-map.h" /* remap_debug_filename()  */
 
 #ifdef XCOFF_DEBUGGING_INFO
 
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 101b33aef48..ea3bb36fd1b 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -622,6 +622,10 @@  struct cpp_callbacks
      C++-style comments it does not include the terminating newline.  */
   void (*comment) (cpp_reader *, source_location, const unsigned char *,
 		   size_t);
+
+  /* Callback for filename remapping in __FILE__ and __BASE_FILE__ macro
+     expansions.  */
+  const char *(*remap_filename) (const char*);
 };
 
 #ifdef VMS
diff --git a/libcpp/macro.c b/libcpp/macro.c
index bf473eae358..35c98aaf4c9 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -450,6 +450,8 @@  _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node,
 	    if (!name)
 	      abort ();
 	  }
+	if (pfile->cb.remap_filename)
+	  name = pfile->cb.remap_filename (name);
 	len = strlen (name);
 	buf = _cpp_unaligned_alloc (pfile, len * 2 + 3);
 	result = buf;