[PATCH] pass @-files to collect2 and beyond, take 2

Nathan Froyd froydnj@codesourcery.com
Fri Apr 27 17:56:00 GMT 2007


On Tue, Apr 24, 2007 at 02:57:56PM -0400, DJ Delorie wrote:
> My only comment is that writeargv() should be passed a FILE*, not a
> file name, so that the application can do better error handling if the
> file cannot be opened.  The proposed implementation has no way to tell
> what caused the call to fail, just that it failed.  Also, the fclose()
> isn't checked for errors.

Revised patches attached:

1. writeargv takes a FILE*, not a char*.  Error-checking for fopen and
   fclose has been moved to the callers.  f* calls in writeargv are
   now all checked for errors.

2. HAVE_GNU_LD is now always defined, to 0/1 as appropriate.

Bootstrapped and tested on x86_64-unknown-linux-gnu, no regressions.

OK?

-Nathan

include/
2007-04-27  Nathan Froyd  <froydnj@codesourcery.com>

	* libiberty.h (writeargv): Declare.

libiberty/
2007-04-27  Nathan Froyd  <froydnj@codesourcery.com>

	* argv.c (writeargv): New function.

gcc/
2007-04-27  Nathan Froyd  <froydnj@codesourcery.com>

	* configure.ac: Add define for HAVE_GNU_LD.
	* configure: Regenerate.
	* config.in: Regenerate.

gcc/
2007-04-27  Nathan Froyd  <froydnj@codesourcery.com>

	* gcc.c (at_file_supplied): New variable.
	(main): Set it if we expanded argv.
	(do_spec_1): Pass an @-file to the linker if we were called with
	an @-file argument and HAVE_GNU_LD.
	* collect2.c (at_file_supplied): New variable.
	(response_file): New variable.
	(collect_exit): Unlink response_file if necessary.
	(handler): Likewise.
	(do_wait): Likewise.
	(main): Set at_file_supplied if we expanded argv.
	(collect_execute): Pass an @-file to subprocesses if we were called
	with an @-file argument.
-------------- next part --------------
Index: gcc/gcc.c
===================================================================
--- gcc/gcc.c	(revision 124103)
+++ gcc/gcc.c	(working copy)
@@ -127,6 +127,9 @@ static const char dir_separator_str[] = 
 /* Flag set by cppspec.c to 1.  */
 int is_cpp_driver;
 
+/* Flag set to non-zero if an @file argument has been supplied to gcc.  */
+static bool at_file_supplied;
+
 /* Flag saying to pass the greatest exit code returned by a sub-process
    to the calling program.  */
 static int pass_exit_codes;
@@ -5006,9 +5009,63 @@ do_spec_1 (const char *spec, int inswitc
 	      int max = n_infiles;
 	      max += lang_specific_extra_outfiles;
 
-	      for (i = 0; i < max; i++)
-		if (outfiles[i])
-		  store_arg (outfiles[i], 0, 0);
+              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 *temp_file = make_temp_file ("");
+                  char *at_argument;
+                  char **argv;
+                  int n_files, j, status;
+                  FILE *f;
+
+                  at_argument = concat ("@", temp_file, NULL);
+                  store_arg (at_argument, 0, 0);
+
+                  /* 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 = alloca (sizeof (char *) * (n_files + 1));
+
+                  /* Copy the strings over.  */
+                  for (i = 0, j = 0; i < max; i++)
+                    if (outfiles[i])
+                      {
+                        argv[j] = (char *) outfiles[i];
+                        j++;
+                      }
+                  argv[j] = NULL;
+
+                  f = fopen (temp_file, "w");
+
+                  if (f == NULL)
+                    fatal ("could not open temporary response file %s",
+                           temp_file);
+
+                  status = writeargv (argv, f);
+
+                  if (status)
+                    fatal ("could not write to temporary response file %s",
+                           temp_file);
+
+                  status = fclose (f);
+
+                  if (EOF == status)
+                    fatal ("could not close temporary response file %s",
+                           temp_file);
+
+                  record_temp_file (temp_file, 1, 1);
+                }
+              else
+                for (i = 0; i < max; i++)
+	          if (outfiles[i])
+		    store_arg (outfiles[i], 0, 0);
 	      break;
 	    }
 
