/* Compiler driver program that can handle many languages.
- Copyright (C) 1987-2016 Free Software Foundation, Inc.
+ Copyright (C) 1987-2020 Free Software Foundation, Inc.
This file is part of GCC.
#include "obstack.h"
#include "intl.h"
#include "prefix.h"
+#include "opt-suggestions.h"
#include "gcc.h"
#include "diagnostic.h"
#include "flags.h"
#include "opts.h"
-#include "params.h"
#include "filenames.h"
#include "spellcheck.h"
getenv ();
Hence we need to use "get" for the accessor method, not "getenv". */
-class env_manager
+struct env_manager
{
public:
void init (bool can_restore, bool debug);
\f
/* By default there is no special suffix for target executables. */
-/* FIXME: when autoconf is fixed, remove the host check - dj */
-#if defined(TARGET_EXECUTABLE_SUFFIX) && defined(HOST_EXECUTABLE_SUFFIX)
+#ifdef TARGET_EXECUTABLE_SUFFIX
#define HAVE_TARGET_EXECUTABLE_SUFFIX
+#else
+#define TARGET_EXECUTABLE_SUFFIX ""
#endif
/* By default there is no special suffix for host executables. */
static int print_version;
+/* Flag that stores string prefix for which we provide bash completion. */
+
+static const char *completion = NULL;
+
/* Flag indicating whether we should ONLY print the command and
arguments (like verbose_flag) without executing the command.
Displayed arguments are quoted so that the generated command
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 inline void process_marked_switches (void);
static const char *process_brace_body (const char *, const char *, const char *, int, int);
static const struct spec_function *lookup_spec_function (const char *);
-static const char *eval_spec_function (const char *, const char *);
-static const char *handle_spec_function (const char *, bool *);
+static const char *eval_spec_function (const char *, const char *, const char *);
+static const char *handle_spec_function (const char *, bool *, const char *);
static char *save_string (const char *, int);
static void set_collect_gcc_options (void);
static int do_spec_1 (const char *, int, const char *);
-static int do_spec_2 (const char *);
+static int do_spec_2 (const char *, const char *);
static void do_option_spec (const char *, const char *);
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 int default_arg (const char *, int);
static void set_multilib_dir (void);
static void print_multilib_info (void);
-static void perror_with_name (const char *);
static void display_help (void);
static void add_preprocessor_option (const char *, int);
static void add_assembler_option (const char *, 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
into the sequence of arguments that %o will substitute later.
%V indicates that this compilation produces no "output file".
%W{...}
- like %{...} but mark last argument supplied within
- as a file to be deleted on failure.
+ like %{...} but marks the last argument supplied within as a file
+ to be deleted on failure.
+ %@{...}
+ like %{...} but puts the result into a FILE and substitutes @FILE
+ if an @file argument has been supplied.
%o substitutes the names of all the output files, with spaces
automatically placed around them. You should write spaces
around the %o as well or the results are undefined.
%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.
%(Spec) processes a specification defined in a specs file as *Spec:
+The switch matching text S in a %{S}, %{S:X}, or similar construct can use
+a backslash to ignore the special meaning of the character following it,
+thus allowing literal matching of a character that is otherwise specially
+treated. For example, %{std=iso9899\:1999:X} substitutes X if the
+-std=iso9899:1999 option is given.
+
The conditional text X in a %{S:X} or similar construct may contain
other nested % constructs or spaces, or even newlines. They are
processed as usual, as described above. Trailing white space in X is
#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
#ifndef LIBASAN_SPEC
#define STATIC_LIBASAN_LIBS \
- " %{static-libasan:%:include(libsanitizer.spec)%(link_libasan)}"
+ " %{static-libasan|static:%:include(libsanitizer.spec)%(link_libasan)}"
#ifdef LIBASAN_EARLY_SPEC
#define LIBASAN_SPEC STATIC_LIBASAN_LIBS
#elif defined(HAVE_LD_STATIC_DYNAMIC)
#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:%:include(libsanitizer.spec)%(link_libtsan)}"
+ " %{static-libtsan|static:%:include(libsanitizer.spec)%(link_libtsan)}"
#ifdef LIBTSAN_EARLY_SPEC
#define LIBTSAN_SPEC STATIC_LIBTSAN_LIBS
#elif defined(HAVE_LD_STATIC_DYNAMIC)
#ifndef LIBLSAN_SPEC
#define STATIC_LIBLSAN_LIBS \
- " %{static-liblsan:%:include(libsanitizer.spec)%(link_liblsan)}"
+ " %{static-liblsan|static:%:include(libsanitizer.spec)%(link_liblsan)}"
#ifdef LIBLSAN_EARLY_SPEC
#define LIBLSAN_SPEC STATIC_LIBLSAN_LIBS
#elif defined(HAVE_LD_STATIC_DYNAMIC)
#ifndef LIBUBSAN_SPEC
#define STATIC_LIBUBSAN_LIBS \
- " %{static-libubsan:%:include(libsanitizer.spec)%(link_libubsan)}"
+ " %{static-libubsan|static:%:include(libsanitizer.spec)%(link_libubsan)}"
#ifdef HAVE_LD_STATIC_DYNAMIC
#define LIBUBSAN_SPEC "%{static-libubsan:" LD_STATIC_OPTION \
"} -lubsan %{static-libubsan:" LD_DYNAMIC_OPTION "}" \
#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 \
- ? "%{!g0:%{gdwarf*:--gdwarf2}%{!gdwarf*:%{g*:--gstabs}}}" ASM_MAP \
- : "%{!g0:%{gstabs*:--gstabs}%{!gstabs*:%{g*:--gdwarf2}}}" ASM_MAP)
+ ? "%{%:debug-level-gt(0):" \
+ "%{gdwarf*:" ASM_DEBUG_DWARF_OPTION "};" \
+ ":%{g*:--gstabs}}" ASM_MAP \
+ : "%{%:debug-level-gt(0):" \
+ "%{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*:%{!g0:--gstabs}}" ASM_MAP
+# 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*:%{!g0:--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
-lgcc and -lc order specially, yet not require them to override all
of LINK_COMMAND_SPEC. */
#ifndef LINK_GCC_C_SEQUENCE_SPEC
-#define LINK_GCC_C_SEQUENCE_SPEC "%G %L %G"
+#define LINK_GCC_C_SEQUENCE_SPEC "%G %{!nolibc:%L %G}"
#endif
#ifndef LINK_SSP_SPEC
#endif
#ifdef ENABLE_DEFAULT_PIE
-#define NO_PIE_SPEC "no-pie|static"
-#define PIE_SPEC NO_PIE_SPEC "|r|shared:;"
+#define PIE_SPEC "!no-pie"
#define NO_FPIE1_SPEC "fno-pie"
#define FPIE1_SPEC NO_FPIE1_SPEC ":;"
#define NO_FPIE2_SPEC "fno-PIE"
#define FPIE_OR_FPIC_SPEC NO_FPIE_AND_FPIC_SPEC ":;"
#else
#define PIE_SPEC "pie"
-#define NO_PIE_SPEC PIE_SPEC "|r|shared:;"
#define FPIE1_SPEC "fpie"
#define NO_FPIE1_SPEC FPIE1_SPEC ":;"
#define FPIE2_SPEC "fPIE"
#else
#define LD_PIE_SPEC ""
#endif
-#define LINK_PIE_SPEC "%{no-pie:} " "%{" PIE_SPEC ":" LD_PIE_SPEC "} "
+#define LINK_PIE_SPEC "%{static|shared|r:;" PIE_SPEC ":" LD_PIE_SPEC "} "
#endif
#ifndef LINK_BUILDID_SPEC
# endif
#endif
+#ifndef LTO_PLUGIN_SPEC
+#define LTO_PLUGIN_SPEC ""
+#endif
+
/* Conditional to test whether the LTO plugin is used or not.
FIXME: For slim LTO we will need to enable plugin unconditionally. This
still cause problems with PLUGIN_LD != LD and when plugin is built but
-plugin %(linker_plugin_file) \
-plugin-opt=%(lto_wrapper) \
-plugin-opt=-fresolution=%u.res \
+ " LTO_PLUGIN_SPEC "\
+ %{flinker-output=*:-plugin-opt=-linker-output-known} \
%{!nostdlib:%{!nodefaultlibs:%:pass-through-libs(%(link_gcc_c_sequence))}} \
}" PLUGIN_COND_CLOSE
#else
/* Linker command line options for -fsanitize= early on the command line. */
#ifndef SANITIZER_EARLY_SPEC
#define SANITIZER_EARLY_SPEC "\
-%{!nostdlib:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_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 "}}}"
+ %{%:sanitize(leak):" LIBLSAN_EARLY_SPEC "}}}}"
#endif
/* Linker command line options for -fsanitize= late on the command line. */
#ifndef SANITIZER_SPEC
#define SANITIZER_SPEC "\
-%{!nostdlib:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_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 "}\
- %{%:sanitize(leak):" LIBLSAN_SPEC "}}}"
+ %{%:sanitize(leak):" LIBLSAN_SPEC "}}}}"
#endif
#ifndef POST_LINK_SPEC
the vtable verification runtime functions are in libstdc++, so we use
the spec just below this one. */
#ifndef VTABLE_VERIFICATION_SPEC
+#if ENABLE_VTABLE_VERIFY
+#define VTABLE_VERIFICATION_SPEC "\
+%{!nostdlib:%{!r:%{fvtable-verify=std: -lvtv -u_vtable_map_vars_start -u_vtable_map_vars_end}\
+ %{fvtable-verify=preinit: -lvtv -u_vtable_map_vars_start -u_vtable_map_vars_end}}}"
+#else
#define VTABLE_VERIFICATION_SPEC "\
-%{!nostdlib:%{fvtable-verify=std: -lvtv -u_vtable_map_vars_start -u_vtable_map_vars_end}\
- %{fvtable-verify=preinit: -lvtv -u_vtable_map_vars_start -u_vtable_map_vars_end}}"
+%{fvtable-verify=none:} \
+%{fvtable-verify=std: \
+ %e-fvtable-verify=std is not supported in this configuration} \
+%{fvtable-verify=preinit: \
+ %e-fvtable-verify=preinit is not supported in this configuration}"
#endif
-
-#ifndef CHKP_SPEC
-#define CHKP_SPEC ""
#endif
/* -u* was put back because both BSD and SysV seem to support it. */
-/* %{static:} simply prevents an error message if the target machine
- doesn't handle -static. */
+/* %{static|no-pie|static-pie:} simply prevents an error message:
+ 1. If the target machine doesn't handle -static.
+ 2. If PIE isn't enabled by default.
+ 3. If the target machine doesn't handle -static-pie.
+ */
/* We want %{T*} after %{L*} and %D so that it can be used to specify linker
scripts which exist in user specified directories, or in standard
directories. */
%{flto} %{fno-lto} %{flto=*} %l " LINK_PIE_SPEC \
"%{fuse-ld=*:-fuse-ld=%*} " LINK_COMPRESS_DEBUG_SPEC \
"%X %{o*} %{e*} %{N} %{n} %{r}\
- %{s} %{t} %{u*} %{z} %{Z} %{!nostdlib:%{!nostartfiles:%S}} \
- %{static:} %{L*} %(mfwrap) %(link_libgcc) " \
- VTABLE_VERIFICATION_SPEC " " SANITIZER_EARLY_SPEC " %o " CHKP_SPEC " \
- %{fopenacc|fopenmp|%:gt(%{ftree-parallelize-loops=*} 1):\
+ %{s} %{t} %{u*} %{z} %{Z} %{!nostdlib:%{!r:%{!nostartfiles:%S}}} \
+ %{static|no-pie|static-pie:} %@{L*} %(mfwrap) %(link_libgcc) " \
+ VTABLE_VERIFICATION_SPEC " " SANITIZER_EARLY_SPEC " %o "" \
+ %{fopenacc|fopenmp|%:gt(%{ftree-parallelize-loops=*:%*} 1):\
%:include(libgomp.spec)%(link_gomp)}\
- %{fcilkplus:%:include(libcilkrts.spec)%(link_cilkrts)}\
%{fgnu-tm:%:include(libitm.spec)%(link_itm)}\
%(mflib) " STACK_SPLIT_SPEC "\
%{fprofile-arcs|fprofile-generate*|coverage:-lgcov} " SANITIZER_SPEC " \
- %{!nostdlib:%{!nodefaultlibs:%(link_ssp) %(link_gcc_c_sequence)}}\
- %{!nostdlib:%{!nostartfiles:%E}} %{T*} \n%(post_link) }}}}}}"
+ %{!nostdlib:%{!r:%{!nodefaultlibs:%(link_ssp) %(link_gcc_c_sequence)}}}\
+ %{!nostdlib:%{!r:%{!nostartfiles:%E}}} %{T*} \n%(post_link) }}}}}}"
#endif
#ifndef LINK_LIBGCC_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;
therefore no dependency entry, confuses make into thinking a .o
file that happens to exist is up-to-date. */
static const char *cpp_unique_options =
-"%{!Q:-quiet} %{nostdinc*} %{C} %{CC} %{v} %{I*&F*} %{P} %I\
+"%{!Q:-quiet} %{nostdinc*} %{C} %{CC} %{v} %@{I*&F*} %{P} %I\
%{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|gcoff3|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*}}";
in turn cause preprocessor symbols to be defined specially. */
static const char *cpp_options =
"%(cpp_unique_options) %1 %{m*} %{std*&ansi&trigraphs} %{W*&pedantic*} %{w}\
- %{f*} %{g*:%{!g0:%{g*} %{!fno-working-directory:-fworking-directory}}} %{O*}\
+ %{f*} %{g*:%{%:debug-level-gt(0):%{g*}\
+ %{!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}";
+ %{coverage:-fprofile-arcs -ftest-coverage}\
+ %{fprofile-arcs|fprofile-generate*|coverage:\
+ %{!fprofile-update=single:\
+ %{pthread:-fprofile-update=prefer-atomic}}}";
static const char *asm_options =
"%{-target-help:%:print-asm-header()} "
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}";
for targets that use different start files and suchlike. */
#ifndef GOMP_SELF_SPECS
#define GOMP_SELF_SPECS \
- "%{fopenacc|fopenmp|%:gt(%{ftree-parallelize-loops=*} 1): " \
+ "%{fopenacc|fopenmp|%:gt(%{ftree-parallelize-loops=*:%*} 1): " \
"-pthread}"
#endif
#define GTM_SELF_SPECS "%{fgnu-tm: -pthread}"
#endif
-/* Likewise for -fcilkplus. */
-#ifndef CILK_SELF_SPECS
-#define CILK_SELF_SPECS "%{fcilkplus: -pthread}"
-#endif
-
static const char *const driver_self_specs[] = {
"%{fdump-final-insns:-fdump-final-insns=.} %<fdump-final-insns",
- DRIVER_SELF_SPECS, CONFIGURE_SPECS, GOMP_SELF_SPECS, GTM_SELF_SPECS,
- CILK_SELF_SPECS
+ DRIVER_SELF_SPECS, CONFIGURE_SPECS, GOMP_SELF_SPECS, GTM_SELF_SPECS
};
#ifndef OPTION_DEFAULT_SPECS
const char *cpp_spec; /* If non-NULL, substitute this spec
for `%C', rather than the usual
cpp_spec. */
- const int combinable; /* If nonzero, compiler can deal with
+ int combinable; /* If nonzero, compiler can deal with
multiple source files at once (IMA). */
- const int needs_preprocessing; /* If nonzero, source files need to
+ int needs_preprocessing; /* If nonzero, source files need to
be run through a preprocessor. */
};
{".f03", "#Fortran", 0, 0, 0}, {".F03", "#Fortran", 0, 0, 0},
{".f08", "#Fortran", 0, 0, 0}, {".F08", "#Fortran", 0, 0, 0},
{".r", "#Ratfor", 0, 0, 0},
- {".p", "#Pascal", 0, 0, 0}, {".pas", "#Pascal", 0, 0, 0},
- {".java", "#Java", 0, 0, 0}, {".class", "#Java", 0, 0, 0},
- {".zip", "#Java", 0, 0, 0}, {".jar", "#Java", 0, 0, 0},
{".go", "#Go", 0, 1, 0},
+ {".d", "#D", 0, 1, 0}, {".dd", "#D", 0, 1, 0}, {".di", "#D", 0, 1, 0},
/* Next come the entries for C. */
{".c", "@c", 0, 0, 1},
{"@c",
%(cpp_options) -o %{save-temps*:%b.i} %{!save-temps*:%g.i} \n\
cc1 -fpreprocessed %{save-temps*:%b.i} %{!save-temps*:%g.i} \
%(cc1_options)\
- %{!fsyntax-only:-o %g.s \
+ %{!fsyntax-only:%{!S:-o %g.s} \
%{!fdump-ada-spec*:%{!o*:--output-pch=%i.gch}\
%W{o*:--output-pch=%*}}%V}}\
%{!save-temps*:%{!traditional-cpp:%{!no-integrated-cpp:\
cc1 %(cpp_unique_options) %(cc1_options)\
- %{!fsyntax-only:-o %g.s \
+ %{!fsyntax-only:%{!S:-o %g.s} \
%{!fdump-ada-spec*:%{!o*:--output-pch=%i.gch}\
%W{o*:--output-pch=%*}}%V}}}}}}}", 0, 0, 0},
{".i", "@cpp-output", 0, 0, 0},
{
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
#endif
{
char *buf;
- buf = concat ("%{static|static-libgcc:", static_name, " ", eh_name, "}"
- "%{!static:%{!static-libgcc:"
#if USE_LD_AS_NEEDED
+ buf = concat ("%{static|static-libgcc|static-pie:", static_name, " ", eh_name, "}"
+ "%{!static:%{!static-libgcc:%{!static-pie:"
"%{!shared-libgcc:",
static_name, " " LD_AS_NEEDED_OPTION " ",
shared_name, " " LD_NO_AS_NEEDED_OPTION
"}"
"%{shared-libgcc:",
shared_name, "%{!shared: ", static_name, "}"
- "}"
+ "}}"
#else
+ buf = concat ("%{static|static-libgcc:", static_name, " ", eh_name, "}"
+ "%{!static:%{!static-libgcc:"
"%{!shared:"
"%{!shared-libgcc:", static_name, " ", eh_name, "}"
"%{shared-libgcc:", shared_name, " ", static_name, "}"
"-lgcc_eh"
#ifdef USE_LIBUNWIND_EXCEPTIONS
# ifdef HAVE_LD_STATIC_DYNAMIC
- " %{!static:" LD_STATIC_OPTION "} -lunwind"
- " %{!static:" LD_DYNAMIC_OPTION "}"
+ " %{!static:%{!static-pie:" LD_STATIC_OPTION "}} -lunwind"
+ " %{!static:%{!static-pie:" LD_DYNAMIC_OPTION "}}"
# else
" -lunwind"
# endif
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
typedef const char *const_char_p; /* For DEF_VEC_P. */
/* Vector of pointers to arguments in the current line of specifications. */
-
static vec<const_char_p> argbuf;
+/* Likewise, but for the current @file. */
+static vec<const_char_p> at_file_argbuf;
+
+/* Whether an @file is currently open. */
+static bool in_at_file = false;
+
/* Were the options -c, -S or -E passed. */
static int have_c = 0;
/* Was the option -o passed. */
static int have_o = 0;
+/* Was the option -E passed. */
+static int have_E = 0;
+
/* Pointer to output file name passed in with -o. */
static const char *output_file = 0;
alloc_args (void)
{
argbuf.create (10);
+ at_file_argbuf.create (10);
}
/* Clear out the vector of arguments (after a command is executed). */
clear_args (void)
{
argbuf.truncate (0);
+ at_file_argbuf.truncate (0);
}
/* Add one argument to the vector at the end.
static void
store_arg (const char *arg, int delete_always, int delete_failure)
{
- argbuf.safe_push (arg);
+ if (in_at_file)
+ at_file_argbuf.safe_push (arg);
+ else
+ argbuf.safe_push (arg);
if (delete_always || delete_failure)
{
record_temp_file (arg, delete_always, delete_failure);
}
}
+
+/* Open a temporary @file into which subsequent arguments will be stored. */
+
+static void
+open_at_file (void)
+{
+ if (in_at_file)
+ fatal_error (input_location, "cannot open nested response file");
+ else
+ in_at_file = true;
+}
+
+/* Close the temporary @file and add @file to the argument list. */
+
+static void
+close_at_file (void)
+{
+ if (!in_at_file)
+ fatal_error (input_location, "cannot close nonexistent response file");
+
+ in_at_file = false;
+
+ const unsigned int n_args = at_file_argbuf.length ();
+ if (n_args == 0)
+ return;
+
+ char **argv = (char **) alloca (sizeof (char *) * (n_args + 1));
+ char *temp_file = make_temp_file ("");
+ char *at_argument = concat ("@", temp_file, NULL);
+ FILE *f = fopen (temp_file, "w");
+ int status;
+ unsigned int i;
+
+ /* Copy the strings over. */
+ for (i = 0; i < n_args; i++)
+ argv[i] = CONST_CAST (char *, at_file_argbuf[i]);
+ argv[i] = NULL;
+
+ at_file_argbuf.truncate (0);
+
+ if (f == NULL)
+ fatal_error (input_location, "could not open temporary response file %s",
+ temp_file);
+
+ status = writeargv (argv, f);
+
+ if (status)
+ fatal_error (input_location,
+ "could not write to temporary response file %s",
+ temp_file);
+
+ status = fclose (f);
+
+ if (status == EOF)
+ fatal_error (input_location, "could not close temporary response file %s",
+ temp_file);
+
+ store_arg (at_argument, 0, 0);
+
+ record_temp_file (temp_file, !save_temps_flag, !save_temps_flag);
+}
\f
/* Load specs from a file name named FILENAME, replacing occurrences of
various different types of line-endings, \r\n, \n\r and just \r, with
/* Open and stat the file. */
desc = open (filename, O_RDONLY, 0);
if (desc < 0)
- pfatal_with_name (filename);
+ {
+ failed:
+ /* This leaves DESC open, but the OS will save us. */
+ fatal_error (input_location, "cannot read spec file %qs: %m", filename);
+ }
+
if (stat (filename, &statbuf) < 0)
- pfatal_with_name (filename);
+ goto failed;
/* Read contents of file into BUFFER. */
buffer = XNEWVEC (char, statbuf.st_size + 1);
readlen = read (desc, buffer, (unsigned) statbuf.st_size);
if (readlen < 0)
- pfatal_with_name (filename);
+ goto failed;
buffer[readlen] = 0;
close (desc);
if (stat (NAME, &ST) >= 0 && S_ISREG (ST.st_mode)) \
if (unlink (NAME) < 0) \
if (VERBOSE_FLAG) \
- perror_with_name (NAME); \
+ error ("%s: %m", (NAME)); \
} while (0)
#endif
add_prefix (pprefix, prefix, component, priority,
require_machine_suffix, os_multilib);
}
+
+/* Same as add_prefix, but prepending target_sysroot_hdrs_suffix to prefix. */
+
+static void
+add_sysrooted_hdrs_prefix (struct path_prefix *pprefix, const char *prefix,
+ const char *component,
+ /* enum prefix_priority */ int priority,
+ int require_machine_suffix, int os_multilib)
+{
+ if (!IS_ABSOLUTE_PATH (prefix))
+ fatal_error (input_location, "system path %qs is not absolute", prefix);
+
+ if (target_system_root)
+ {
+ char *sysroot_no_trailing_dir_separator = xstrdup (target_system_root);
+ size_t sysroot_len = strlen (target_system_root);
+
+ if (sysroot_len > 0
+ && target_system_root[sysroot_len - 1] == DIR_SEPARATOR)
+ sysroot_no_trailing_dir_separator[sysroot_len - 1] = '\0';
+
+ if (target_sysroot_hdrs_suffix)
+ prefix = concat (sysroot_no_trailing_dir_separator,
+ target_sysroot_hdrs_suffix, prefix, NULL);
+ else
+ prefix = concat (sysroot_no_trailing_dir_separator, prefix, NULL);
+
+ free (sysroot_no_trailing_dir_separator);
+
+ /* We have to override this because GCC's notion of sysroot
+ moves along with GCC. */
+ component = "GCC";
+ }
+
+ add_prefix (pprefix, prefix, component, priority,
+ require_machine_suffix, os_multilib);
+}
+
\f
/* Execute the command specified by the arguments on the current line of spec.
When using pipes, this includes several piped-together commands
if (!wrapper_string)
{
string = find_a_file (&exec_prefixes, commands[0].prog, X_OK, false);
- commands[0].argv[0] = (string) ? string : commands[0].argv[0];
+ if (string)
+ commands[0].argv[0] = string;
}
for (n_commands = 1, i = 0; argbuf.iterate (i, &arg); i++)
if (arg && strcmp (arg, "|") == 0)
{ /* each command. */
#if defined (__MSDOS__) || defined (OS2) || defined (VMS)
- fatal_error (input_location, "-pipe not supported");
+ fatal_error (input_location, "%<-pipe%> not supported");
#endif
- argbuf[i] = 0; /* Termination of
- command args. */
+ argbuf[i] = 0; /* Termination of command args. */
commands[n_commands].prog = argbuf[i + 1];
commands[n_commands].argv
= &(argbuf.address ())[i + 1];
? PEX_RECORD_TIMES : 0),
progname, temp_filename);
if (pex == NULL)
- fatal_error (input_location, "pex_init failed: %m");
+ fatal_error (input_location, "%<pex_init%> failed: %m");
for (i = 0; i < n_commands; i++)
{
NULL, NULL, &err);
if (errmsg != NULL)
{
- if (err == 0)
- fatal_error (input_location, errmsg);
- else
- {
- errno = err;
- pfatal_with_name (errmsg);
- }
+ errno = err;
+ fatal_error (input_location,
+ err ? G_("cannot execute %qs: %s: %m")
+ : G_("cannot execute %qs: %s"),
+ string, errmsg);
}
if (i && string != commands[i].prog)
int status = statuses[i];
if (WIFSIGNALED (status))
- {
+ switch (WTERMSIG (status))
+ {
+ case SIGINT:
+ case SIGTERM:
+ /* SIGQUIT and SIGKILL are not available on MinGW. */
+#ifdef SIGQUIT
+ case SIGQUIT:
+#endif
+#ifdef SIGKILL
+ case SIGKILL:
+#endif
+ /* The user (or environment) did something to the
+ inferior. Making this an ICE confuses the user into
+ thinking there's a compiler bug. Much more likely is
+ the user or OOM killer nuked it. */
+ fatal_error (input_location,
+ "%s signal terminated program %s",
+ strsignal (WTERMSIG (status)),
+ commands[i].prog);
+ break;
+
#ifdef SIGPIPE
- /* SIGPIPE is a special case. It happens in -pipe mode
- when the compiler dies before the preprocessor is done,
- or the assembler dies before the compiler is done.
- There's generally been an error already, and this is
- just fallout. So don't generate another error unless
- we would otherwise have succeeded. */
- if (WTERMSIG (status) == SIGPIPE
- && (signal_count || greatest_status >= MIN_FATAL_STATUS))
- {
- signal_count++;
- ret_code = -1;
- }
- else
+ case SIGPIPE:
+ /* SIGPIPE is a special case. It happens in -pipe mode
+ when the compiler dies before the preprocessor is
+ done, or the assembler dies before the compiler is
+ done. There's generally been an error already, and
+ this is just fallout. So don't generate another
+ error unless we would otherwise have succeeded. */
+ if (signal_count || greatest_status >= MIN_FATAL_STATUS)
+ {
+ signal_count++;
+ ret_code = -1;
+ break;
+ }
#endif
- internal_error_no_backtrace ("%s (program %s)",
+ /* FALLTHROUGH */
+
+ default:
+ /* The inferior failed to catch the signal. */
+ internal_error_no_backtrace ("%s signal terminated program %s",
strsignal (WTERMSIG (status)),
commands[i].prog);
- }
+ }
else if (WIFEXITED (status)
&& WEXITSTATUS (status) >= MIN_FATAL_STATUS)
{
#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--)
fputs (_(" -S Compile only; do not assemble or link.\n"), stdout);
fputs (_(" -c Compile and assemble, but do not link.\n"), stdout);
fputs (_(" -o <file> Place the output into <file>.\n"), stdout);
- fputs (_(" -pie Create a position independent executable.\n"), stdout);
+ fputs (_(" -pie Create a dynamically linked position independent\n\
+ executable.\n"), stdout);
fputs (_(" -shared Create a shared library.\n"), stdout);
fputs (_("\
-x <language> Specify the language of the following input files.\n\
n_switches++;
}
+/* Set the SOURCE_DATE_EPOCH environment variable to the current time if it is
+ not set already. */
+
+static void
+set_source_date_epoch_envvar ()
+{
+ /* Array size is 21 = ceil(log_10(2^64)) + 1 to hold string representations
+ of 64 bit integers. */
+ char source_date_epoch[21];
+ time_t tt;
+
+ errno = 0;
+ tt = time (NULL);
+ if (tt < (time_t) 0 || errno != 0)
+ tt = (time_t) 0;
+
+ snprintf (source_date_epoch, 21, "%llu", (unsigned long long) tt);
+ /* Using setenv instead of xputenv because we want the variable to remain
+ after finalizing so that it's still set in the second run when using
+ -fcompare-debug. */
+ setenv ("SOURCE_DATE_EPOCH", source_date_epoch, 0);
+}
+
/* Handle an option DECODED that is unknown to the option-processing
machinery. */
const struct cl_option *option = &cl_options[decoded->opt_index];
if (option->cl_reject_driver)
- error ("unrecognized command line option %qs",
+ error ("unrecognized command-line option %qs",
decoded->orig_option_with_args_text);
else
save_switch (decoded->canonical_option[0],
unsigned int lang_mask ATTRIBUTE_UNUSED, int kind,
location_t loc,
const struct cl_option_handlers *handlers ATTRIBUTE_UNUSED,
- diagnostic_context *dc)
+ diagnostic_context *dc,
+ void (*) (void))
{
size_t opt_index = decoded->opt_index;
const char *arg = decoded->arg;
printf ("%s\n", spec_machine);
exit (0);
+ case OPT_dumpfullversion:
+ printf ("%s\n", BASEVER);
+ exit (0);
+
case OPT__version:
print_version = 1;
add_linker_option ("--version", strlen ("--version"));
break;
+ case OPT__completion_:
+ validated = true;
+ completion = decoded->arg;
+ break;
+
case OPT__help:
print_help_list = 1;
else
compare_debug_opt = arg;
save_switch (compare_debug_replacement_opt, 0, NULL, validated, true);
+ set_source_date_epoch_envvar ();
return true;
case OPT_fdiagnostics_color_:
diagnostic_color_init (dc, value);
break;
+ case OPT_fdiagnostics_urls_:
+ diagnostic_urls_init (dc, value);
+ break;
+
+ case OPT_fdiagnostics_format_:
+ diagnostic_output_format_init (dc,
+ (enum diagnostics_output_format)value);
+ break;
+
case OPT_Wa_:
{
int prev, j;
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;
|| strcmp (arg, "object") == 0)
save_temps_flag = SAVE_TEMPS_OBJ;
else
- fatal_error (input_location, "%qs is an unknown -save-temps option",
+ 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:
validated = true;
break;
+ case OPT_E:
+ have_E = true;
+ break;
+
case OPT_x:
spec_lang = arg;
if (!strcmp (spec_lang, "none"))
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'. */
fname = xstrdup (arg);
if (strcmp (fname, "-") != 0 && access (fname, F_OK) < 0)
- perror_with_name (fname);
+ {
+ bool resp = fname[0] == '@' && access (fname + 1, F_OK) < 0;
+ error ("%s: %m", fname + resp);
+ }
else
add_infile (arg, spec_lang);
output_file);
}
+ 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)
{
- save_temps_length = strlen (save_temps_prefix);
- temp = strrchr (lbasename (save_temps_prefix), '.');
- if (temp)
- {
- save_temps_length -= strlen (temp);
- save_temps_prefix[save_temps_length] = '\0';
- }
-
+ free (dumpdir);
+ dumpdir = NULL;
+ temp = lbasename (output_file);
+ if (temp != output_file)
+ dumpdir = xstrndup (output_file,
+ strlen (output_file) - strlen (temp));
}
- else if (save_temps_prefix != NULL)
+ else if (dumpdir)
{
- free (save_temps_prefix);
- save_temps_prefix = NULL;
+ free (dumpdir);
+ dumpdir = NULL;
}
- if (save_temps_flag && use_pipes)
+ 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 overrides -pipe, so that temp files are produced */
- if (save_temps_flag)
- warning (0, "-pipe ignored because -save-temps specified");
- use_pipes = 0;
+ free (dumpdir);
+ dumpdir = NULL;
}
- if (!compare_debug)
+ /* Check that dumpbase_ext matches the end of dumpbase, drop it
+ otherwise. */
+ if (dumpbase_ext && dumpbase && *dumpbase)
{
- const char *gcd = env.get ("GCC_COMPARE_DEBUG");
+ int lendb = strlen (dumpbase);
+ int lendbx = strlen (dumpbase_ext);
- if (gcd && gcd[0] == '-')
- {
- compare_debug = 2;
- compare_debug_opt = gcd;
- }
- else if (gcd && *gcd && strcmp (gcd, "0"))
+ /* -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)
{
- compare_debug = 3;
- compare_debug_opt = "-gtoggle";
+ free (dumpbase_ext);
+ dumpbase_ext = NULL;
}
}
- else if (compare_debug < 0)
+
+ /* -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)))
{
- compare_debug = 0;
+ 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);
+ }
+
+ /* 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)
+ {
+ /* -save-temps overrides -pipe, so that temp files are produced */
+ if (save_temps_flag)
+ warning (0, "%<-pipe%> ignored because %<-save-temps%> specified");
+ use_pipes = 0;
+ }
+
+ if (!compare_debug)
+ {
+ const char *gcd = env.get ("GCC_COMPARE_DEBUG");
+
+ if (gcd && gcd[0] == '-')
+ {
+ compare_debug = 2;
+ compare_debug_opt = gcd;
+ }
+ else if (gcd && *gcd && strcmp (gcd, "0"))
+ {
+ compare_debug = 3;
+ compare_debug_opt = "-gtoggle";
+ }
+ }
+ else if (compare_debug < 0)
+ {
+ compare_debug = 0;
gcc_assert (!compare_debug_opt);
}
/* More prefixes are enabled in main, after we read the specs file
and determine whether this is cross-compilation or not. */
- if (n_infiles == last_language_n_infiles && spec_lang != 0)
+ if (n_infiles != 0 && n_infiles == last_language_n_infiles && spec_lang != 0)
warning (0, "%<-x %s%> after last input file has no effect", spec_lang);
/* Synthesize -fcompare-debug flag from the GCC_COMPARE_DEBUG
}
/* Ensure we only invoke each subprocess once. */
- if (print_subprocess_help || print_help_list || print_version)
+ if (n_infiles == 0
+ && (print_subprocess_help || print_help_list || print_version))
{
- n_infiles = 0;
-
/* Create a dummy input file, so that we can pass
the help option on to the various sub-processes. */
add_infile ("help-dummy", "c");
/* Decide if undefined variable references are allowed in specs. */
- /* --version and --help alone or together are safe. Note that -v would
- make them unsafe, as they'd then be run for subprocesses as well, the
- location of which might depend on variables possibly coming from
- self-specs.
-
- Count the number of options we have for which undefined variables
- are harmless for sure, and check that nothing else is set. */
+ /* -v alone is safe. --version and --help alone or together are safe. Note
+ that -v would make them unsafe, as they'd then be run for subprocesses as
+ well, the location of which might depend on variables possibly coming
+ from self-specs. Note also that the command name is counted in
+ decoded_options_count. */
- unsigned n_varsafe_options = 0;
+ unsigned help_version_count = 0;
if (print_version)
- n_varsafe_options++;
-
+ help_version_count++;
+
if (print_help_list)
- n_varsafe_options++;
-
- spec_undefvar_allowed = (n_varsafe_options == decoded_options_count - 1);
+ help_version_count++;
+
+ spec_undefvar_allowed =
+ ((verbose_flag && decoded_options_count == 2)
+ || help_version_count == decoded_options_count - 1);
alloc_switch ();
switches[n_switches].part1 = 0;
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));
{
int value;
- value = do_spec_2 (spec);
+ value = do_spec_2 (spec, NULL);
/* Force out any unfinished command.
If -pipe, this forces out the last command if it ended in `|'. */
return value;
}
+/* Process the spec SPEC, with SOFT_MATCHED_PART designating the current value
+ of a matched * pattern which may be re-injected by way of %*. */
+
static int
-do_spec_2 (const char *spec)
+do_spec_2 (const char *spec, const char *soft_matched_part)
{
int result;
input_from_pipe = 0;
suffix_subst = NULL;
- result = do_spec_1 (spec, 0, NULL);
+ result = do_spec_1 (spec, 0, soft_matched_part);
end_going_arg ();
return result;
}
-
/* Process the given spec string and add any new options to the end
of the switches/n_switches array. */
{
int i;
- do_spec_2 (spec);
+ do_spec_2 (spec, NULL);
do_spec_1 (" ", 0, NULL);
/* Mark %<S switches processed by do_self_spec to be ignored permanently.
return NULL;
}
-/* Create a temporary FILE with the contents of ARGV. Add @FILE to the
- argument list. */
-
-static void
-create_at_file (char **argv)
-{
- char *temp_file = make_temp_file ("");
- char *at_argument = concat ("@", temp_file, NULL);
- FILE *f = fopen (temp_file, "w");
- int status;
-
- if (f == NULL)
- fatal_error (input_location, "could not open temporary response file %s",
- temp_file);
-
- status = writeargv (argv, f);
-
- if (status)
- fatal_error (input_location,
- "could not write to temporary response file %s",
- temp_file);
-
- status = fclose (f);
-
- if (EOF == status)
- fatal_error (input_location, "could not close temporary response file %s",
- temp_file);
-
- store_arg (at_argument, 0, 0);
-
- record_temp_file (temp_file, !save_temps_flag, !save_temps_flag);
-}
-
/* True if we should compile INFILE. */
static bool
}
}
+/* Add options passed via -Xassembler or -Wa to COLLECT_AS_OPTIONS. */
+
+static void
+putenv_COLLECT_AS_OPTIONS (vec<char_p> vec)
+{
+ if (vec.is_empty ())
+ return;
+
+ obstack_init (&collect_obstack);
+ obstack_grow (&collect_obstack, "COLLECT_AS_OPTIONS=",
+ strlen ("COLLECT_AS_OPTIONS="));
+
+ char *opt;
+ unsigned ix;
+
+ FOR_EACH_VEC_ELT (vec, ix, opt)
+ {
+ obstack_1grow (&collect_obstack, '\'');
+ obstack_grow (&collect_obstack, opt, strlen (opt));
+ obstack_1grow (&collect_obstack, '\'');
+ if (ix < vec.length () - 1)
+ obstack_1grow(&collect_obstack, ' ');
+ }
+
+ obstack_1grow (&collect_obstack, '\0');
+ xputenv (XOBFINISH (&collect_obstack, char *));
+}
+
/* Process the sub-spec SPEC as a portion of a larger spec.
This is like processing a whole spec except that we do
not initialize at the beginning and we do not supply a
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;
buf = (char *) alloca (p - q + 1);
strncpy (buf, q, p - q);
buf[p - q] = 0;
- inform (0, "%s", _(buf));
+ inform (UNKNOWN_LOCATION, "%s", _(buf));
if (*p)
p++;
}
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)
if (files_differ)
#endif
{
- temp_filename = save_string (temp_filename,
- temp_filename_length + 1);
+ temp_filename
+ = save_string (temp_filename,
+ temp_filename_length - 1);
obstack_grow (&obstack, temp_filename,
temp_filename_length);
arg_going = 1;
case 'i':
if (combine_inputs)
{
+ /* We are going to expand `%i' into `@FILE', where FILE
+ is a newly-created temporary filename. The filenames
+ that would usually be expanded in place of %o will be
+ written to the temporary file. */
if (at_file_supplied)
- {
- /* We are going to expand `%i' to `@FILE', where FILE
- is a newly-created temporary filename. The filenames
- that would usually be expanded in place of %o will be
- written to the temporary file. */
- char **argv;
- int n_files = 0;
- int j;
-
- for (i = 0; i < n_infiles; i++)
- if (compile_input_file_p (&infiles[i]))
- n_files++;
-
- argv = (char **) alloca (sizeof (char *) * (n_files + 1));
-
- /* Copy the strings over. */
- for (i = 0, j = 0; i < n_infiles; i++)
- if (compile_input_file_p (&infiles[i]))
- {
- argv[j] = CONST_CAST (char *, infiles[i].name);
- infiles[i].compiled = true;
- j++;
- }
- argv[j] = NULL;
+ open_at_file ();
- create_at_file (argv);
- }
- else
- for (i = 0; (int) i < n_infiles; i++)
- if (compile_input_file_p (&infiles[i]))
- {
- store_arg (infiles[i].name, 0, 0);
- infiles[i].compiled = true;
- }
+ for (i = 0; (int) i < n_infiles; i++)
+ if (compile_input_file_p (&infiles[i]))
+ {
+ store_arg (infiles[i].name, 0, 0);
+ infiles[i].compiled = true;
+ }
+
+ if (at_file_supplied)
+ close_at_file ();
}
else
{
break;
case 'o':
- {
- int max = n_infiles;
- max += lang_specific_extra_outfiles;
-
- if (HAVE_GNU_LD && at_file_supplied)
- {
- /* We are going to expand `%o' to `@FILE', where FILE
- is a newly-created temporary filename. The filenames
- that would usually be expanded in place of %o will be
- written to the temporary file. */
-
- char **argv;
- int n_files, j;
-
- /* Convert OUTFILES into a form suitable for writeargv. */
-
- /* Determine how many are non-NULL. */
- for (n_files = 0, i = 0; i < max; i++)
- n_files += outfiles[i] != NULL;
-
- argv = (char **) alloca (sizeof (char *) * (n_files + 1));
-
- /* Copy the strings over. */
- for (i = 0, j = 0; i < max; i++)
- if (outfiles[i])
- {
- argv[j] = CONST_CAST (char *, outfiles[i]);
- j++;
- }
- argv[j] = NULL;
-
- create_at_file (argv);
- }
- else
- for (i = 0; i < max; i++)
- if (outfiles[i])
- store_arg (outfiles[i], 0, 0);
- break;
- }
+ /* We are going to expand `%o' into `@FILE', where FILE
+ is a newly-created temporary filename. The filenames
+ that would usually be expanded in place of %o will be
+ written to the temporary file. */
+ if (at_file_supplied)
+ open_at_file ();
+
+ for (i = 0; i < n_infiles + lang_specific_extra_outfiles; i++)
+ if (outfiles[i])
+ store_arg (outfiles[i], 0, 0);
+
+ if (at_file_supplied)
+ close_at_file ();
+ break;
case 'O':
obstack_grow (&obstack, TARGET_OBJECT_SUFFIX, strlen (TARGET_OBJECT_SUFFIX));
break;
}
+ case '@':
+ /* Handle the {...} following the %@. */
+ if (*p != '{')
+ fatal_error (input_location,
+ "spec %qs has invalid %<%%@%c%>", spec, *p);
+ if (at_file_supplied)
+ open_at_file ();
+ p = handle_braces (p + 1);
+ if (at_file_supplied)
+ close_at_file ();
+ if (p == 0)
+ return -1;
+ break;
+
/* %x{OPTION} records OPTION for %X to output. */
case 'x':
{
break;
case ':':
- p = handle_spec_function (p, NULL);
+ p = handle_spec_function (p, NULL, soft_matched_part);
if (p == 0)
return -1;
break;
}
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. */
/* Evaluate a spec function. */
static const char *
-eval_spec_function (const char *func, const char *args)
+eval_spec_function (const char *func, const char *args,
+ const char *soft_matched_part)
{
const struct spec_function *sf;
const char *funcval;
arguments. */
alloc_args ();
- if (do_spec_2 (args) < 0)
- fatal_error (input_location, "error in args to spec function %qs", func);
+ if (do_spec_2 (args, soft_matched_part) < 0)
+ fatal_error (input_location, "error in arguments to spec function %qs",
+ func);
/* argbuf_index is an index for the next argument to be inserted, and
so contains the count of the args already inserted. */
NULL if no processing is required.
If RETVAL_NONNULL is not NULL, then store a bool whether function
- returned non-NULL. */
+ returned non-NULL.
+
+ SOFT_MATCHED_PART holds the current value of a matched * pattern, which
+ may be re-expanded with a %* as part of the function arguments. */
static const char *
-handle_spec_function (const char *p, bool *retval_nonnull)
+handle_spec_function (const char *p, bool *retval_nonnull,
+ const char *soft_matched_part)
{
char *func, *args;
const char *endp, *funcval;
/* p now points to just past the end of the spec function expression. */
- funcval = eval_spec_function (func, args);
+ funcval = eval_spec_function (func, args, soft_matched_part);
if (funcval != NULL && do_spec_1 (funcval, 0, NULL) < 0)
p = NULL;
if (retval_nonnull)
{
const char *atom, *end_atom;
const char *d_atom = NULL, *d_end_atom = NULL;
+ char *esc_buf = NULL, *d_esc_buf = NULL;
+ int esc;
const char *orig = p;
bool a_is_suffix;
{
atom = NULL;
end_atom = NULL;
- p = handle_spec_function (p + 2, &a_matched);
+ p = handle_spec_function (p + 2, &a_matched, NULL);
}
else
{
p++, a_is_spectype = true;
atom = p;
+ esc = 0;
while (ISIDNUM (*p) || *p == '-' || *p == '+' || *p == '='
- || *p == ',' || *p == '.' || *p == '@')
- p++;
+ || *p == ',' || *p == '.' || *p == '@' || *p == '\\')
+ {
+ if (*p == '\\')
+ {
+ p++;
+ if (!*p)
+ fatal_error (input_location,
+ "braced spec %qs ends in escape", orig);
+ esc++;
+ }
+ p++;
+ }
end_atom = p;
+ if (esc)
+ {
+ const char *ap;
+ char *ep;
+
+ if (esc_buf && esc_buf != d_esc_buf)
+ free (esc_buf);
+ esc_buf = NULL;
+ ep = esc_buf = (char *) xmalloc (end_atom - atom - esc + 1);
+ for (ap = atom; ap != end_atom; ap++, ep++)
+ {
+ if (*ap == '\\')
+ ap++;
+ *ep = *ap;
+ }
+ *ep = '\0';
+ atom = esc_buf;
+ end_atom = ep;
+ }
+
if (*p == '*')
p++, a_is_starred = 1;
}
disj_matched = true;
d_atom = atom;
d_end_atom = end_atom;
+ d_esc_buf = esc_buf;
}
}
}
p = process_brace_body (p + 1, d_atom, d_end_atom, disj_starred,
disj_matched && !n_way_matched);
if (p == 0)
- return 0;
+ goto done;
/* If we have an N-way choice, reset state for the next
disjunction. */
}
while (*p++ != '}');
+ done:
+ if (d_esc_buf && d_esc_buf != esc_buf)
+ free (d_esc_buf);
+ if (esc_buf)
+ free (esc_buf);
+
return p;
invalid:
#endif
fnotice (file, "Thread model: %s\n", thrmod);
+ fnotice (file, "Supported LTO compression algorithms: zlib");
+#ifdef HAVE_ZSTD_H
+ fnotice (file, " zstd");
+#endif
+ fnotice (file, "\n");
/* compiler_version is truncated at the first space when initialized
from version string, so truncate version_string at the first space
pex = pex_init (PEX_USE_PIPES, new_argv[0], NULL);
if (!pex)
- fatal_error (input_location, "pex_init failed: %m");
+ fatal_error (input_location, "%<pex_init%> failed: %m");
errmsg = pex_run (pex, pex_flags, new_argv[0],
- CONST_CAST2 (char *const *, const char **, &new_argv[1]), out_temp,
- err_temp, &err);
+ CONST_CAST2 (char *const *, const char **, &new_argv[1]),
+ out_temp, err_temp, &err);
if (errmsg != NULL)
{
- if (err == 0)
- fatal_error (input_location, errmsg);
- else
- {
- errno = err;
- pfatal_with_name (errmsg);
- }
+ errno = err;
+ fatal_error (input_location,
+ err ? G_ ("cannot execute %qs: %s: %m")
+ : G_ ("cannot execute %qs: %s"),
+ new_argv[0], errmsg);
}
if (!pex_get_status (pex, 1, &exit_status))
/* In final attempt we append compiler options and preprocesssed code to last
generated .out file with configuration and backtrace. */
- char **output = &temp_stdout_files[RETRY_ICE_ATTEMPTS - 1];
- do_report_bug (new_argv, nargs, stderr_commented, output);
+ char **err = &temp_stderr_files[RETRY_ICE_ATTEMPTS - 1];
+ do_report_bug (new_argv, nargs, stderr_commented, err);
}
out:
if (!ret && length[0] != length[1])
{
- error ("%s: -fcompare-debug failure (length)", gcc_input_filename);
+ error ("%s: %<-fcompare-debug%> failure (length)", gcc_input_filename);
ret = 1;
}
{
if (memcmp (map[0], map[1], length[0]) != 0)
{
- error ("%s: -fcompare-debug failure", gcc_input_filename);
+ error ("%s: %<-fcompare-debug%> failure", gcc_input_filename);
ret = 1;
}
}
if (c0 != c1)
{
- error ("%s: -fcompare-debug failure",
+ error ("%s: %<-fcompare-debug%> failure",
gcc_input_filename);
ret = 1;
break;
driver::driver (bool can_finalize, bool debug) :
explicit_link_files (NULL),
- decoded_options (NULL),
- m_option_suggestions (NULL)
+ decoded_options (NULL)
{
env.init (can_finalize, debug);
}
{
XDELETEVEC (explicit_link_files);
XDELETEVEC (decoded_options);
- if (m_option_suggestions)
- {
- int i;
- char *str;
- FOR_EACH_VEC_ELT (*m_option_suggestions, i, str)
- free (str);
- delete m_option_suggestions;
- }
}
/* driver::main is implemented as a series of driver:: method calls. */
global_initializations ();
build_multilib_strings ();
set_up_specs ();
+ putenv_COLLECT_AS_OPTIONS (assembler_options);
putenv_COLLECT_GCC (argv[0]);
maybe_putenv_COLLECT_LTO_WRAPPER ();
maybe_putenv_OFFLOAD_TARGETS ();
handle_unrecognized_options ();
+ if (completion)
+ {
+ m_option_proposer.suggest_completion (completion);
+ return 0;
+ }
+
if (!maybe_print_and_exit ())
return 0;
void
driver::decode_argv (int argc, const char **argv)
{
- /* Register the language-independent parameters. */
- global_init_params ();
- finish_params ();
-
init_opts_obstack ();
init_options_struct (&global_options, &global_options_set);
diagnostic_initialize (global_dc, 0);
diagnostic_color_init (global_dc);
+ diagnostic_urls_init (global_dc);
#ifdef GCC_DRIVER_HOST_INITIALIZATION
/* Perform host dependent initialization when needed. */
/* Process sysroot_suffix_spec. */
if (*sysroot_suffix_spec != 0
&& !no_sysroot_suffix
- && do_spec_2 (sysroot_suffix_spec) == 0)
+ && do_spec_2 (sysroot_suffix_spec, NULL) == 0)
{
if (argbuf.length () > 1)
- error ("spec failure: more than one arg to SYSROOT_SUFFIX_SPEC");
+ error ("spec failure: more than one argument to "
+ "%<SYSROOT_SUFFIX_SPEC%>");
else if (argbuf.length () == 1)
target_sysroot_suffix = xstrdup (argbuf.last ());
}
/* Process sysroot_hdrs_suffix_spec. */
if (*sysroot_hdrs_suffix_spec != 0
&& !no_sysroot_suffix
- && do_spec_2 (sysroot_hdrs_suffix_spec) == 0)
+ && do_spec_2 (sysroot_hdrs_suffix_spec, NULL) == 0)
{
if (argbuf.length () > 1)
- error ("spec failure: more than one arg to SYSROOT_HEADERS_SUFFIX_SPEC");
+ error ("spec failure: more than one argument "
+ "to %<SYSROOT_HEADERS_SUFFIX_SPEC%>");
else if (argbuf.length () == 1)
target_sysroot_hdrs_suffix = xstrdup (argbuf.last ());
}
/* Look for startfiles in the standard places. */
if (*startfile_prefix_spec != 0
- && do_spec_2 (startfile_prefix_spec) == 0
+ && do_spec_2 (startfile_prefix_spec, NULL) == 0
&& do_spec_1 (" ", 0, NULL) == 0)
{
const char *arg;
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);
offload_targets = NULL;
}
-/* Helper function for driver::suggest_option. Populate
- m_option_suggestions with candidate strings for misspelled options.
- The strings will be freed by the driver's dtor. */
-
-void
-driver::build_option_suggestions (void)
-{
- gcc_assert (m_option_suggestions == NULL);
- m_option_suggestions = new auto_vec <char *> ();
-
- /* We build a vec of m_option_suggestions, using add_misspelling_candidates
- to add copies of strings, without a leading dash. */
-
- for (unsigned int i = 0; i < cl_options_count; i++)
- {
- const struct cl_option *option = &cl_options[i];
- const char *opt_text = option->opt_text;
- switch (i)
- {
- default:
- if (option->var_type == CLVC_ENUM)
- {
- const struct cl_enum *e = &cl_enums[option->var_enum];
- for (unsigned j = 0; e->values[j].arg != NULL; j++)
- {
- char *with_arg = concat (opt_text, e->values[j].arg, NULL);
- add_misspelling_candidates (m_option_suggestions, with_arg);
- free (with_arg);
- }
- }
- else
- add_misspelling_candidates (m_option_suggestions, opt_text);
- break;
-
- case OPT_fsanitize_:
- case OPT_fsanitize_recover_:
- /* -fsanitize= and -fsanitize-recover= can take
- a comma-separated list of arguments. Given that combinations
- are supported, we can't add all potential candidates to the
- vec, but if we at least add them individually without commas,
- we should do a better job e.g. correcting
- "-sanitize=address"
- to
- "-fsanitize=address"
- rather than to "-Wframe-address" (PR driver/69265). */
- {
- for (int j = 0; sanitizer_opts[j].name != NULL; ++j)
- {
- /* Get one arg at a time e.g. "-fsanitize=address". */
- char *with_arg = concat (opt_text,
- sanitizer_opts[j].name,
- NULL);
- /* Add with_arg and all of its variant spellings e.g.
- "-fno-sanitize=address" to candidates (albeit without
- leading dashes). */
- add_misspelling_candidates (m_option_suggestions, with_arg);
- free (with_arg);
- }
- }
- break;
- }
- }
-}
-
-/* Helper function for driver::handle_unrecognized_options.
-
- Given an unrecognized option BAD_OPT (without the leading dash),
- locate the closest reasonable matching option (again, without the
- leading dash), or NULL.
-
- The returned string is owned by the driver instance. */
-
-const char *
-driver::suggest_option (const char *bad_opt)
-{
- /* Lazily populate m_option_suggestions. */
- if (!m_option_suggestions)
- build_option_suggestions ();
- gcc_assert (m_option_suggestions);
-
- /* "m_option_suggestions" is now populated. Use it. */
- return find_closest_string
- (bad_opt,
- (auto_vec <const char *> *) m_option_suggestions);
-}
-
/* Reject switches that no pass was interested in. */
void
for (size_t i = 0; (int) i < n_switches; i++)
if (! switches[i].validated)
{
- const char *hint = suggest_option (switches[i].part1);
+ const char *hint = m_option_proposer.suggest_option (switches[i].part1);
if (hint)
- error ("unrecognized command line option %<-%s%>;"
+ error ("unrecognized command-line option %<-%s%>;"
" did you mean %<-%s%>?",
switches[i].part1, hint);
else
- error ("unrecognized command line option %<-%s%>",
+ error ("unrecognized command-line option %<-%s%>",
switches[i].part1);
}
}
{
printf (_("%s %s%s\n"), progname, pkgversion_string,
version_string);
- printf ("Copyright %s 2016 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 (!combine_inputs && have_c && have_o && lang_n_infiles > 1)
fatal_error (input_location,
- "cannot specify -o with -c, -S or -E with multiple files");
+ "cannot specify %<-o%> with %<-c%>, %<-S%> or %<-E%> "
+ "with multiple files");
/* No early exit needed from main; we can continue. */
return false;
else if (compare_debug && debug_check_temp_file[0])
{
if (verbose_flag)
- inform (0, "recompiling with -fcompare-debug");
+ inform (UNKNOWN_LOCATION,
+ "recompiling with %<-fcompare-debug%>");
compare_debug = -compare_debug;
n_switches = n_switches_debug_check[1];
if (value < 0)
{
- error ("during -fcompare-debug recompilation");
+ error ("during %<-fcompare-debug%> recompilation");
this_file_error = 1;
}
debug_check_temp_file[1]));
if (verbose_flag)
- inform (0, "comparing final insns dumps");
+ inform (UNKNOWN_LOCATION, "comparing final insns dumps");
if (compare_files (debug_check_temp_file))
this_file_error = 1;
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)
{
int tmp = execution_count;
+ detect_jobserver ();
+
if (! have_c)
{
#if HAVE_LTO_PLUGIN > 0
{
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
false);
if (!temp_spec)
fatal_error (input_location,
- "-fuse-linker-plugin, but %s not found",
+ "%<-fuse-linker-plugin%>, but %s not found",
LTOPLUGINSONAME);
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
}
}
+/* Detect whether jobserver is active and working. If not drop
+ --jobserver-auth from MAKEFLAGS. */
+
+void
+driver::detect_jobserver () const
+{
+ /* Detect jobserver and drop it if it's not working. */
+ const char *makeflags = env.get ("MAKEFLAGS");
+ if (makeflags != NULL)
+ {
+ const char *needle = "--jobserver-auth=";
+ const char *n = strstr (makeflags, needle);
+ if (n != NULL)
+ {
+ int rfd = -1;
+ int wfd = -1;
+
+ bool jobserver
+ = (sscanf (n + strlen (needle), "%d,%d", &rfd, &wfd) == 2
+ && rfd > 0
+ && wfd > 0
+ && is_valid_fd (rfd)
+ && is_valid_fd (wfd));
+
+ /* Drop the jobserver if it's not working now. */
+ if (!jobserver)
+ {
+ unsigned offset = n - makeflags;
+ char *dup = xstrdup (makeflags);
+ dup[offset] = '\0';
+
+ const char *space = strchr (makeflags + offset, ' ');
+ if (space != NULL)
+ strcpy (dup + offset, space);
+ xputenv (concat ("MAKEFLAGS=", dup, NULL));
+ }
+ }
+ }
+}
+
/* Determine what the exit code of the driver should be. */
int
{
for (cp = compilers + n_compilers - 1; cp >= compilers; cp--)
if (cp->suffix[0] == '@' && !strcmp (cp->suffix + 1, language))
- return cp;
+ {
+ if (name != NULL && strcmp (name, "-") == 0
+ && (strcmp (cp->suffix, "@c-header") == 0
+ || strcmp (cp->suffix, "@c++-header") == 0)
+ && !have_E)
+ fatal_error (input_location,
+ "cannot use %<-%> as input filename for a "
+ "precompiled header");
+
+ return cp;
+ }
error ("language %s not recognized", language);
return 0;
{
char *result = XNEWVEC (char, len + 1);
+ gcc_checking_assert (strlen (s) >= (unsigned int) len);
memcpy (result, s, len);
result[len] = 0;
return result;
}
-void
-pfatal_with_name (const char *name)
-{
- perror_with_name (name);
- delete_temp_files ();
- exit (1);
-}
-
-static void
-perror_with_name (const char *name)
-{
- error ("%s: %m", name);
-}
\f
static inline void
validate_switches_from_spec (const char *spec, bool user)
const char *p = spec;
char c;
while ((c = *p++))
- if (c == '%' && (*p == '{' || *p == '<' || (*p == 'W' && *++p == '{')))
+ if (c == '%'
+ && (*p == '{'
+ || *p == '<'
+ || (*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, true);
}
else
p++;
/* Check whether a particular argument was used. The first time we
canonicalize the switches to keep only the ones we care about. */
-class used_arg_t
+struct used_arg_t
{
public:
int operator () (const char *p, int len);
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 == ' ')
Returns the value of the environment variable given by its first argument,
concatenated with the second argument. If the variable is not defined, a
fatal error is issued unless such undefs are internally allowed, in which
- case the variable name is used as the variable value. */
+ case the variable name prefixed by a '/' is used as the variable value.
+
+ The leading '/' allows using the result at a spot where a full path would
+ normally be expected and when the actual value doesn't really matter since
+ undef vars are allowed. */
static const char *
getenv_spec_function (int argc, const char **argv)
varname = argv[0];
value = env.get (varname);
+ /* If the variable isn't defined and this is allowed, craft our expected
+ return value. Assume variable names used in specs strings don't contain
+ any active spec character so don't need escaping. */
if (!value && spec_undefvar_allowed)
- value = varname;
+ {
+ result = XNEWVAR (char, strlen(varname) + 2);
+ sprintf (result, "/%s", varname);
+ return result;
+ }
if (!value)
fatal_error (input_location,
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)
- return ((flag_sanitize & (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT))
+ return ((flag_sanitize
+ & (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT))
&& !flag_sanitize_undefined_trap_on_error) ? "" : NULL;
if (strcmp (argv[0], "leak") == 0)
return ((flag_sanitize
fatal_error (input_location,
"too many arguments to %%:compare-debug-dump-opt");
- do_spec_2 ("%{fdump-final-insns=*:%*}");
+ do_spec_2 ("%{fdump-final-insns=*:%*}", NULL);
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}}");
- ext = ".gkd";
- }
+ do_spec_2 ("%B.gkd", NULL);
else if (!compare_debug)
return NULL;
else
- do_spec_2 ("%g.gkd");
+ 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*:%*}}");
- 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");
- 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 the n in ARGV[1] == -opt=<n> is greater than ARGV[2].
+/* Returns "" if ARGV[ARGC - 2] is greater than ARGV[ARGC-1].
Otherwise, return NULL. */
static const char *
if (argc == 1)
return NULL;
- gcc_assert (argc == 3);
- gcc_assert (argv[0][0] == '-');
- gcc_assert (argv[0][1] == '\0');
+ gcc_assert (argc >= 2);
- /* Point p to <n> in in -opt=<n>. */
- const char *p = argv[1];
- while (true)
- {
- char c = *p;
- if (c == '\0')
- gcc_unreachable ();
+ long arg = strtol (argv[argc - 2], &converted, 10);
+ gcc_assert (converted != argv[argc - 2]);
- ++p;
+ long lim = strtol (argv[argc - 1], &converted, 10);
+ gcc_assert (converted != argv[argc - 1]);
- if (c == '=')
- break;
- }
+ if (arg > lim)
+ return "";
+
+ return NULL;
+}
- long arg = strtol (p, &converted, 10);
- gcc_assert (converted != p);
+/* Returns "" if debug_info_level is greater than ARGV[ARGC-1].
+ Otherwise, return NULL. */
- long lim = strtol (argv[2], &converted, 10);
- gcc_assert (converted != argv[2]);
+static const char *
+debug_level_greater_than_spec_func (int argc, const char **argv)
+{
+ char *converted;
- if (arg > lim)
+ if (argc != 1)
+ fatal_error (input_location,
+ "wrong number of arguments to %%:debug-level-gt");
+
+ long arg = strtol (argv[0], &converted, 10);
+ gcc_assert (converted != argv[0]);
+
+ if (debug_info_level > arg)
return "";
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)
+{
+ struct prefix_list *iter, *next;
+ iter = prefix->plist;
+ while (iter)
+ {
+ next = iter->next;
+ free (const_cast <char *> (iter->prefix));
+ XDELETE (iter);
+ iter = next;
+ }
+ prefix->plist = 0;
+ prefix->max_len = 0;
+}
+
+/* The function takes 3 arguments: OPTION name, file name and location
+ where we search for Fortran modules.
+ When the FILE is found by find_file, return OPTION=path_to_file. */
+
+static const char *
+find_fortran_preinclude_file (int argc, const char **argv)
+{
+ char *result = NULL;
+ if (argc != 3)
+ return NULL;
+
+ struct path_prefix prefixes = { 0, 0, "preinclude" };
+
+ /* Search first for 'finclude' folder location for a header file
+ installed by the compiler (similar to omp_lib.h). */
+ add_prefix (&prefixes, argv[2], NULL, 0, 0, 0);
+#ifdef TOOL_INCLUDE_DIR
+ /* Then search: <prefix>/<target>/<include>/finclude */
+ add_prefix (&prefixes, TOOL_INCLUDE_DIR "/finclude/",
+ NULL, 0, 0, 0);
+#endif
+#ifdef NATIVE_SYSTEM_HEADER_DIR
+ /* Then search: <sysroot>/usr/include/finclude/<multilib> */
+ add_sysrooted_hdrs_prefix (&prefixes, NATIVE_SYSTEM_HEADER_DIR "/finclude/",
+ NULL, 0, 0, 0);
+#endif
+
+ const char *path = find_a_file (&include_prefixes, argv[1], R_OK, false);
+ if (path != NULL)
+ result = concat (argv[0], path, NULL);
+ else
+ {
+ path = find_a_file (&prefixes, argv[1], R_OK, false);
+ if (path != NULL)
+ result = concat (argv[0], path, NULL);
+ }
+
+ path_prefix_reset (&prefixes);
+ 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;
-
- for (len = 0; orig[len]; len++)
- if (orig[len] == ' ' || orig[len] == '\t') number_of_space++;
+ return quote_string (orig, whitespace_to_convert_p, NULL);
+}
- if (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)
{
- 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;
+ case ' ':
+ case '\t':
+ case '\n':
+ case '|':
+ case '%':
+ case '\\':
+ return true;
+
+ default:
+ return false;
+ }
}
-static void
-path_prefix_reset (path_prefix *prefix)
+/* Like convert_white_space, but deactivate all active spec chars by
+ quoting them. */
+
+static inline char *
+quote_spec (char *orig)
{
- struct prefix_list *iter, *next;
- iter = prefix->plist;
- while (iter)
+ 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)
{
- next = iter->next;
- free (const_cast <char *> (iter->prefix));
- XDELETE (iter);
- iter = next;
+ free (orig);
+ return xstrdup ("%\"");
}
- prefix->plist = 0;
- prefix->max_len = 0;
+
+ return quote_spec (orig);
}
/* Restore all state within gcc.c to the initial state, so that the driver
driver::finalize ()
{
env.restore ();
- params_c_finalize ();
diagnostic_finish (global_dc);
is_cpp_driver = 0;
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;
- finalize_options_struct (&global_options);
- finalize_options_struct (&global_options_set);
-
obstack_free (&obstack, NULL);
obstack_free (&opts_obstack, NULL); /* in opts.c */
obstack_free (&collect_obstack, NULL);
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;
processing_spec_function = 0;
- argbuf.truncate (0);
+ clear_args ();
have_c = 0;
have_o = 0;
mdswitches = NULL;
n_mdswitches = 0;
- debug_auxbase_opt = NULL;
-
used_arg.finalize ();
}