diff options
Diffstat (limited to 'gcc/gcc.c')
-rw-r--r-- | gcc/gcc.c | 938 |
1 files changed, 765 insertions, 173 deletions
@@ -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 (); } |