@@ -6090,6 +6147,7 @@ main (int argc, char **argv)
   char *specs_file;
   const char *p;
   struct user_specs *uptr;
+  char **old_argv = argv;
 
   p = argv[0] + strlen (argv[0]);
   while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
@@ -6100,6 +6158,10 @@ main (int argc, char **argv)
 
   expandargv (&argc, &argv);
 
+  /* Determine if any expansions were made.  */
+  if (argv != old_argv)
+    at_file_supplied = true;
+
   prune_options (&argc, &argv);
 
 #ifdef GCC_DRIVER_HOST_INITIALIZATION
Index: gcc/collect2.c
===================================================================
--- gcc/collect2.c	(revision 124103)
+++ gcc/collect2.c	(working copy)
@@ -1,7 +1,7 @@
 /* Collect static initialization info into data structures that can be
    traversed by C++ initialization and finalization routines.
    Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
    Contributed by Chris Smith (csmith@convex.com).
    Heavily modified by Michael Meissner (meissner@cygnus.com),
    Per Bothner (bothner@cygnus.com), and John Gilmore (gnu@cygnus.com).
@@ -202,6 +202,9 @@ static struct head exports;		/* list of 
 #endif
 static struct head frame_tables;	/* list of frame unwind info tables */
 
+static bool at_file_supplied;		/* Whether to use @file arguments */
+static char *response_file;		/* Name of any current response file */
+
 struct obstack temporary_obstack;
 char * temporary_firstobj;
 
@@ -302,6 +305,9 @@ collect_exit (int status)
   if (status != 0 && output_file != 0 && output_file[0])
     maybe_unlink (output_file);
 
+  if (response_file)
+    maybe_unlink (response_file);
+
   exit (status);
 }
 
@@ -393,6 +399,9 @@ handler (int signo)
     maybe_unlink (export_file);
 #endif
 
+  if (response_file)
+    maybe_unlink (response_file);
+
   signal (signo, SIG_DFL);
   raise (signo);
 }
@@ -793,7 +802,15 @@ main (int argc, char **argv)
   char **object_lst;
   const char **object;
   int first_file;
-  int num_c_args	= argc+9;
+  int num_c_args;
+  char **old_argv;
+
+  old_argv = argv;
+  expandargv (&argc, &argv);
+  if (argv != old_argv)
+    at_file_supplied = 1;
+
+  num_c_args = argc + 9;
 
   no_demangle = !! getenv ("COLLECT_NO_DEMANGLE");
 
@@ -1513,6 +1530,12 @@ do_wait (const char *prog, struct pex_ob
       error ("%s returned %d exit status", prog, ret);
       collect_exit (ret);
     }
+
+  if (response_file)
+    {
+      unlink (response_file);
+      response_file = NULL;
+    }
 }
 
 
