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.
"%{fuse-ld=*:-fuse-ld=%*} " LINK_COMPRESS_DEBUG_SPEC \
"%X %{o*} %{e*} %{N} %{n} %{r}\
%{s} %{t} %{u*} %{z} %{Z} %{!nostdlib:%{!nostartfiles:%S}} \
- %{static|no-pie|static-pie:} %{L*} %(mfwrap) %(link_libgcc) " \
+ %{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)}\
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*}\
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;
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
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
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':
{
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_spec);
else if (p[0] == 'W' && p[1] == '{')
p = validate_switches (p+2, user_spec);
+ else if (p[0] == '@' && p[1] == '{')
+ p = validate_switches (p+2, user_spec);
}
else
p++;
processing_spec_function = 0;
- argbuf.truncate (0);
+ clear_args ();
have_c = 0;
have_o = 0;