Hello, This patch restores the --specs flags functionality.
There are 2 parts 1) Lazily check the flag validation until all command line spec files are read. For this purpose, 'read_specs' records specs, to be analyzed with 'file_spec_p'. Such flags have 'live_cond' = SWITCH_USER 2) --specs options need to be propagated to COLLECT_GCC_OPTIONS. Without this lto1 might be given user flags without the corresponding --spec file. Note that --specs LTO is broken since 4.6 included. Regression tests included. Bootstrapped and reg tested on [x86,sh4] Linux and regression tested on sh-superh-elf with proprietary BSP supports. Also tested with LTO. Some references on the issue: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49858 http://gcc.gnu.org/ml/gcc/2012-05/msg00079.html Having experienced with the implementation a bit, I prefer to implement this lazy check, rather than explicitly expose the user flags thru a new -fextension option, because I think the full previous behavior can be restored without lost of missing diagnostic (as opposed with older version (< 4.6) and without ambiguity : There is an error for unrecognized flags either from internal .opt or explicitly provided in a spec file. Thanks a lot for your feedback. I'd like to candidate this change for the 4.7 and trunk branches. Christian
2012-05-18 Christian Bruel <christian.br...@st.com> * gcc.c (save_switch) Add user_p parameter. Set live_cond. (read_specs): Likewise. Call record_file_spec. (main): Call read_specs with user_p. (record_file_spec): New function. (file_spec_p): Likewise. (SWITCH_USER): New flag. (driver_unknown_option_callback): Test OPT_SPECIAL_unknown. Add user_p parameter. (set_collect_gcc_options): Don't ignore SWITCH_USER. (check_live_switch): Likewise. (validate_switches): Validate SWITCH_USER options. (driver_handle_option): Propagate OPT_specs to collect2. 2012-05-18 Christian Bruel <christian.br...@st.com> * gcc.dg/spec-options.c: Test recognized --specs option. * gcc.dg/spec-options-2.c: Test unknown --specs option. * gcc.dg/foo.specs: New file. Index: gcc/gcc.c =================================================================== --- gcc/gcc.c (revision 187500) +++ gcc/gcc.c (working copy) @@ -190,7 +190,7 @@ static void store_arg (const char *, int, int); static void insert_wrapper (const char *); static char *load_specs (const char *); -static void read_specs (const char *, int); +static void read_specs (const char *, bool, bool); static void set_spec (const char *, const char *); static struct compiler *lookup_compiler (const char *, size_t, const char *); static char *build_search_list (const struct path_prefix *, const char *, @@ -1472,6 +1472,47 @@ specs = sl; } +struct file_specs +{ + struct file_specs *next; + const char *spec; +}; + +static struct file_specs *file_specs_head; + +static void +record_file_spec (const char *spec) +{ + struct file_specs *user = XNEW (struct file_specs); + + user->next = file_specs_head; + + if (spec[0] == '+' && ISSPACE ((unsigned char)spec[1])) + user->spec = spec + 1; + else + user->spec = spec; + + file_specs_head = user; +} + +static bool +file_spec_p (const char *name) +{ + struct file_specs *user; + + for (user = file_specs_head; user; user = user->next) + { + const char *p = user->spec; + char c; + while ((c = *p++)) + if (c == '%' && (*p == '{' || *p == '<' || (*p == 'W' && *++p == '{'))) + if (!strcmp (name, p + 1)) + return true; + } + + return false; +} + /* 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 @@ -1686,7 +1727,7 @@ Anything invalid in the file is a fatal error. */ static void -read_specs (const char *filename, int main_p) +read_specs (const char *filename, bool main_p, bool user_p) { char *buffer; char *p; @@ -1735,7 +1776,7 @@ p[-2] = '\0'; new_filename = find_a_file (&startfile_prefixes, p1, R_OK, true); - read_specs (new_filename ? new_filename : p1, FALSE); + read_specs (new_filename ? new_filename : p1, false, user_p); continue; } else if (!strncmp (p1, "%include_noerr", sizeof "%include_noerr" - 1) @@ -1756,7 +1797,7 @@ p[-2] = '\0'; new_filename = find_a_file (&startfile_prefixes, p1, R_OK, true); if (new_filename) - read_specs (new_filename, FALSE); + read_specs (new_filename, false, user_p); else if (verbose_flag) fnotice (stderr, "could not find specs file %s\n", p1); continue; @@ -1899,7 +1940,12 @@ if (! strcmp (suffix, "*link_command")) link_command_spec = spec; else - set_spec (suffix + 1, spec); + { + set_spec (suffix + 1, spec); + + if (user_p) + record_file_spec (spec); + } } else { @@ -2806,20 +2852,22 @@ SWITCH_IGNORE_PERMANENTLY to indicate this switch should be ignored in all do_spec calls afterwards. Used for %<S from self specs. The `validated' field is nonzero if any spec has looked at this switch; - if it remains zero at the end of the run, it must be meaningless. */ + if it remains zero at the end of the run, it must be meaningless. + SWITCH_USER to indicate this switch could be defined in a user spec file. */ #define SWITCH_LIVE (1 << 0) #define SWITCH_FALSE (1 << 1) #define SWITCH_IGNORE (1 << 2) #define SWITCH_IGNORE_PERMANENTLY (1 << 3) #define SWITCH_KEEP_FOR_GCC (1 << 4) +#define SWITCH_USER (1 << 5) struct switchstr { const char *part1; const char **args; unsigned int live_cond; - unsigned char validated; + bool validated; unsigned char ordering; }; @@ -3086,11 +3134,11 @@ } /* Save an option OPT with N_ARGS arguments in array ARGS, marking it - as validated if VALIDATED. */ + as validated if VALIDATED, and USER to check after user spec file are read. */ static void save_switch (const char *opt, size_t n_args, const char *const *args, - bool validated) + bool validated, bool user) { alloc_switch (); switches[n_switches].part1 = opt + 1; @@ -3103,7 +3151,7 @@ switches[n_switches].args[n_args] = NULL; } - switches[n_switches].live_cond = 0; + switches[n_switches].live_cond = user ? SWITCH_USER : 0; switches[n_switches].validated = validated; switches[n_switches].ordering = 0; n_switches++; @@ -3123,9 +3171,17 @@ diagnosed only if there are warnings. */ save_switch (decoded->canonical_option[0], decoded->canonical_option_num_elements - 1, - &decoded->canonical_option[1], false); + &decoded->canonical_option[1], false, false); return false; } + + if (decoded->opt_index == OPT_SPECIAL_unknown) + { + save_switch (decoded->canonical_option[0], + decoded->canonical_option_num_elements - 1, + &decoded->canonical_option[1], false, true); + return false; + } else return true; } @@ -3150,7 +3206,7 @@ else save_switch (decoded->canonical_option[0], decoded->canonical_option_num_elements - 1, - &decoded->canonical_option[1], false); + &decoded->canonical_option[1], false, false); } static const char *spec_lang = 0; @@ -3293,7 +3349,7 @@ compare_debug_opt = NULL; else compare_debug_opt = arg; - save_switch (compare_debug_replacement_opt, 0, NULL, validated); + save_switch (compare_debug_replacement_opt, 0, NULL, validated, false); return true; case OPT_Wa_: @@ -3378,12 +3434,12 @@ case OPT_L: /* Similarly, canonicalize -L for linkers that may not accept separate arguments. */ - save_switch (concat ("-L", arg, NULL), 0, NULL, validated); + save_switch (concat ("-L", arg, NULL), 0, NULL, validated, false); return true; case OPT_F: /* Likewise -F. */ - save_switch (concat ("-F", arg, NULL), 0, NULL, validated); + save_switch (concat ("-F", arg, NULL), 0, NULL, validated, false); return true; case OPT_save_temps: @@ -3426,7 +3482,7 @@ user_specs_head = user; user_specs_tail = user; } - do_save = false; + validated = true; break; case OPT__sysroot_: @@ -3505,7 +3561,7 @@ 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); + save_switch ("-o", 1, &arg, validated, false); return true; case OPT_static_libgcc: @@ -3528,7 +3584,7 @@ if (do_save) save_switch (decoded->canonical_option[0], decoded->canonical_option_num_elements - 1, - &decoded->canonical_option[1], validated); + &decoded->canonical_option[1], validated, false); return true; } @@ -4003,8 +4059,8 @@ /* Ignore elided switches. */ if ((switches[i].live_cond - & (SWITCH_IGNORE | SWITCH_KEEP_FOR_GCC)) - == SWITCH_IGNORE) + & (SWITCH_IGNORE | SWITCH_KEEP_FOR_GCC | SWITCH_USER)) + == (SWITCH_IGNORE | SWITCH_USER)) continue; obstack_grow (&collect_obstack, "'-", 2); @@ -4330,7 +4386,7 @@ save_switch (decoded_options[j].canonical_option[0], (decoded_options[j].canonical_option_num_elements - 1), - &decoded_options[j].canonical_option[1], false); + &decoded_options[j].canonical_option[1], false, false); break; default: @@ -5789,7 +5845,7 @@ /* If we already processed this switch and determined if it was live or not, return our past determination. */ - if (switches[switchnum].live_cond != 0) + if ((switches[switchnum].live_cond & ~SWITCH_USER) != 0) return ((switches[switchnum].live_cond & SWITCH_LIVE) != 0 && (switches[switchnum].live_cond & SWITCH_FALSE) == 0 && (switches[switchnum].live_cond & SWITCH_IGNORE_PERMANENTLY) @@ -5861,7 +5917,7 @@ static void give_switch (int switchnum, int omit_first_word) { - if ((switches[switchnum].live_cond & SWITCH_IGNORE) != 0) + if (switches[switchnum].live_cond & (SWITCH_IGNORE | SWITCH_USER)) return; if (!omit_first_word) @@ -6269,7 +6325,7 @@ specs_file = find_a_file (&startfile_prefixes, "specs", R_OK, true); /* Read the specs file unless it is a default one. */ if (specs_file != 0 && strcmp (specs_file, "specs")) - read_specs (specs_file, TRUE); + read_specs (specs_file, true, false); else init_spec (); @@ -6282,7 +6338,7 @@ strcat (specs_file, just_machine_suffix); strcat (specs_file, "specs"); if (access (specs_file, R_OK) == 0) - read_specs (specs_file, TRUE); + read_specs (specs_file, true, false); /* Process any configure-time defaults specified for the command line options, via OPTION_DEFAULT_SPECS. */ @@ -6402,7 +6458,7 @@ { char *filename = find_a_file (&startfile_prefixes, uptr->filename, R_OK, true); - read_specs (filename ? filename : uptr->filename, FALSE); + read_specs (filename ? filename : uptr->filename, false, true); } /* Process any user self specs. */ @@ -6501,7 +6557,7 @@ for (i = 0; (int) i < n_switches; i++) if (! switches[i].validated) - error ("unrecognized option %<-%s%>", switches[i].part1); + error ("unrecognized command line option %<-%s%>", switches[i].part1); /* Obey some of the options. */ @@ -7107,9 +7163,16 @@ { /* Mark all matching switches as valid. */ for (i = 0; i < n_switches; i++) - if (!strncmp (switches[i].part1, atom, len) - && (starred || switches[i].part1[len] == 0)) - switches[i].validated = 1; + if (!switches[i].validated && !strncmp (switches[i].part1, atom, len)) + { + if (switches[i].live_cond & SWITCH_USER) + { + if (file_spec_p (start)) + switches[i].validated = true; + } + else if (starred || switches[i].part1[len] == 0) + switches[i].validated = true; + } } if (*p) p++; @@ -8056,7 +8119,7 @@ abort (); file = find_a_file (&startfile_prefixes, argv[0], R_OK, true); - read_specs (file ? file : argv[0], FALSE); + read_specs (file ? file : argv[0], false, false); return NULL; } Index: gcc/testsuite/gcc.dg/spec-options.c =================================================================== --- gcc/testsuite/gcc.dg/spec-options.c (revision 0) +++ gcc/testsuite/gcc.dg/spec-options.c (revision 0) @@ -0,0 +1,15 @@ +/* Check that -mfoo is accepted if defined in a user spec. */ +/* { dg-do compile } */ +/* { dg-do run { target sh*-*-* } } */ +/* { dg-options "-B${srcdir}/gcc.dg --specs=foo.specs -mfoo" } */ + +extern void abort(void); + +int main(void) +{ +#ifdef FOO + return 0; +#else + abort(); +#endif +} Index: gcc/testsuite/gcc.dg/spec-options-2.c =================================================================== --- gcc/testsuite/gcc.dg/spec-options-2.c (revision 0) +++ gcc/testsuite/gcc.dg/spec-options-2.c (revision 0) @@ -0,0 +1,17 @@ +/* Check that -mbar is rejected if not defined in a user spec. */ +/* { dg-do compile } */ +/* { dg-options "-B${srcdir}/gcc.dg --specs=foo.specs -mbar" } */ +/* { dg-error "unrecognized command line option" "" { target *-*-* } 0 } */ + +extern void abort(void); + +int main(void) +{ +#ifdef FOO + return 0; +#else + abort(); +#endif +} + + Index: gcc/testsuite/gcc.dg/foo.specs =================================================================== --- gcc/testsuite/gcc.dg/foo.specs (revision 0) +++ gcc/testsuite/gcc.dg/foo.specs (revision 0) @@ -0,0 +1,3 @@ +*cc1runtime: ++ %{mfoo: -DFOO} +