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. */
static void do_self_spec (const char *);
static const char *find_file (const char *);
static int is_directory (const char *, bool);
-static const char *validate_switches (const char *, bool);
+static const char *validate_switches (const char *, bool, bool);
static void validate_all_switches (void);
static inline void validate_switches_from_spec (const char *, bool);
static void give_switch (int, int);
static const char *getenv_spec_function (int, const char **);
static const char *if_exists_spec_function (int, const char **);
static const char *if_exists_else_spec_function (int, const char **);
+static const char *if_exists_then_else_spec_function (int, const char **);
static const char *sanitize_spec_function (int, const char **);
static const char *replace_outfile_spec_function (int, const char **);
static const char *remove_outfile_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 *dwarf_version_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 *);
+
\f
/* The Specs Language
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
%s current argument is the name of a library or startup file of some sort.
Search for that file in a standard list of directories
and substitute the full name found.
+ %T current argument is the name of a linker script.
+ Search for that file in the current list of directories to scan for
+ libraries. If the file is located, insert a --script option into the
+ command line followed by the full path name found. If the file is
+ not found then generate an error message.
+ Note: the current working directory is not searched.
%eSTR Print STR as an error message. STR is terminated by a newline.
Use this when inconsistent options are detected.
%nSTR Print STR as a notice. STR is terminated by a newline.
#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
#define LIBASAN_EARLY_SPEC ""
#endif
+#ifndef LIBHWASAN_SPEC
+#define STATIC_LIBHWASAN_LIBS \
+ " %{static-libhwasan|static:%:include(libsanitizer.spec)%(link_libhwasan)}"
+#ifdef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_SPEC STATIC_LIBHWASAN_LIBS
+#elif defined(HAVE_LD_STATIC_DYNAMIC)
+#define LIBHWASAN_SPEC "%{static-libhwasan:" LD_STATIC_OPTION \
+ "} -lhwasan %{static-libhwasan:" LD_DYNAMIC_OPTION "}" \
+ STATIC_LIBHWASAN_LIBS
+#else
+#define LIBHWASAN_SPEC "-lhwasan" STATIC_LIBHWASAN_LIBS
+#endif
+#endif
+
+#ifndef LIBHWASAN_EARLY_SPEC
+#define LIBHWASAN_EARLY_SPEC ""
+#endif
+
#ifndef LIBTSAN_SPEC
#define STATIC_LIBTSAN_LIBS \
" %{static-libtsan|static:%:include(libsanitizer.spec)%(link_libtsan)}"
#endif /* HAVE_LD_COMPRESS_DEBUG >= 2 */
/* Define ASM_DEBUG_SPEC to be a spec suitable for translating '-g'
- to the assembler. */
+ to the assembler, when compiling assembly sources only. */
#ifndef ASM_DEBUG_SPEC
+# if defined(HAVE_AS_GDWARF_5_DEBUG_FLAG) && defined(HAVE_AS_WORKING_DWARF_N_FLAG)
+/* If --gdwarf-N is supported and as can handle even compiler generated
+ .debug_line with it, supply --gdwarf-N in ASM_DEBUG_OPTION_SPEC rather
+ than in ASM_DEBUG_SPEC, so that it applies to both .s and .c etc.
+ compilations. */
+# define ASM_DEBUG_DWARF_OPTION ""
+# elif defined(HAVE_AS_GDWARF_5_DEBUG_FLAG)
+# define ASM_DEBUG_DWARF_OPTION "%{%:dwarf-version-gt(4):--gdwarf-5;" \
+ "%:dwarf-version-gt(3):--gdwarf-4;" \
+ "%:dwarf-version-gt(2):--gdwarf-3;" \
+ ":--gdwarf2}"
+# else
+# define ASM_DEBUG_DWARF_OPTION "--gdwarf2"
+# endif
# if defined(DBX_DEBUGGING_INFO) && defined(DWARF2_DEBUGGING_INFO) \
&& defined(HAVE_AS_GDWARF2_DEBUG_FLAG) && defined(HAVE_AS_GSTABS_DEBUG_FLAG)
# define ASM_DEBUG_SPEC \
(PREFERRED_DEBUGGING_TYPE == DBX_DEBUG \
? "%{%:debug-level-gt(0):" \
- "%{gdwarf*:--gdwarf2}%{!gdwarf*:%{g*:--gstabs}}}" ASM_MAP \
+ "%{gdwarf*:" ASM_DEBUG_DWARF_OPTION "};" \
+ ":%{g*:--gstabs}}" ASM_MAP \
: "%{%:debug-level-gt(0):" \
- "%{gstabs*:--gstabs}%{!gstabs*:%{g*:--gdwarf2}}}" ASM_MAP)
+ "%{gstabs*:--gstabs;" \
+ ":%{g*:" ASM_DEBUG_DWARF_OPTION "}}}" ASM_MAP)
# else
# if defined(DBX_DEBUGGING_INFO) && defined(HAVE_AS_GSTABS_DEBUG_FLAG)
# define ASM_DEBUG_SPEC "%{g*:%{%:debug-level-gt(0):--gstabs}}" ASM_MAP
# endif
# if defined(DWARF2_DEBUGGING_INFO) && defined(HAVE_AS_GDWARF2_DEBUG_FLAG)
-# define ASM_DEBUG_SPEC "%{g*:%{%:debug-level-gt(0):--gdwarf2}}" ASM_MAP
+# define ASM_DEBUG_SPEC "%{g*:%{%:debug-level-gt(0):" \
+ ASM_DEBUG_DWARF_OPTION "}}" ASM_MAP
# endif
# endif
#endif
# define ASM_DEBUG_SPEC ""
#endif
+/* Define ASM_DEBUG_OPTION_SPEC to be a spec suitable for translating '-g'
+ to the assembler when compiling all sources. */
+#ifndef ASM_DEBUG_OPTION_SPEC
+# if defined(HAVE_AS_GDWARF_5_DEBUG_FLAG) && defined(HAVE_AS_WORKING_DWARF_N_FLAG)
+# define ASM_DEBUG_OPTION_DWARF_OPT \
+ "%{%:dwarf-version-gt(4):--gdwarf-5 ;" \
+ "%:dwarf-version-gt(3):--gdwarf-4 ;" \
+ "%:dwarf-version-gt(2):--gdwarf-3 ;" \
+ ":--gdwarf2 }"
+# if defined(DBX_DEBUGGING_INFO) && defined(DWARF2_DEBUGGING_INFO)
+# define ASM_DEBUG_OPTION_SPEC \
+ (PREFERRED_DEBUGGING_TYPE == DBX_DEBUG \
+ ? "%{%:debug-level-gt(0):" \
+ "%{gdwarf*:" ASM_DEBUG_OPTION_DWARF_OPT "}}" \
+ : "%{%:debug-level-gt(0):" \
+ "%{!gstabs*:%{g*:" ASM_DEBUG_OPTION_DWARF_OPT "}}}")
+# elif defined(DWARF2_DEBUGGING_INFO)
+# define ASM_DEBUG_OPTION_SPEC "%{g*:%{%:debug-level-gt(0):" \
+ ASM_DEBUG_OPTION_DWARF_OPT "}}"
+# endif
+# endif
+#endif
+#ifndef ASM_DEBUG_OPTION_SPEC
+# define ASM_DEBUG_OPTION_SPEC ""
+#endif
+
/* Here is the spec for running the linker, after compiling all files. */
/* This is overridable by the target in case they need to specify the
#ifndef SANITIZER_EARLY_SPEC
#define SANITIZER_EARLY_SPEC "\
%{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_EARLY_SPEC "} \
+ %{%:sanitize(hwaddress):" LIBHWASAN_EARLY_SPEC "} \
%{%:sanitize(thread):" LIBTSAN_EARLY_SPEC "} \
%{%:sanitize(leak):" LIBLSAN_EARLY_SPEC "}}}}"
#endif
#define SANITIZER_SPEC "\
%{!nostdlib:%{!r:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_SPEC "\
%{static:%ecannot specify -static with -fsanitize=address}}\
+ %{%:sanitize(hwaddress):" LIBHWASAN_SPEC "\
+ %{static:%ecannot specify -static with -fsanitize=hwaddress}}\
%{%:sanitize(thread):" LIBTSAN_SPEC "\
%{static:%ecannot specify -static with -fsanitize=thread}}\
%{%:sanitize(undefined):" LIBUBSAN_SPEC "}\
#endif
static const char *asm_debug = ASM_DEBUG_SPEC;
+static const char *asm_debug_option = ASM_DEBUG_OPTION_SPEC;
static const char *cpp_spec = CPP_SPEC;
static const char *cc1_spec = CC1_SPEC;
static const char *cc1plus_spec = CC1PLUS_SPEC;
%{MD:-MD %{!o:%b.d}%{o*:%.d%*}}\
%{MMD:-MMD %{!o:%b.d}%{o*:%.d%*}}\
%{M} %{MM} %{MF*} %{MG} %{MP} %{MQ*} %{MT*}\
+ %{Mmodules} %{Mno-modules}\
%{!E:%{!M:%{!MM:%{!MT:%{!MQ:%{MD|MMD:%{o*:-MQ %*}}}}}}}\
- %{remap} %{g3|ggdb3|gstabs3|gxcoff3|gvms3:-dD}\
+ %{remap} %{%:debug-level-gt(2):-dD}\
%{!iplugindir*:%{fplugin*:%:find-plugindir()}}\
%{H} %C %{D*&U*&A*} %{i*} %Z %i\
%{E|M|MM:%W{o*}}";
%{!fno-working-directory:-fworking-directory}}} %{O*}\
%{undef} %{save-temps*:-fpch-preprocess}";
+/* Pass -d* flags, possibly modifying -dumpdir, -dumpbase et al.
+
+ Make it easy for a language to override the argument for the
+ %:dumps specs function call. */
+#define DUMPS_OPTIONS(EXTS) \
+ "%<dumpdir %<dumpbase %<dumpbase-ext %{d*} %:dumps(" EXTS ")"
+
/* 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 = DUMPS_OPTIONS ("");
/* 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:\
to the assembler equivalents. */
"%{v} %{w:-W} %{I*} "
#endif
+"%(asm_debug_option)"
ASM_COMPRESS_DEBUG_SPEC
"%a %Y %{c:%W{o*}%{!o*:-o %w%b%O}}%{!c:-o %d%w%u%O}";
{
INIT_STATIC_SPEC ("asm", &asm_spec),
INIT_STATIC_SPEC ("asm_debug", &asm_debug),
+ INIT_STATIC_SPEC ("asm_debug_option", &asm_debug_option),
INIT_STATIC_SPEC ("asm_final", &asm_final_spec),
INIT_STATIC_SPEC ("asm_options", &asm_options),
INIT_STATIC_SPEC ("invoke_as", &invoke_as),
{ "getenv", getenv_spec_function },
{ "if-exists", if_exists_spec_function },
{ "if-exists-else", if_exists_else_spec_function },
+ { "if-exists-then-else", if_exists_then_else_spec_function },
{ "sanitize", sanitize_spec_function },
{ "replace-outfile", replace_outfile_spec_function },
{ "remove-outfile", remove_outfile_spec_function },
{ "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 },
+ { "dwarf-version-gt", dwarf_version_greater_than_spec_func },
{ "fortran-preinclude-file", find_fortran_preinclude_file},
#ifdef EXTRA_SPEC_FUNCTIONS
EXTRA_SPEC_FUNCTIONS
specs = sl;
}
+
+/* Update the entry for SPEC in the static_specs table to point to VALUE,
+ ensuring that we free the previous value if necessary. Set alloc_p for the
+ entry to ALLOC_P: this determines whether we take ownership of VALUE (i.e.
+ whether we need to free it later on). */
+static void
+set_static_spec (const char **spec, const char *value, bool alloc_p)
+{
+ struct spec_list *sl = NULL;
+
+ for (unsigned i = 0; i < ARRAY_SIZE (static_specs); i++)
+ {
+ if (static_specs[i].ptr_spec == spec)
+ {
+ sl = static_specs + i;
+ break;
+ }
+ }
+
+ gcc_assert (sl);
+
+ if (sl->alloc_p)
+ {
+ const char *old = *spec;
+ free (const_cast <char *> (old));
+ }
+
+ *spec = value;
+ sl->alloc_p = alloc_p;
+}
+
+/* Update a static spec to a new string, taking ownership of that
+ string's memory. */
+static void set_static_spec_owned (const char **spec, const char *val)
+{
+ return set_static_spec (spec, val, true);
+}
+
+/* Update a static spec to point to a new value, but don't take
+ ownership of (i.e. don't free) that string. */
+static void set_static_spec_shared (const char **spec, const char *val)
+{
+ return set_static_spec (spec, val, false);
+}
+
\f
/* Change the value of spec NAME to SPEC. If SPEC is empty, then the spec is
removed; If the spec starts with a + then SPEC is added to the end of the
#if defined(HAVE_TARGET_EXECUTABLE_SUFFIX)
/* If there is no filetype, make it the executable suffix (which includes
the "."). But don't get confused if we have just "-o". */
- if (! do_exe || TARGET_EXECUTABLE_SUFFIX[0] == 0 || (len == 2 && name[0] == '-'))
+ if (! do_exe || TARGET_EXECUTABLE_SUFFIX[0] == 0 || not_actual_file_p (name))
return name;
for (i = len - 1; i >= 0; i--)
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;
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:
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);
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
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'. */
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)
+ {
+ free (dumpdir);
+ dumpdir = NULL;
+ }
+
+ /* Check that dumpbase_ext matches the end of dumpbase, drop it
+ otherwise. */
+ if (dumpbase_ext && dumpbase && *dumpbase)
{
- save_temps_length = strlen (save_temps_prefix);
- temp = strrchr (lbasename (save_temps_prefix), '.');
- if (temp)
+ int lendb = strlen (dumpbase);
+ int lendbx = strlen (dumpbase_ext);
+
+ /* -dumpbase-ext must be a suffix proper; discard it if it
+ matches all of -dumpbase, as that would make for an empty
+ basename. */
+ if (lendbx >= lendb
+ || strcmp (dumpbase + lendb - lendbx, dumpbase_ext) != 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)))
{
- save_temps_length -= strlen (temp);
- save_temps_prefix[save_temps_length] = '\0';
+ 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;
+ }
}
- else if (save_temps_prefix != 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))
{
- free (save_temps_prefix);
- save_temps_prefix = NULL;
+ 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);
+ }
+
+ /* 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. We
+ won't deal with -dumpbase-ext when -dumpbase is not explicitly
+ given, even if just to activate backward-compatible dumpbase:
+ dropping it on the floor is correct, expected and documented
+ behavior. Attempting to deal with a -dumpbase-ext that might
+ match the end of some input filename, or of the combination of
+ the output basename with the suffix of the input filename,
+ possible with an intermediate .gk extension for -fcompare-debug,
+ is just calling for trouble. */
+ if (dumpbase_ext)
+ {
+ if (!dumpbase || !*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)
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 *));
}
}
while ((p = strchr (p, ',')) != NULL);
- argbuf.safe_grow (old_length + n);
+ argbuf.safe_grow (old_length + n, true);
memmove (argbuf.address () + n,
argbuf.address (),
old_length * sizeof (const_char_p));
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;
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 += outbase_length;
+ else
+ temp_filename_length += basename_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, outbase,
+ outbase_length);
+ else if (basename_length)
+ memcpy (tmp + dumpdir_length, input_basename,
+ basename_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)
}
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;
/* 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. */
if (lto_wrapper_file)
{
lto_wrapper_file = convert_white_space (lto_wrapper_file);
- lto_wrapper_spec = lto_wrapper_file;
+ set_static_spec_owned (<o_wrapper_spec, lto_wrapper_file);
obstack_init (&collect_obstack);
obstack_grow (&collect_obstack, "COLLECT_LTO_WRAPPER=",
sizeof ("COLLECT_LTO_WRAPPER=") - 1);
{
printf (_("%s %s%s\n"), progname, pkgversion_string,
version_string);
- printf ("Copyright %s 2020 Free Software Foundation, Inc.\n",
+ printf ("Copyright %s 2021 Free Software Foundation, Inc.\n",
_("(C)"));
fputs (_("This is free software; see the source for copying conditions. There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"),
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)
{
char *s = find_a_file (&exec_prefixes, "collect2", X_OK, false);
if (s == NULL)
- linker_name_spec = "ld";
+ set_static_spec_shared (&linker_name_spec, "ld");
}
#if HAVE_LTO_PLUGIN > 0
linker_plugin_file_spec = convert_white_space (temp_spec);
}
#endif
- lto_gcc_spec = argv0;
+ set_static_spec_shared (<o_gcc_spec, argv0);
}
/* Rebuild the COMPILER_PATH and LIBRARY_PATH environment variables
|| (*p == 'W' && *++p == '{')
|| (*p == '@' && *++p == '{')))
/* We have a switch spec. */
- p = validate_switches (p + 1, user);
+ p = validate_switches (p + 1, user, *p == '{');
}
static void
validate_switches_from_spec (link_command_spec, false);
}
-/* Look at the switch-name that comes after START
- and mark as valid all supplied switches that match it. */
+/* Look at the switch-name that comes after START and mark as valid
+ all supplied switches that match it. If BRACED, handle other
+ switches after '|' and '&', and specs after ':' until ';' or '}',
+ going back for more switches after ';'. Without BRACED, handle
+ only one atom. Return a pointer to whatever follows the handled
+ items, after the closing brace if BRACED. */
static const char *
-validate_switches (const char *start, bool user_spec)
+validate_switches (const char *start, bool user_spec, bool braced)
{
const char *p = start;
const char *atom;
switches[i].validated = true;
}
+ if (!braced)
+ return p;
+
if (*p) p++;
if (*p && (p[-1] == '|' || p[-1] == '&'))
goto next_member;
{
p++;
if (*p == '{' || *p == '<')
- p = validate_switches (p+1, user_spec);
+ p = validate_switches (p+1, user_spec, *p == '{');
else if (p[0] == 'W' && p[1] == '{')
- p = validate_switches (p+2, user_spec);
+ p = validate_switches (p+2, user_spec, true);
else if (p[0] == '@' && p[1] == '{')
- p = validate_switches (p+2, user_spec);
+ p = validate_switches (p+2, user_spec, true);
}
else
p++;
const char *p = multilib_select;
const char *last_path = 0, *this_path;
int skip;
+ int not_arg;
unsigned int last_path_len = 0;
while (*p != '\0')
goto invalid_select;
if (*q == '!')
- arg = NULL;
+ {
+ not_arg = 1;
+ q++;
+ }
else
- arg = q;
+ not_arg = 0;
+ arg = q;
while (*q != ' ' && *q != ';')
{
++q;
}
- if (arg != NULL
- && default_arg (arg, q - arg))
+ if (default_arg (arg, q - arg))
{
- skip = 1;
- break;
+ /* Stop checking if any default arguments appeared in not
+ list. */
+ if (not_arg)
+ {
+ skip = 0;
+ break;
+ }
+ else
+ skip = 1;
}
if (*q == ' ')
return argv[1];
}
+/* if-exists-then-else built-in spec function.
+
+ Checks to see if the file specified by the absolute pathname in
+ the first arg exists. Returns the second arg if so, otherwise returns
+ the third arg if it is present. */
+
+static const char *
+if_exists_then_else_spec_function (int argc, const char **argv)
+{
+
+ /* Must have two or three arguments. */
+ if (argc != 2 && argc != 3)
+ return NULL;
+
+ if (IS_ABSOLUTE_PATH (argv[0]) && ! access (argv[0], R_OK))
+ return argv[1];
+
+ if (argc == 3)
+ return argv[2];
+
+ return NULL;
+}
+
/* sanitize built-in spec function.
This returns non-NULL, if sanitizing address, thread or
if (strcmp (argv[0], "address") == 0)
return (flag_sanitize & SANITIZE_USER_ADDRESS) ? "" : NULL;
+ if (strcmp (argv[0], "hwaddress") == 0)
+ return (flag_sanitize & SANITIZE_USER_HWADDRESS) ? "" : NULL;
if (strcmp (argv[0], "kernel-address") == 0)
return (flag_sanitize & SANITIZE_KERNEL_ADDRESS) ? "" : NULL;
+ if (strcmp (argv[0], "kernel-hwaddress") == 0)
+ return (flag_sanitize & SANITIZE_KERNEL_HWADDRESS) ? "" : NULL;
if (strcmp (argv[0], "thread") == 0)
return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
if (strcmp (argv[0], "undefined") == 0)
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;
}
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;
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. */
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 \
", 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
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 (name, 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");
+
+ if (dumpdir)
+ {
+ p = quote_spec_arg (xstrdup (dumpdir));
+ args[nargs++] = concat (" -dumpdir ", p, NULL);
+ free (p);
+ }
- p = strrchr (name + i + 1, '.');
- if (p != NULL)
- *p = '\0';
+ if (!ext)
+ ext = input_basename + basename_length;
- result = concat (name, argv[1], NULL);
+ /* Use the precomputed outbase, or compute dumpbase from
+ input_basename, just like %b would. */
+ char *base;
- free (name);
- return result;
+ 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].
return NULL;
}
+/* Returns "" if dwarf_version is greater than ARGV[ARGC-1].
+ Otherwise, return NULL. */
+
+static const char *
+dwarf_version_greater_than_spec_func (int argc, const char **argv)
+{
+ char *converted;
+
+ if (argc != 1)
+ fatal_error (input_location,
+ "wrong number of arguments to %%:dwarf-version-gt");
+
+ long arg = strtol (argv[0], &converted, 10);
+ gcc_assert (converted != argv[0]);
+
+ if (dwarf_version > arg)
+ return "";
+
+ return NULL;
+}
+
static void
path_prefix_reset (path_prefix *prefix)
{
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.
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
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;
just_machine_suffix = 0;
gcc_exec_prefix = 0;
gcc_libexec_prefix = 0;
- md_exec_prefix = MD_EXEC_PREFIX;
- md_startfile_prefix = MD_STARTFILE_PREFIX;
- md_startfile_prefix_1 = MD_STARTFILE_PREFIX_1;
+ set_static_spec_shared (&md_exec_prefix, MD_EXEC_PREFIX);
+ set_static_spec_shared (&md_startfile_prefix, MD_STARTFILE_PREFIX);
+ set_static_spec_shared (&md_startfile_prefix_1, MD_STARTFILE_PREFIX_1);
multilib_dir = 0;
multilib_os_dir = 0;
multiarch_dir = 0;
spec_list *sl = &static_specs[i];
if (sl->alloc_p)
{
- if (0)
- free (const_cast <char *> (*(sl->ptr_spec)));
+ free (const_cast <char *> (*(sl->ptr_spec)));
sl->alloc_p = false;
}
*(sl->ptr_spec) = sl->default_ptr;
mdswitches = NULL;
n_mdswitches = 0;
- debug_auxbase_opt = NULL;
-
used_arg.finalize ();
}