@@ -1525,6 +1548,47 @@ collect_execute (const char *prog, char 
   struct pex_obj *pex;
   const char *errmsg;
   int err;
+  char *response_arg = NULL;
+  char *response_argv[3] ATTRIBUTE_UNUSED;
+
+  if (HAVE_GNU_LD && at_file_supplied && argv[0] != NULL)
+    {
+      /* If using @file arguments, create a temporary file and put the
+         contents of argv into it.  Then change argv to an array corresponding
+         to a single argument @FILE, where FILE is the temporary filename.  */
+
+      char **current_argv = argv + 1;
+      char *argv0 = argv[0];
+      int status;
+      FILE *f;
+
+      /* Note: we assume argv contains at least one element; this is
+         checked above.  */
+
+      response_file = make_temp_file ("");
+
+      f = fopen (response_file, "w");
+
+      if (f == NULL)
+        fatal ("could not open response file %s", response_file);
+
+      status = writeargv (current_argv, f);
+
+      if (status)
+        fatal ("could not write to response file %s", response_file);
+
+      status = fclose (f);
+
+      if (EOF == status)
+        fatal ("could not close response file %s", response_file);
+
+      response_arg = concat ("@", response_file, NULL);
+      response_argv[0] = argv0;
+      response_argv[1] = response_arg;
+      response_argv[2] = NULL;
+
+      argv = response_argv;
+    }
 
   if (vflag || debug)
     {
@@ -1568,6 +1632,9 @@ collect_execute (const char *prog, char 
 	fatal (errmsg);
     }
 
+  if (response_arg)
+    free (response_arg);
+
   return pex;
 }
 
-------------- next part --------------
Index: include/libiberty.h
===================================================================
--- include/libiberty.h	(revision 124103)
+++ include/libiberty.h	(working copy)
@@ -86,6 +86,10 @@ extern char **dupargv (char **) ATTRIBUT
 
 extern void expandargv PARAMS ((int *, char ***));
 
+/* Write argv to an @-file, inserting necessary quoting.  */
+
+extern int writeargv PARAMS ((char **, FILE *));
+
 /* Return the last component of a path name.  Note that we can't use a
    prototype here because the parameter is declared inconsistently
    across different systems, sometimes as "char *" and sometimes as
Index: libiberty/argv.c
===================================================================
--- libiberty/argv.c	(revision 124103)
+++ libiberty/argv.c	(working copy)
@@ -290,6 +290,62 @@ char **buildargv (const char *input)
 
 /*
 
+@deftypefn Extension int writeargv (const char **@var{argv}, FILE *@{file})
+
+Write each member of ARGV, handling all necessary quoting, to the file
+named by FILE, separated by whitespace.  Return 0 on success, non-zero
+if an error occurred while writing to FILE.
+
+@end deftypefn
+
+*/
+
+int
+writeargv (char **argv, FILE *f)
+{
+  int status = 0;
+
+  if (f == NULL)
+    return 1;
+
+  while (*argv != NULL)
+    {
+      int ret;
+      const char *arg = *argv;
+
+      while (*arg != EOS)
+        {
+          char c = *arg;
+
+          if (ISSPACE(c) || c == '\\' || c == '\'' || c == '"')
+            if (EOF == fputc ('\\', f))
+              {
+                status = 1;
+                goto done;
+              }
+
+          if (EOF == fputc (c, f))
+            {
+              status = 1;
+              goto done;
+            }
+          arg++;
+        }
+
+      if (EOF == fputc ('\n', f))
+        {
+          status = 1;
+          goto done;
+        }
+      argv++;
+    }
+
+ done:
+  return status;
+}
+
+/*
+
 @deftypefn Extension void expandargv (int *@var{argcp}, char ***@var{argvp})
 
 The @var{argcp} and @code{argvp} arguments are pointers to the usual
-------------- next part --------------
Index: gcc/configure.ac
===================================================================
--- gcc/configure.ac	(revision 124103)
+++ gcc/configure.ac	(working copy)
@@ -206,6 +206,9 @@ if test x"${DEFAULT_LINKER+set}" = x"set
 	[Define to enable the use of a default linker.])
 fi
 
+gnu_ld=`if test x"$gnu_ld_flag" = x"yes"; then echo 1; else echo 0; fi`
+AC_DEFINE_UNQUOTED(HAVE_GNU_LD, $gnu_ld, [Define if using GNU ld.])
+
 AC_MSG_CHECKING([whether a default linker was specified])
 if test x"${DEFAULT_LINKER+set}" = x"set"; then
   if test x"$gnu_ld_flag" = x"no"; then


More information about the Gcc-patches mailing list