aboutsummaryrefslogtreecommitdiff
path: root/gcc/gcc.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/gcc.c')
-rw-r--r--gcc/gcc.c938
1 files changed, 765 insertions, 173 deletions
diff --git a/gcc/gcc.c b/gcc/gcc.c
index b0d0308..8c851d7 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -270,12 +270,36 @@ static const char *target_sysroot_hdrs_suffix = 0;
static enum save_temps {
SAVE_TEMPS_NONE, /* no -save-temps */
SAVE_TEMPS_CWD, /* -save-temps in current directory */
+ SAVE_TEMPS_DUMP, /* -save-temps in dumpdir */
SAVE_TEMPS_OBJ /* -save-temps in object directory */
} save_temps_flag;
-/* Output file to use to get the object directory for -save-temps=obj */
-static char *save_temps_prefix = 0;
-static size_t save_temps_length = 0;
+/* Set this iff the dumppfx implied by a -save-temps=* option is to
+ override a -dumpdir option, if any. */
+static bool save_temps_overrides_dumpdir = false;
+
+/* -dumpdir, -dumpbase and -dumpbase-ext flags passed in, possibly
+ rearranged as they are to be passed down, e.g., dumpbase and
+ dumpbase_ext may be cleared if integrated with dumpdir or
+ dropped. */
+static char *dumpdir, *dumpbase, *dumpbase_ext;
+
+/* Usually the length of the string in dumpdir. However, during
+ linking, it may be shortened to omit a driver-added trailing dash,
+ by then replaced with a trailing period, that is still to be passed
+ to sub-processes in -dumpdir, but not to be generally used in spec
+ filename expansions. See maybe_run_linker. */
+static size_t dumpdir_length = 0;
+
+/* Set if the last character in dumpdir is (or was) a dash that the
+ driver added to dumpdir after dumpbase or linker output name. */
+static bool dumpdir_trailing_dash_added = false;
+
+/* Basename of dump and aux outputs, computed from dumpbase (given or
+ derived from output name), to override input_basename in non-%w %b
+ et al. */
+static char *outbase;
+static size_t outbase_length = 0;
/* The compiler version. */
@@ -402,13 +426,16 @@ static const char *find_plugindir_spec_function (int, const char **);
static const char *print_asm_header_spec_function (int, const char **);
static const char *compare_debug_dump_opt_spec_function (int, const char **);
static const char *compare_debug_self_opt_spec_function (int, const char **);
-static const char *compare_debug_auxbase_opt_spec_function (int, const char **);
static const char *pass_through_libs_spec_func (int, const char **);
-static const char *replace_extension_spec_func (int, const char **);
+static const char *dumps_spec_func (int, const char **);
static const char *greater_than_spec_func (int, const char **);
static const char *debug_level_greater_than_spec_func (int, const char **);
static const char *find_fortran_preinclude_file (int, const char **);
static char *convert_white_space (char *);
+static char *quote_spec (char *);
+static char *quote_spec_arg (char *);
+static bool not_actual_file_p (const char *);
+
/* The Specs Language
@@ -426,12 +453,19 @@ expanding these sequences; therefore, you can concatenate them together
or with constant text in a single argument.
%% substitute one % into the program name or argument.
+ %" substitute an empty argument.
%i substitute the name of the input file being processed.
- %b substitute the basename of the input file being processed.
- This is the substring up to (and not including) the last period
- and not including the directory unless -save-temps was specified
- to put temporaries in a different location.
- %B same as %b, but include the file suffix (text after the last period).
+ %b substitute the basename for outputs related with the input file
+ being processed. This is often a substring of the input file name,
+ up to (and not including) the last period but, unless %w is active,
+ it is affected by the directory selected by -save-temps=*, by
+ -dumpdir, and, in case of multiple compilations, even by -dumpbase
+ and -dumpbase-ext and, in case of linking, by the linker output
+ name. When %w is active, it derives the main output name only from
+ the input file base name; when it is not, it names aux/dump output
+ file.
+ %B same as %b, but include the input file suffix (text after the last
+ period).
%gSUFFIX
substitute a file name that has suffix SUFFIX and is chosen
once per compilation, and mark the argument a la %d. To reduce
@@ -641,10 +675,10 @@ proper position among the other output files. */
#define ASM_FINAL_SPEC \
"%{gsplit-dwarf: \n\
objcopy --extract-dwo \
- %{c:%{o*:%*}%{!o*:%b%O}}%{!c:%U%O} \
- %{c:%{o*:%:replace-extension(%{o*:%*} .dwo)}%{!o*:%b.dwo}}%{!c:%b.dwo} \n\
+ %{c:%{o*:%*}%{!o*:%w%b%O}}%{!c:%U%O} \
+ %b.dwo \n\
objcopy --strip-dwo \
- %{c:%{o*:%*}%{!o*:%b%O}}%{!c:%U%O} \
+ %{c:%{o*:%*}%{!o*:%w%b%O}}%{!c:%U%O} \
}"
#endif
@@ -1144,22 +1178,20 @@ static const char *cpp_options =
/* This contains cpp options which are not passed when the preprocessor
output will be used by another program. */
-static const char *cpp_debug_options = "%{d*}";
+static const char *cpp_debug_options = "%<dumpdir %<dumpbase %<dumpbase-ext %{d*} %:dumps()";
/* NB: This is shared amongst all front-ends, except for Ada. */
static const char *cc1_options =
"%{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
%{!iplugindir*:%{fplugin*:%:find-plugindir()}}\
- %1 %{!Q:-quiet} %{!dumpbase:-dumpbase %B} %{d*} %{m*} %{aux-info*}\
- %{fcompare-debug-second:%:compare-debug-auxbase-opt(%b)} \
- %{!fcompare-debug-second:%{c|S:%{o*:-auxbase-strip %*}%{!o*:-auxbase %b}}}%{!c:%{!S:-auxbase %b}} \
+ %1 %{!Q:-quiet} %(cpp_debug_options) %{m*} %{aux-info*}\
%{g*} %{O*} %{W*&pedantic*} %{w} %{std*&ansi&trigraphs}\
%{v:-version} %{pg:-p} %{p} %{f*} %{undef}\
%{Qn:-fno-ident} %{Qy:} %{-help:--help}\
%{-target-help:--target-help}\
%{-version:--version}\
%{-help=*:--help=%*}\
- %{!fsyntax-only:%{S:%W{o*}%{!o*:-o %b.s}}}\
+ %{!fsyntax-only:%{S:%W{o*}%{!o*:-o %w%b.s}}}\
%{fsyntax-only:-o %j} %{-param*}\
%{coverage:-fprofile-arcs -ftest-coverage}\
%{fprofile-arcs|fprofile-generate*|coverage:\
@@ -1647,9 +1679,8 @@ static const struct spec_function static_spec_functions[] =
{ "print-asm-header", print_asm_header_spec_function },
{ "compare-debug-dump-opt", compare_debug_dump_opt_spec_function },
{ "compare-debug-self-opt", compare_debug_self_opt_spec_function },
- { "compare-debug-auxbase-opt", compare_debug_auxbase_opt_spec_function },
{ "pass-through-libs", pass_through_libs_spec_func },
- { "replace-extension", replace_extension_spec_func },
+ { "dumps", dumps_spec_func },
{ "gt", greater_than_spec_func },
{ "debug-level-gt", debug_level_greater_than_spec_func },
{ "fortran-preinclude-file", find_fortran_preinclude_file},
@@ -4145,7 +4176,8 @@ driver_handle_option (struct gcc_options *opts,
return true;
case OPT_save_temps:
- save_temps_flag = SAVE_TEMPS_CWD;
+ if (!save_temps_flag)
+ save_temps_flag = SAVE_TEMPS_DUMP;
validated = true;
break;
@@ -4158,6 +4190,23 @@ driver_handle_option (struct gcc_options *opts,
else
fatal_error (input_location, "%qs is an unknown %<-save-temps%> option",
decoded->orig_option_with_args_text);
+ save_temps_overrides_dumpdir = true;
+ break;
+
+ case OPT_dumpdir:
+ free (dumpdir);
+ dumpdir = xstrdup (arg);
+ save_temps_overrides_dumpdir = false;
+ break;
+
+ case OPT_dumpbase:
+ free (dumpbase);
+ dumpbase = xstrdup (arg);
+ break;
+
+ case OPT_dumpbase_ext:
+ free (dumpbase_ext);
+ dumpbase_ext = xstrdup (arg);
break;
case OPT_no_canonical_prefixes:
@@ -4264,8 +4313,6 @@ driver_handle_option (struct gcc_options *opts,
arg = convert_filename (arg, ! have_c, 0);
#endif
output_file = arg;
- /* Save the output name in case -save-temps=obj was used. */
- save_temps_prefix = xstrdup (arg);
/* On some systems, ld cannot handle "-o" without a space. So
split the option from its argument. */
save_switch ("-o", 1, &arg, validated, true);
@@ -4308,6 +4355,19 @@ driver_handle_option (struct gcc_options *opts,
return true;
}
+/* Return true if F2 is F1 followed by a single suffix, i.e., by a
+ period and additional characters other than a period. */
+
+static inline bool
+adds_single_suffix_p (const char *f2, const char *f1)
+{
+ size_t len = strlen (f1);
+
+ return (strncmp (f1, f2, len) == 0
+ && f2[len] == '.'
+ && strchr (f2 + len + 1, '.') == NULL);
+}
+
/* Put the driver's standard set of option handlers in *HANDLERS. */
static void
@@ -4324,6 +4384,32 @@ set_option_handlers (struct cl_option_handlers *handlers)
handlers->handlers[2].mask = CL_TARGET;
}
+
+/* Return the index into infiles for the single non-library
+ non-lto-wpa input file, -1 if there isn't any, or -2 if there is
+ more than one. */
+static inline int
+single_input_file_index ()
+{
+ int ret = -1;
+
+ for (int i = 0; i < n_infiles; i++)
+ {
+ if (infiles[i].language
+ && (infiles[i].language[0] == '*'
+ || (flag_wpa
+ && strcmp (infiles[i].language, "lto") == 0)))
+ continue;
+
+ if (ret != -1)
+ return -2;
+
+ ret = i;
+ }
+
+ return ret;
+}
+
/* Create the vector `switches' and its contents.
Store its length in `n_switches'. */
@@ -4636,23 +4722,371 @@ process_command (unsigned int decoded_options_count,
if (output_file != NULL && output_file[0] == '\0')
fatal_error (input_location, "output filename may not be empty");
+ /* -dumpdir and -save-temps=* both specify the location of aux/dump
+ outputs; the one that appears last prevails. When compiling
+ multiple sources, an explicit dumpbase (minus -ext) may be
+ combined with an explicit or implicit dumpdir, whereas when
+ linking, a specified or implied link output name (minus
+ extension) may be combined with a prevailing -save-temps=* or an
+ otherwise implied dumpdir, but not override a prevailing
+ -dumpdir. Primary outputs (e.g., linker output when linking
+ without -o, or .i, .s or .o outputs when processing multiple
+ inputs with -E, -S or -c, respectively) are NOT affected by these
+ -save-temps=/-dump* options, always landing in the current
+ directory and with the same basename as the input when an output
+ name is not given, but when they're intermediate outputs, they
+ are named like other aux outputs, so the options affect their
+ location and name.
+
+ Here are some examples. There are several more in the
+ documentation of -o and -dump*, and some quite exhaustive tests
+ in gcc.misc-tests/outputs.exp.
+
+ When compiling any number of sources, no -dump* nor
+ -save-temps=*, all outputs in cwd without prefix:
+
+ # gcc -c b.c -gsplit-dwarf
+ -> cc1 [-dumpdir ./] -dumpbase b.c -dumpbase-ext .c # b.o b.dwo
+
+ # gcc -c b.c d.c -gsplit-dwarf
+ -> cc1 [-dumpdir ./] -dumpbase b.c -dumpbase-ext .c # b.o b.dwo
+ && cc1 [-dumpdir ./] -dumpbase d.c -dumpbase-ext .c # d.o d.dwo
+
+ When compiling and linking, no -dump* nor -save-temps=*, .o
+ outputs are temporary, aux outputs land in the dir of the output,
+ prefixed with the basename of the linker output:
+
+ # gcc b.c d.c -o ab -gsplit-dwarf
+ -> cc1 -dumpdir ab- -dumpbase b.c -dumpbase-ext .c # ab-b.dwo
+ && cc1 -dumpdir ab- -dumpbase d.c -dumpbase-ext .c # ab-d.dwo
+ && link ... -o ab
+
+ # gcc b.c d.c [-o a.out] -gsplit-dwarf
+ -> cc1 -dumpdir a- -dumpbase b.c -dumpbase-ext .c # a-b.dwo
+ && cc1 -dumpdir a- -dumpbase d.c -dumpbase-ext .c # a-d.dwo
+ && link ... [-o a.out]
+
+ When compiling and linking, a prevailing -dumpdir fully overrides
+ the prefix of aux outputs given by the output name:
+
+ # gcc -dumpdir f b.c d.c -gsplit-dwarf [-o [dir/]whatever]
+ -> cc1 -dumpdir f -dumpbase b.c -dumpbase-ext .c # fb.dwo
+ && cc1 -dumpdir f -dumpbase d.c -dumpbase-ext .c # fd.dwo
+ && link ... [-o whatever]
+
+ When compiling multiple inputs, an explicit -dumpbase is combined
+ with -dumpdir, affecting aux outputs, but not the .o outputs:
+
+ # gcc -dumpdir f -dumpbase g- b.c d.c -gsplit-dwarf -c
+ -> cc1 -dumpdir fg- -dumpbase b.c -dumpbase-ext .c # b.o fg-b.dwo
+ && cc1 -dumpdir fg- -dumpbase d.c -dumpbase-ext .c # d.o fg-d.dwo
+
+ When compiling and linking with -save-temps, the .o outputs that
+ would have been temporary become aux outputs, so they get
+ affected by -dump* flags:
+
+ # gcc -dumpdir f -dumpbase g- -save-temps b.c d.c
+ -> cc1 -dumpdir fg- -dumpbase b.c -dumpbase-ext .c # fg-b.o
+ && cc1 -dumpdir fg- -dumpbase d.c -dumpbase-ext .c # fg-d.o
+ && link
+
+ If -save-temps=* prevails over -dumpdir, however, the explicit
+ -dumpdir is discarded, as if it wasn't there. The basename of
+ the implicit linker output, a.out or a.exe, becomes a- as the aux
+ output prefix for all compilations:
+
+ # gcc [-dumpdir f] -save-temps=cwd b.c d.c
+ -> cc1 -dumpdir a- -dumpbase b.c -dumpbase-ext .c # a-b.o
+ && cc1 -dumpdir a- -dumpbase d.c -dumpbase-ext .c # a-d.o
+ && link
+
+ A single -dumpbase, applying to multiple inputs, overrides the
+ linker output name, implied or explicit, as the aux output prefix:
+
+ # gcc [-dumpdir f] -dumpbase g- -save-temps=cwd b.c d.c
+ -> cc1 -dumpdir g- -dumpbase b.c -dumpbase-ext .c # g-b.o
+ && cc1 -dumpdir g- -dumpbase d.c -dumpbase-ext .c # g-d.o
+ && link
+
+ # gcc [-dumpdir f] -dumpbase g- -save-temps=cwd b.c d.c -o dir/h.out
+ -> cc1 -dumpdir g- -dumpbase b.c -dumpbase-ext .c # g-b.o
+ && cc1 -dumpdir g- -dumpbase d.c -dumpbase-ext .c # g-d.o
+ && link -o dir/h.out
+
+ Now, if the linker output is NOT overridden as a prefix, but
+ -save-temps=* overrides implicit or explicit -dumpdir, the
+ effective dump dir combines the dir selected by the -save-temps=*
+ option with the basename of the specified or implied link output:
+
+ # gcc [-dumpdir f] -save-temps=cwd b.c d.c -o dir/h.out
+ -> cc1 -dumpdir h- -dumpbase b.c -dumpbase-ext .c # h-b.o
+ && cc1 -dumpdir h- -dumpbase d.c -dumpbase-ext .c # h-d.o
+ && link -o dir/h.out
+
+ # gcc [-dumpdir f] -save-temps=obj b.c d.c -o dir/h.out
+ -> cc1 -dumpdir dir/h- -dumpbase b.c -dumpbase-ext .c # dir/h-b.o
+ && cc1 -dumpdir dir/h- -dumpbase d.c -dumpbase-ext .c # dir/h-d.o
+ && link -o dir/h.out
+
+ But then again, a single -dumpbase applying to multiple inputs
+ gets used instead of the linker output basename in the combined
+ dumpdir:
+
+ # gcc [-dumpdir f] -dumpbase g- -save-temps=obj b.c d.c -o dir/h.out
+ -> cc1 -dumpdir dir/g- -dumpbase b.c -dumpbase-ext .c # dir/g-b.o
+ && cc1 -dumpdir dir/g- -dumpbase d.c -dumpbase-ext .c # dir/g-d.o
+ && link -o dir/h.out
+
+ With a single input being compiled, the output basename does NOT
+ affect the dumpdir prefix.
+
+ # gcc -save-temps=obj b.c -gsplit-dwarf -c -o dir/b.o
+ -> cc1 -dumpdir dir/ -dumpbase b.c -dumpbase-ext .c # dir/b.o dir/b.dwo
+
+ but when compiling and linking even a single file, it does:
+
+ # gcc -save-temps=obj b.c -o dir/h.out
+ -> cc1 -dumpdir dir/h- -dumpbase b.c -dumpbase-ext .c # dir/h-b.o
+
+ unless an explicit -dumpdir prevails:
+
+ # gcc -save-temps[=obj] -dumpdir g- b.c -o dir/h.out
+ -> cc1 -dumpdir g- -dumpbase b.c -dumpbase-ext .c # g-b.o
+
+ */
+
+ bool explicit_dumpdir = dumpdir;
+
+ if (!save_temps_overrides_dumpdir && explicit_dumpdir)
+ {
+ /* Do nothing. */
+ }
+
/* If -save-temps=obj and -o name, create the prefix to use for %b.
Otherwise just make -save-temps=obj the same as -save-temps=cwd. */
- if (save_temps_flag == SAVE_TEMPS_OBJ && save_temps_prefix != NULL)
+ else if (save_temps_flag != SAVE_TEMPS_CWD && output_file != NULL)
+ {
+ free (dumpdir);
+ dumpdir = NULL;
+ temp = lbasename (output_file);
+ if (temp != output_file)
+ dumpdir = xstrndup (output_file,
+ strlen (output_file) - strlen (temp));
+ }
+ else if (dumpdir)
+ {
+ free (dumpdir);
+ dumpdir = NULL;
+ }
+
+ if (save_temps_flag)
+ save_temps_flag = SAVE_TEMPS_DUMP;
+
+ /* If there is any pathname component in an explicit -dumpbase, it
+ overrides dumpdir entirely, so discard it right away. Although
+ the presence of an explicit -dumpdir matters for the driver, it
+ shouldn't matter for other processes, that get all that's needed
+ from the -dumpdir and -dumpbase always passed to them. */
+ if (dumpdir && dumpbase && lbasename (dumpbase) != dumpbase)
{
- save_temps_length = strlen (save_temps_prefix);
- temp = strrchr (lbasename (save_temps_prefix), '.');
- if (temp)
+ free (dumpdir);
+ dumpdir = NULL;
+ }
+
+ /* Check that dumpbase_ext matches the end of dumpbase, drop it
+ otherwise. */
+ if (dumpbase_ext && dumpbase && *dumpbase)
+ {
+ int lendb = strlen (dumpbase);
+ int lendbx = strlen (dumpbase_ext);
+
+ if (lendbx >= lendb
+ || strcmp (dumpbase + lendb - lendbx, dumpbase_ext) != 0)
{
- save_temps_length -= strlen (temp);
- save_temps_prefix[save_temps_length] = '\0';
+ free (dumpbase_ext);
+ dumpbase_ext = NULL;
}
+ }
+
+ /* -dumpbase with multiple sources goes into dumpdir. With a single
+ source, it does only if linking and if dumpdir was not explicitly
+ specified. */
+ if (dumpbase && *dumpbase
+ && (single_input_file_index () == -2
+ || (!have_c && !explicit_dumpdir)))
+ {
+ char *prefix;
+ if (dumpbase_ext)
+ /* We checked that they match above. */
+ dumpbase[strlen (dumpbase) - strlen (dumpbase_ext)] = '\0';
+
+ if (dumpdir)
+ prefix = concat (dumpdir, dumpbase, "-", NULL);
+ else
+ prefix = concat (dumpbase, "-", NULL);
+
+ free (dumpdir);
+ free (dumpbase);
+ free (dumpbase_ext);
+ dumpbase = dumpbase_ext = NULL;
+ dumpdir = prefix;
+ dumpdir_trailing_dash_added = true;
+ }
+
+ /* If dumpbase was not brought into dumpdir but we're linking, bring
+ output_file into dumpdir unless dumpdir was explicitly specified.
+ The test for !explicit_dumpdir is further below, because we want
+ to use the obase computation for a ghost outbase, passed to
+ GCC_COLLECT_OPTIONS. */
+ else if (!have_c && (!explicit_dumpdir || (dumpbase && !*dumpbase)))
+ {
+ /* If we get here, we know dumpbase was not specified, or it was
+ specified as an empty string. If it was anything else, it
+ would have combined with dumpdir above, because the condition
+ for dumpbase to be used when present is broader than the
+ condition that gets us here. */
+ gcc_assert (!dumpbase || !*dumpbase);
+
+ const char *obase;
+ char *tofree = NULL;
+ if (!output_file || not_actual_file_p (output_file))
+ obase = "a";
+ else
+ {
+ obase = lbasename (output_file);
+ size_t blen = strlen (obase), xlen;
+ /* Drop the suffix if it's dumpbase_ext, if given,
+ otherwise .exe or the target executable suffix, or if the
+ output was explicitly named a.out, but not otherwise. */
+ if (dumpbase_ext
+ ? (blen > (xlen = strlen (dumpbase_ext))
+ && strcmp ((temp = (obase + blen - xlen)),
+ dumpbase_ext) == 0)
+ : ((temp = strrchr (obase + 1, '.'))
+ && (xlen = strlen (temp))
+ && (strcmp (temp, ".exe") == 0
+#if defined(HAVE_TARGET_EXECUTABLE_SUFFIX)
+ || strcmp (temp, TARGET_EXECUTABLE_SUFFIX) == 0
+#endif
+ || strcmp (obase, "a.out") == 0)))
+ {
+ tofree = xstrndup (obase, blen - xlen);
+ obase = tofree;
+ }
+ }
+
+ /* We wish to save this basename to the -dumpdir passed through
+ GCC_COLLECT_OPTIONS within maybe_run_linker, for e.g. LTO,
+ but we do NOT wish to add it to e.g. %b, so we keep
+ outbase_length as zero. */
+ gcc_assert (!outbase);
+ outbase_length = 0;
+
+ /* If we're building [dir1/]foo[.exe] out of a single input
+ [dir2/]foo.c that shares the same basename, dump to
+ [dir2/]foo.c.* rather than duplicating the basename into
+ [dir2/]foo-foo.c.*. */
+ int idxin;
+ if (dumpbase
+ || ((idxin = single_input_file_index ()) >= 0
+ && adds_single_suffix_p (lbasename (infiles[idxin].name),
+ obase)))
+ {
+ if (obase == tofree)
+ outbase = tofree;
+ else
+ {
+ outbase = xstrdup (obase);
+ free (tofree);
+ }
+ obase = tofree = NULL;
+ }
+ else
+ {
+ if (dumpdir)
+ {
+ char *p = concat (dumpdir, obase, "-", NULL);
+ free (dumpdir);
+ dumpdir = p;
+ }
+ else
+ dumpdir = concat (obase, "-", NULL);
+
+ dumpdir_trailing_dash_added = true;
+
+ free (tofree);
+ obase = tofree = NULL;
+ }
+
+ if (!explicit_dumpdir || dumpbase)
+ {
+ /* Absent -dumpbase and present -dumpbase-ext have been applied
+ to the linker output name, so compute fresh defaults for each
+ compilation. */
+ free (dumpbase_ext);
+ dumpbase_ext = NULL;
+ }
+ }
+
+ /* Now, if we're compiling, or if we haven't used the dumpbase
+ above, then outbase (%B) is derived from dumpbase, if given, or
+ from the output name, given or implied. We can't precompute
+ implied output names, but that's ok, since they're derived from
+ input names. Just make sure we skip this if dumpbase is the
+ empty string: we want to use input names then, so don't set
+ outbase. */
+ if ((dumpbase || have_c)
+ && !(dumpbase && !*dumpbase))
+ {
+ gcc_assert (!outbase);
+
+ if (dumpbase)
+ {
+ gcc_assert (single_input_file_index () != -2);
+ /* We do not want lbasename here; dumpbase with dirnames
+ overrides dumpdir entirely, even if dumpdir is
+ specified. */
+ if (dumpbase_ext)
+ /* We've already checked above that the suffix matches. */
+ outbase = xstrndup (dumpbase,
+ strlen (dumpbase) - strlen (dumpbase_ext));
+ else
+ outbase = xstrdup (dumpbase);
+ }
+ else if (output_file && !not_actual_file_p (output_file))
+ {
+ outbase = xstrdup (lbasename (output_file));
+ char *p = strrchr (outbase + 1, '.');
+ if (p)
+ *p = '\0';
+ }
+
+ if (outbase)
+ outbase_length = strlen (outbase);
}
- else if (save_temps_prefix != NULL)
+
+ /* If there is any pathname component in an explicit -dumpbase, do
+ not use dumpdir, but retain it to pass it on to the compiler. */
+ if (dumpdir)
+ dumpdir_length = strlen (dumpdir);
+ else
+ dumpdir_length = 0;
+
+ /* Check that dumpbase_ext, if still present, still matches the end
+ of dumpbase, if present, and drop it otherwise. We only retained
+ it above when dumpbase was absent to maybe use it to drop the
+ extension from output_name before combining it with dumpdir. */
+ if (dumpbase_ext)
{
- free (save_temps_prefix);
- save_temps_prefix = NULL;
+ if (!dumpbase)
+ {
+ free (dumpbase_ext);
+ dumpbase_ext = NULL;
+ }
+ else
+ gcc_assert (strcmp (dumpbase + strlen (dumpbase)
+ - strlen (dumpbase_ext), dumpbase_ext) == 0);
}
if (save_temps_flag && use_pipes)
@@ -4848,6 +5282,28 @@ set_collect_gcc_options (void)
obstack_grow (&collect_obstack, "'", 1);
}
}
+
+ if (dumpdir)
+ {
+ if (!first_time)
+ obstack_grow (&collect_obstack, " ", 1);
+ first_time = FALSE;
+
+ obstack_grow (&collect_obstack, "'-dumpdir' '", 12);
+ const char *p, *q;
+
+ q = dumpdir;
+ while ((p = strchr (q, '\'')))
+ {
+ obstack_grow (&collect_obstack, q, p - q);
+ obstack_grow (&collect_obstack, "'\\''", 4);
+ q = ++p;
+ }
+ obstack_grow (&collect_obstack, q, strlen (q));
+
+ obstack_grow (&collect_obstack, "'", 1);
+ }
+
obstack_grow (&collect_obstack, "\0", 1);
xputenv (XOBFINISH (&collect_obstack, char *));
}
@@ -5366,22 +5822,33 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part)
fatal_error (input_location, "spec %qs invalid", spec);
case 'b':
- if (save_temps_length)
- obstack_grow (&obstack, save_temps_prefix, save_temps_length);
- else
+ /* Don't use %b in the linker command. */
+ gcc_assert (suffixed_basename_length);
+ if (!this_is_output_file && dumpdir_length)
+ obstack_grow (&obstack, dumpdir, dumpdir_length);
+ if (this_is_output_file || !outbase_length)
obstack_grow (&obstack, input_basename, basename_length);
+ else
+ obstack_grow (&obstack, outbase, outbase_length);
if (compare_debug < 0)
obstack_grow (&obstack, ".gk", 3);
arg_going = 1;
break;
case 'B':
- if (save_temps_length)
- obstack_grow (&obstack, save_temps_prefix, save_temps_length);
+ /* Don't use %B in the linker command. */
+ gcc_assert (suffixed_basename_length);
+ if (!this_is_output_file && dumpdir_length)
+ obstack_grow (&obstack, dumpdir, dumpdir_length);
+ if (this_is_output_file || !outbase_length)
+ obstack_grow (&obstack, input_basename, basename_length);
else
- obstack_grow (&obstack, input_basename, suffixed_basename_length);
+ obstack_grow (&obstack, outbase, outbase_length);
if (compare_debug < 0)
obstack_grow (&obstack, ".gk", 3);
+ obstack_grow (&obstack, input_basename + basename_length,
+ suffixed_basename_length - basename_length);
+
arg_going = 1;
break;
@@ -5534,42 +6001,44 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part)
suffix_length += 3;
}
- /* If -save-temps=obj and -o were specified, use that for the
+ /* If -save-temps was specified, use that for the
temp file. */
- if (save_temps_length)
- {
- char *tmp;
- temp_filename_length
- = save_temps_length + suffix_length + 1;
- tmp = (char *) alloca (temp_filename_length);
- memcpy (tmp, save_temps_prefix, save_temps_length);
- memcpy (tmp + save_temps_length, suffix, suffix_length);
- tmp[save_temps_length + suffix_length] = '\0';
- temp_filename = save_string (tmp, save_temps_length
- + suffix_length);
- obstack_grow (&obstack, temp_filename,
- temp_filename_length);
- arg_going = 1;
- delete_this_arg = 0;
- break;
- }
-
- /* If the gcc_input_filename has the same suffix specified
- for the %g, %u, or %U, and -save-temps is specified,
- we could end up using that file as an intermediate
- thus clobbering the user's source file (.e.g.,
- gcc -save-temps foo.s would clobber foo.s with the
- output of cpp0). So check for this condition and
- generate a temp file as the intermediate. */
-
if (save_temps_flag)
{
char *tmp;
- temp_filename_length = basename_length + suffix_length + 1;
+ bool adjusted_suffix = false;
+ if (suffix_length
+ && !outbase_length && !basename_length
+ && !dumpdir_trailing_dash_added)
+ {
+ adjusted_suffix = true;
+ suffix++;
+ suffix_length--;
+ }
+ temp_filename_length
+ = dumpdir_length + suffix_length + 1;
+ if (!outbase_length)
+ temp_filename_length += basename_length;
+ else
+ temp_filename_length += outbase_length;
tmp = (char *) alloca (temp_filename_length);
- memcpy (tmp, input_basename, basename_length);
- memcpy (tmp + basename_length, suffix, suffix_length);
- tmp[basename_length + suffix_length] = '\0';
+ if (dumpdir_length)
+ memcpy (tmp, dumpdir, dumpdir_length);
+ if (!outbase_length)
+ memcpy (tmp + dumpdir_length, input_basename,
+ basename_length);
+ else
+ memcpy (tmp + dumpdir_length, outbase,
+ outbase_length);
+ memcpy (tmp + temp_filename_length - suffix_length - 1,
+ suffix, suffix_length);
+ if (adjusted_suffix)
+ {
+ adjusted_suffix = false;
+ suffix--;
+ suffix_length++;
+ }
+ tmp[temp_filename_length - 1] = '\0';
temp_filename = tmp;
if (filename_cmp (temp_filename, gcc_input_filename) != 0)
@@ -6080,6 +6549,14 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part)
}
break;
+ case '"':
+ /* End a previous argument, if there is one, then issue an
+ empty argument. */
+ end_going_arg ();
+ arg_going = 1;
+ end_going_arg ();
+ break;
+
default:
error ("spec failure: unrecognized spec option %qc", c);
break;
@@ -6090,6 +6567,9 @@ do_spec_1 (const char *spec, int inswitch, const char *soft_matched_part)
/* Backslash: treat next character as ordinary. */
c = *p++;
+ /* When adding more cases that previously matched default, make
+ sure to adjust quote_spec_char_p as well. */
+
/* Fall through. */
default:
/* Ordinary character: put it into the current argument. */
@@ -8296,6 +8776,40 @@ driver::maybe_run_linker (const char *argv0) const
if (explicit_link_files[i] || outfiles[i] != NULL)
num_linker_inputs++;
+ /* Arrange for temporary file names created during linking to take
+ on names related with the linker output rather than with the
+ inputs when appropriate. */
+ if (outbase && *outbase)
+ {
+ if (dumpdir)
+ {
+ char *tofree = dumpdir;
+ gcc_checking_assert (strlen (dumpdir) == dumpdir_length);
+ dumpdir = concat (dumpdir, outbase, ".", NULL);
+ free (tofree);
+ }
+ else
+ dumpdir = concat (outbase, ".", NULL);
+ dumpdir_length += strlen (outbase) + 1;
+ dumpdir_trailing_dash_added = true;
+ }
+ else if (dumpdir_trailing_dash_added)
+ {
+ gcc_assert (dumpdir[dumpdir_length - 1] == '-');
+ dumpdir[dumpdir_length - 1] = '.';
+ }
+
+ if (dumpdir_trailing_dash_added)
+ {
+ gcc_assert (dumpdir_length > 0);
+ gcc_assert (dumpdir[dumpdir_length - 1] == '.');
+ dumpdir_length--;
+ }
+
+ free (outbase);
+ input_basename = outbase = NULL;
+ outbase_length = suffixed_basename_length = basename_length = 0;
+
/* Run ld to link all the compiler output files. */
if (num_linker_inputs > 0 && !seen_error () && print_subprocess_help < 2)
@@ -9766,7 +10280,7 @@ compare_debug_dump_opt_spec_function (int arg,
do_spec_1 (" ", 0, NULL);
if (argbuf.length () > 0
- && strcmp (argv[argbuf.length () - 1], "."))
+ && strcmp (argv[argbuf.length () - 1], ".") != 0)
{
if (!compare_debug)
return NULL;
@@ -9776,25 +10290,22 @@ compare_debug_dump_opt_spec_function (int arg,
}
else
{
- const char *ext = NULL;
-
if (argbuf.length () > 0)
- {
- do_spec_2 ("%{o*:%*}%{!o:%{!S:%b%O}%{S:%b.s}}", NULL);
- ext = ".gkd";
- }
+ do_spec_2 ("%B.gkd", NULL);
else if (!compare_debug)
return NULL;
else
- do_spec_2 ("%g.gkd", NULL);
+ do_spec_2 ("%{!save-temps*:%g.gkd}%{save-temps*:%B.gkd}", NULL);
do_spec_1 (" ", 0, NULL);
gcc_assert (argbuf.length () > 0);
- name = concat (argbuf.last (), ext, NULL);
+ name = xstrdup (argbuf.last ());
- ret = concat ("-fdump-final-insns=", name, NULL);
+ char *arg = quote_spec (xstrdup (name));
+ ret = concat ("-fdump-final-insns=", arg, NULL);
+ free (arg);
}
which = compare_debug < 0;
@@ -9821,8 +10332,6 @@ compare_debug_dump_opt_spec_function (int arg,
return ret;
}
-static const char *debug_auxbase_opt;
-
/* %:compare-debug-self-opt spec function. Expands to the options
that are to be passed in the second compilation of
compare-debug. */
@@ -9838,16 +10347,6 @@ compare_debug_self_opt_spec_function (int arg,
if (compare_debug >= 0)
return NULL;
- do_spec_2 ("%{c|S:%{o*:%*}}", NULL);
- do_spec_1 (" ", 0, NULL);
-
- if (argbuf.length () > 0)
- debug_auxbase_opt = concat ("-auxbase-strip ",
- argbuf.last (),
- NULL);
- else
- debug_auxbase_opt = NULL;
-
return concat ("\
%<o %<MD %<MMD %<MF* %<MG %<MP %<MQ* %<MT* \
%<fdump-final-insns=* -w -S -o %j \
@@ -9855,50 +10354,6 @@ compare_debug_self_opt_spec_function (int arg,
", compare_debug_opt, NULL);
}
-/* %:compare-debug-auxbase-opt spec function. Expands to the auxbase
- options that are to be passed in the second compilation of
- compare-debug. It expects, as an argument, the basename of the
- current input file name, with the .gk suffix appended to it. */
-
-static const char *
-compare_debug_auxbase_opt_spec_function (int arg,
- const char **argv)
-{
- char *name;
- int len;
-
- if (arg == 0)
- fatal_error (input_location,
- "too few arguments to %%:compare-debug-auxbase-opt");
-
- if (arg != 1)
- fatal_error (input_location,
- "too many arguments to %%:compare-debug-auxbase-opt");
-
- if (compare_debug >= 0)
- return NULL;
-
- len = strlen (argv[0]);
- if (len < 3 || strcmp (argv[0] + len - 3, ".gk") != 0)
- fatal_error (input_location, "argument to %%:compare-debug-auxbase-opt "
- "does not end in %<.gk%>");
-
- if (debug_auxbase_opt)
- return debug_auxbase_opt;
-
-#define OPT "-auxbase "
-
- len -= 3;
- name = (char*) xmalloc (sizeof (OPT) + len);
- memcpy (name, OPT, sizeof (OPT) - 1);
- memcpy (name + sizeof (OPT) - 1, argv[0], len);
- name[sizeof (OPT) - 1 + len] = '\0';
-
-#undef OPT
-
- return name;
-}
-
/* %:pass-through-libs spec function. Finds all -l options and input
file names in the lib spec passed to it, and makes a list of them
prepended with the plugin option to cause them to be passed through
@@ -9942,34 +10397,105 @@ pass_through_libs_spec_func (int argc, const char **argv)
return prepended;
}
-/* %:replace-extension spec function. Replaces the extension of the
- first argument with the second argument. */
+static bool
+not_actual_file_p (const char *name)
+{
+ return (strcmp (name, "-") == 0
+ || strcmp (output_file, HOST_BIT_BUCKET) == 0);
+}
+/* %:dumps spec function. Take an optional argument that overrides
+ the default extension for -dumpbase and -dumpbase-ext.
+ Return -dumpdir, -dumpbase and -dumpbase-ext, if needed. */
const char *
-replace_extension_spec_func (int argc, const char **argv)
+dumps_spec_func (int argc, const char **argv ATTRIBUTE_UNUSED)
{
- char *name;
+ const char *ext = dumpbase_ext;
char *p;
- char *result;
- int i;
- if (argc != 2)
- fatal_error (input_location, "too few arguments to %%:replace-extension");
+ char *args[3] = { NULL, NULL, NULL };
+ int nargs = 0;
- name = xstrdup (argv[0]);
+ /* Do not compute a default for -dumpbase-ext when -dumpbase was
+ given explicitly. */
+ if (dumpbase && *dumpbase && !ext)
+ ext = "";
- for (i = strlen (name) - 1; i >= 0; i--)
- if (IS_DIR_SEPARATOR (name[i]))
- break;
+ if (argc == 1)
+ {
+ /* Do not override the explicitly-specified -dumpbase-ext with
+ the specs-provided overrider. */
+ if (!ext)
+ ext = argv[0];
+ }
+ else if (argc != 0)
+ fatal_error (input_location, "too many arguments for %%:dumps");
- p = strrchr (name + i + 1, '.');
- if (p != NULL)
- *p = '\0';
+ if (dumpdir)
+ {
+ p = quote_spec_arg (xstrdup (dumpdir));
+ args[nargs++] = concat (" -dumpdir ", p, NULL);
+ free (p);
+ }
- result = concat (name, argv[1], NULL);
+ if (!ext)
+ ext = input_basename + basename_length;
- free (name);
- return result;
+ /* Use the precomputed outbase, or compute dumpbase from
+ input_basename, just like %b would. */
+ char *base;
+
+ if (dumpbase && *dumpbase)
+ {
+ base = xstrdup (dumpbase);
+ p = base + outbase_length;
+ gcc_checking_assert (strncmp (base, outbase, outbase_length) == 0);
+ gcc_checking_assert (strcmp (p, ext) == 0);
+ }
+ else if (outbase_length)
+ {
+ base = xstrndup (outbase, outbase_length);
+ p = NULL;
+ }
+ else
+ {
+ base = xstrndup (input_basename, suffixed_basename_length);
+ p = base + basename_length;
+ }
+
+ if (compare_debug < 0 || !p || strcmp (p, ext) != 0)
+ {
+ if (p)
+ *p = '\0';
+
+ const char *gk;
+ if (compare_debug < 0)
+ gk = ".gk";
+ else
+ gk = "";
+
+ p = concat (base, gk, ext, NULL);
+
+ free (base);
+ base = p;
+ }
+
+ base = quote_spec_arg (base);
+ args[nargs++] = concat (" -dumpbase ", base, NULL);
+ free (base);
+
+ if (*ext)
+ {
+ p = quote_spec_arg (xstrdup (ext));
+ args[nargs++] = concat (" -dumpbase-ext ", p, NULL);
+ free (p);
+ }
+
+ const char *ret = concat (args[0], args[1], args[2], NULL);
+ while (nargs > 0)
+ free (args[--nargs]);
+
+ return ret;
}
/* Returns "" if ARGV[ARGC - 2] is greater than ARGV[ARGC-1].
@@ -10075,6 +10601,44 @@ find_fortran_preinclude_file (int argc, const char **argv)
return result;
}
+/* If any character in ORIG fits QUOTE_P (_, P), reallocate the string
+ so as to precede every one of them with a backslash. Return the
+ original string or the reallocated one. */
+
+static inline char *
+quote_string (char *orig, bool (*quote_p)(char, void *), void *p)
+{
+ int len, number_of_space = 0;
+
+ for (len = 0; orig[len]; len++)
+ if (quote_p (orig[len], p))
+ number_of_space++;
+
+ if (number_of_space)
+ {
+ char *new_spec = (char *) xmalloc (len + number_of_space + 1);
+ int j, k;
+ for (j = 0, k = 0; j <= len; j++, k++)
+ {
+ if (quote_p (orig[j], p))
+ new_spec[k++] = '\\';
+ new_spec[k] = orig[j];
+ }
+ free (orig);
+ return new_spec;
+ }
+ else
+ return orig;
+}
+
+/* Return true iff C is any of the characters convert_white_space
+ should quote. */
+
+static inline bool
+whitespace_to_convert_p (char c, void *)
+{
+ return (c == ' ' || c == '\t');
+}
/* Insert backslash before spaces in ORIG (usually a file path), to
avoid being broken by spec parser.
@@ -10102,26 +10666,50 @@ find_fortran_preinclude_file (int argc, const char **argv)
static char *
convert_white_space (char *orig)
{
- int len, number_of_space = 0;
+ return quote_string (orig, whitespace_to_convert_p, NULL);
+}
- for (len = 0; orig[len]; len++)
- if (orig[len] == ' ' || orig[len] == '\t') number_of_space++;
+/* Return true iff C matches any of the spec active characters. */
+static inline bool
+quote_spec_char_p (char c, void *)
+{
+ switch (c)
+ {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '|':
+ case '%':
+ case '\\':
+ return true;
- if (number_of_space)
+ default:
+ return false;
+ }
+}
+
+/* Like convert_white_space, but deactivate all active spec chars by
+ quoting them. */
+
+static inline char *
+quote_spec (char *orig)
+{
+ return quote_string (orig, quote_spec_char_p, NULL);
+}
+
+/* Like quote_spec, but also turn an empty string into the spec for an
+ empty argument. */
+
+static inline char *
+quote_spec_arg (char *orig)
+{
+ if (!*orig)
{
- char *new_spec = (char *) xmalloc (len + number_of_space + 1);
- int j, k;
- for (j = 0, k = 0; j <= len; j++, k++)
- {
- if (orig[j] == ' ' || orig[j] == '\t')
- new_spec[k++] = '\\';
- new_spec[k] = orig[j];
- }
free (orig);
- return new_spec;
- }
- else
- return orig;
+ return xstrdup ("%\"");
+ }
+
+ return quote_spec (orig);
}
/* Restore all state within gcc.c to the initial state, so that the driver
@@ -10158,8 +10746,14 @@ driver::finalize ()
target_sysroot_suffix = 0;
target_sysroot_hdrs_suffix = 0;
save_temps_flag = SAVE_TEMPS_NONE;
- save_temps_prefix = 0;
- save_temps_length = 0;
+ save_temps_overrides_dumpdir = false;
+ dumpdir_trailing_dash_added = false;
+ free (dumpdir);
+ free (dumpbase);
+ free (dumpbase_ext);
+ free (outbase);
+ dumpdir = dumpbase = dumpbase_ext = outbase = NULL;
+ dumpdir_length = outbase_length = 0;
spec_machine = DEFAULT_TARGET_MACHINE;
greatest_status = 1;
@@ -10298,8 +10892,6 @@ driver::finalize ()
mdswitches = NULL;
n_mdswitches = 0;
- debug_auxbase_opt = NULL;
-
used_arg.finalize ();
}