diff mbox series

Refactor `execute` from gcc.c

Message ID 20200519002025.reeo63qcm7qxfutt@smtp.gmail.com
State New
Headers show
Series Refactor `execute` from gcc.c | expand

Commit Message

Giuliano Belinassi May 19, 2020, 12:20 a.m. UTC
Hi,

After having so much trouble working on the `execute' function inside
gcc.c, I decided to refactor it so that it could be more digestible.
Since I am using it on my branch, I am submitting this patch for
"battlefield" testing.

Bootstrapped and ran the testsuite on Linux x86_64.

-----

Refactor `execute' function to avoid exposing unnecessary details of
how it works, encapsulating each important step into a function.

gcc/ChangeLog
2020-05-18  Giuliano Belinassi  <giuliano.belinassi@usp.brr>

	* gcc.c (struct command): Move from execute.
	(await_commands_to_finish): New function.
	(split_commands): Same as above.
	(parse_argbuf): Same as above.
	(execute): Refactor based on new functions.

Comments

Richard Biener May 20, 2020, 8:30 a.m. UTC | #1
On Tue, May 19, 2020 at 2:24 AM Giuliano Belinassi via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> Hi,
>
> After having so much trouble working on the `execute' function inside
> gcc.c, I decided to refactor it so that it could be more digestible.
> Since I am using it on my branch, I am submitting this patch for
> "battlefield" testing.
>
> Bootstrapped and ran the testsuite on Linux x86_64.

The ChangeLog misses the new print_verbose function (the diff is also
incredibly hard to read there - I guess a context diff might be easier.
Also there's append_valgrind, so the changelog needs some work.

I wonder how print_verbose can fail?  It also seems to have an odd
name given it contains

  if (verbose_only_flag != 0)
    {
      /* verbose_only_flag should act as if the spec was
         executed, so increment execution_count before
         returning.  This prevents spurious warnings about
         unused linker input files, etc.  */
      execution_count++;
      return 1;
    }

maybe the (this?) verbose_only_flag processing should be in the caller.

I wonder if splitting up the patch helps review.  Note I'm not very familiar
with the code either (now you clearly beat me here!).  Joseph is listed
as official maintainer (CCing him), he might be able to give advice.
As a short summary if you didn't follow this GSoC project there's
need for the driver to process multiple assembler outputs from
a single compiler invocation, assembling them and (partially)
linking the result (based on the requested output).  We're trying to get
away without adding anything to the specs language here.

Richard.

> -----
>
> Refactor `execute' function to avoid exposing unnecessary details of
> how it works, encapsulating each important step into a function.
>
> gcc/ChangeLog
> 2020-05-18  Giuliano Belinassi  <giuliano.belinassi@usp.brr>
>
>         * gcc.c (struct command): Move from execute.
>         (await_commands_to_finish): New function.
>         (split_commands): Same as above.
>         (parse_argbuf): Same as above.
>         (execute): Refactor based on new functions.
diff mbox series

Patch

From df092d93b511a28180105a2db40777fb977e23da Mon Sep 17 00:00:00 2001
From: Giuliano Belinassi <giuliano.belinassi@usp.br>
Date: Mon, 18 May 2020 21:08:43 -0300
Subject: [PATCH] Refactor execute in gcc.c

Refactor `execute' function to avoid exposing unnecessary details of
how it works, encapsulating each important step into a function.

gcc/ChangeLog
2020-05-18  Giuliano Belinassi  <giuliano.belinassi@usp.brr>

	* gcc.c (struct command): Move from execute.
	(await_commands_to_finish): New function.
	(split_commands): Same as above.
	(parse_argbuf): Same as above.
	(execute): Refactor based on new functions.
---
 gcc/ChangeLog |   8 +
 gcc/gcc.c     | 595 +++++++++++++++++++++++++++++---------------------
 2 files changed, 353 insertions(+), 250 deletions(-)

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 7f484da04f0..3ad1b9f7fb9 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,11 @@ 
+2020-05-18  Giuliano Belinassi  <giuliano.belinassi@usp.brr>
+
+	* gcc.c (struct command): Move from execute.
+	(await_commands_to_finish): New function.
+	(split_commands): Same as above.
+	(parse_argbuf): Same as above.
+	(execute): Refactor based on new functions.
+
 2020-05-18  Jason Merrill  <jason@redhat.com>
 
 	* aclocal.m4: Add ax_cxx_compile_stdcxx.m4.
diff --git a/gcc/gcc.c b/gcc/gcc.c
index b0d0308f127..082a5d1d4a8 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -3019,157 +3019,105 @@  add_sysrooted_hdrs_prefix (struct path_prefix *pprefix, const char *prefix,
 }
 
 
-/* Execute the command specified by the arguments on the current line of spec.
-   When using pipes, this includes several piped-together commands
-   with `|' between them.
 
-   Return 0 if successful, -1 if failed.  */
+struct command
+{
+  const char *prog;		/* program name.  */
+  const char **argv;		/* vector of args.  */
+};
+
+/* Print what commands will run.  Return 0 if success, anything else on
+   error.  */
 
 static int
-execute (void)
+print_verbose (int n_commands, struct command commands[])
 {
   int i;
-  int n_commands;		/* # of command.  */
-  char *string;
-  struct pex_obj *pex;
-  struct command
-  {
-    const char *prog;		/* program name.  */
-    const char **argv;		/* vector of args.  */
-  };
-  const char *arg;
-
-  struct command *commands;	/* each command buffer with above info.  */
-
-  gcc_assert (!processing_spec_function);
-
-  if (wrapper_string)
-    {
-      string = find_a_file (&exec_prefixes,
-			    argbuf[0], X_OK, false);
-      if (string)
-	argbuf[0] = string;
-      insert_wrapper (wrapper_string);
-    }
 
-  /* Count # of piped commands.  */
-  for (n_commands = 1, i = 0; argbuf.iterate (i, &arg); i++)
-    if (strcmp (arg, "|") == 0)
-      n_commands++;
-
-  /* Get storage for each command.  */
-  commands = (struct command *) alloca (n_commands * sizeof (struct command));
-
-  /* Split argbuf into its separate piped processes,
-     and record info about each one.
-     Also search for the programs that are to be run.  */
-
-  argbuf.safe_push (0);
-
-  commands[0].prog = argbuf[0]; /* first command.  */
-  commands[0].argv = argbuf.address ();
-
-  if (!wrapper_string)
-    {
-      string = find_a_file (&exec_prefixes, commands[0].prog, X_OK, false);
-      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");
-#endif
-	argbuf[i] = 0; /* Termination of command args.  */
-	commands[n_commands].prog = argbuf[i + 1];
-	commands[n_commands].argv
-	  = &(argbuf.address ())[i + 1];
-	string = find_a_file (&exec_prefixes, commands[n_commands].prog,
-			      X_OK, false);
-	if (string)
-	  commands[n_commands].argv[0] = string;
-	n_commands++;
-      }
-
-  /* If -v, print what we are about to do, and maybe query.  */
+  /* For help listings, put a blank line between sub-processes.  */
+  if (print_help_list)
+    fputc ('\n', stderr);
 
-  if (verbose_flag)
+  /* Print each piped command as a separate line.  */
+  for (i = 0; i < n_commands; i++)
     {
-      /* For help listings, put a blank line between sub-processes.  */
-      if (print_help_list)
-	fputc ('\n', stderr);
+      const char *const *j;
 
-      /* Print each piped command as a separate line.  */
-      for (i = 0; i < n_commands; i++)
+      if (verbose_only_flag)
 	{
-	  const char *const *j;
-
-	  if (verbose_only_flag)
+	  for (j = commands[i].argv; *j; j++)
 	    {
-	      for (j = commands[i].argv; *j; j++)
+	      const char *p;
+	      for (p = *j; *p; ++p)
+		if (!ISALNUM ((unsigned char) *p)
+		    && *p != '_' && *p != '/' && *p != '-' && *p != '.')
+		  break;
+	      if (*p || !*j)
 		{
-		  const char *p;
+		  fprintf (stderr, " \"");
 		  for (p = *j; *p; ++p)
-		    if (!ISALNUM ((unsigned char) *p)
-			&& *p != '_' && *p != '/' && *p != '-' && *p != '.')
-		      break;
-		  if (*p || !*j)
 		    {
-		      fprintf (stderr, " \"");
-		      for (p = *j; *p; ++p)
-			{
-			  if (*p == '"' || *p == '\\' || *p == '$')
-			    fputc ('\\', stderr);
-			  fputc (*p, stderr);
-			}
-		      fputc ('"', stderr);
+		      if (*p == '"' || *p == '\\' || *p == '$')
+			fputc ('\\', stderr);
+		      fputc (*p, stderr);
 		    }
-		  /* If it's empty, print "".  */
-		  else if (!**j)
-		    fprintf (stderr, " \"\"");
-		  else
-		    fprintf (stderr, " %s", *j);
+		  fputc ('"', stderr);
 		}
-	    }
-	  else
-	    for (j = commands[i].argv; *j; j++)
 	      /* If it's empty, print "".  */
-	      if (!**j)
+	      else if (!**j)
 		fprintf (stderr, " \"\"");
 	      else
 		fprintf (stderr, " %s", *j);
-
-	  /* Print a pipe symbol after all but the last command.  */
-	  if (i + 1 != n_commands)
-	    fprintf (stderr, " |");
-	  fprintf (stderr, "\n");
+	    }
 	}
-      fflush (stderr);
-      if (verbose_only_flag != 0)
-        {
-	  /* verbose_only_flag should act as if the spec was
-	     executed, so increment execution_count before
-	     returning.  This prevents spurious warnings about
-	     unused linker input files, etc.  */
-	  execution_count++;
-	  return 0;
-        }
+      else
+	for (j = commands[i].argv; *j; j++)
+	  /* If it's empty, print "".  */
+	  if (!**j)
+	    fprintf (stderr, " \"\"");
+	  else
+	    fprintf (stderr, " %s", *j);
+
+      /* Print a pipe symbol after all but the last command.  */
+      if (i + 1 != n_commands)
+	fprintf (stderr, " |");
+      fprintf (stderr, "\n");
+    }
+  fflush (stderr);
+  if (verbose_only_flag != 0)
+    {
+      /* verbose_only_flag should act as if the spec was
+	 executed, so increment execution_count before
+	 returning.  This prevents spurious warnings about
+	 unused linker input files, etc.  */
+      execution_count++;
+      return 1;
+    }
 #ifdef DEBUG
-      fnotice (stderr, "\nGo ahead? (y or n) ");
-      fflush (stderr);
-      i = getchar ();
-      if (i != '\n')
-	while (getchar () != '\n')
-	  ;
-
-      if (i != 'y' && i != 'Y')
-	return 0;
+  fnotice (stderr, "\nGo ahead? (y or n) ");
+  fflush (stderr);
+  i = getchar ();
+  if (i != '\n')
+    while (getchar () != '\n')
+      ;
+
+  if (i != 'y' && i != 'Y')
+    return 1;
 #endif /* DEBUG */
-    }
+
+  return 0;
+}
 
 #ifdef ENABLE_VALGRIND_CHECKING
+
+/* Append valgrind to each program.  */
+
+static void
+append_valgrind (struct obstack *to_be_released,
+		 int n_commands, struct command commands[])
+{
+  int i;
+
   /* Run the each command through valgrind.  To simplify prepending the
      path to valgrind and the option "-q" (for quiet operation unless
      something triggers), we allocate a separate argv array.  */
@@ -3183,7 +3131,7 @@  execute (void)
       for (argc = 0; commands[i].argv[argc] != NULL; argc++)
 	;
 
-      argv = XALLOCAVEC (const char *, argc + 3);
+      argv = obstack_alloc (to_be_released, (argc + 3) * sizeof (const char *));
 
       argv[0] = VALGRIND_PATH;
       argv[1] = "-q";
@@ -3194,15 +3142,16 @@  execute (void)
       commands[i].argv = argv;
       commands[i].prog = argv[0];
     }
+}
 #endif
 
-  /* Run each piped subprocess.  */
+/* Launch a list of commands asynchronously.  */
 
-  pex = pex_init (PEX_USE_PIPES | ((report_times || report_times_to_file)
-				   ? PEX_RECORD_TIMES : 0),
-		  progname, temp_filename);
-  if (pex == NULL)
-    fatal_error (input_location, "%<pex_init%> failed: %m");
+static void
+async_launch_commands (struct pex_obj *pex,
+		       int n_commands, struct command commands[])
+{
+  int i;
 
   for (i = 0; i < n_commands; i++)
     {
@@ -3229,150 +3178,296 @@  execute (void)
     }
 
   execution_count++;
+}
 
-  /* Wait for all the subprocesses to finish.  */
 
-  {
-    int *statuses;
-    struct pex_time *times = NULL;
-    int ret_code = 0;
+/* Wait for all the subprocesses to finish.  Return 0 on success, -1 on
+   failure.  */
 
-    statuses = (int *) alloca (n_commands * sizeof (int));
-    if (!pex_get_status (pex, n_commands, statuses))
-      fatal_error (input_location, "failed to get exit status: %m");
+static int
+await_commands_to_finish (struct pex_obj *pex,
+			  int n_commands, struct command commands[])
+{
 
-    if (report_times || report_times_to_file)
-      {
-	times = (struct pex_time *) alloca (n_commands * sizeof (struct pex_time));
-	if (!pex_get_times (pex, n_commands, times))
-	  fatal_error (input_location, "failed to get process times: %m");
-      }
+  int *statuses;
+  struct pex_time *times = NULL;
+  int ret_code = 0, i;
 
-    pex_free (pex);
+  statuses = (int *) alloca (n_commands * sizeof (int));
+  if (!pex_get_status (pex, n_commands, statuses))
+    fatal_error (input_location, "failed to get exit status: %m");
 
-    for (i = 0; i < n_commands; ++i)
-      {
-	int status = statuses[i];
+  if (report_times || report_times_to_file)
+    {
+      times = (struct pex_time *) alloca (n_commands * sizeof (*times));
+      if (!pex_get_times (pex, n_commands, times))
+	fatal_error (input_location, "failed to get process times: %m");
+    }
 
-	if (WIFSIGNALED (status))
-	  switch (WTERMSIG (status))
-	    {
-	    case SIGINT:
-	    case SIGTERM:
-	      /* SIGQUIT and SIGKILL are not available on MinGW.  */
+  for (i = 0; i < n_commands; ++i)
+    {
+      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:
+	  case SIGQUIT:
 #endif
 #ifdef SIGKILL
-	    case 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;
+	    /* 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
-	    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;
-		}
+	  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
-	      /* FALLTHROUGH */
+	    /* 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)
-	  {
-	    /* For ICEs in cc1, cc1obj, cc1plus see if it is
-	       reproducible or not.  */
-	    const char *p;
-	    if (flag_report_bug
-		&& WEXITSTATUS (status) == ICE_EXIT_CODE
-		&& i == 0
-		&& (p = strrchr (commands[0].argv[0], DIR_SEPARATOR))
-		&& ! strncmp (p + 1, "cc1", 3))
-	      try_generate_repro (commands[0].argv);
-	    if (WEXITSTATUS (status) > greatest_status)
-	      greatest_status = WEXITSTATUS (status);
-	    ret_code = -1;
+	  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)
+	{
+	  /* For ICEs in cc1, cc1obj, cc1plus see if it is
+	     reproducible or not.  */
+	  const char *p;
+	  if (flag_report_bug
+	      && WEXITSTATUS (status) == ICE_EXIT_CODE
+	      && i == 0
+	      && (p = strrchr (commands[0].argv[0], DIR_SEPARATOR))
+	      && ! strncmp (p + 1, "cc1", 3))
+	    try_generate_repro (commands[0].argv);
+	  if (WEXITSTATUS (status) > greatest_status)
+	    greatest_status = WEXITSTATUS (status);
+	  ret_code = -1;
+	}
 
-	if (report_times || report_times_to_file)
-	  {
-	    struct pex_time *pt = &times[i];
-	    double ut, st;
+      if (report_times || report_times_to_file)
+	{
+	  struct pex_time *pt = &times[i];
+	  double ut, st;
 
-	    ut = ((double) pt->user_seconds
-		  + (double) pt->user_microseconds / 1.0e6);
-	    st = ((double) pt->system_seconds
-		  + (double) pt->system_microseconds / 1.0e6);
+	  ut = ((double) pt->user_seconds
+		+ (double) pt->user_microseconds / 1.0e6);
+	  st = ((double) pt->system_seconds
+		+ (double) pt->system_microseconds / 1.0e6);
 
-	    if (ut + st != 0)
-	      {
-		if (report_times)
-		  fnotice (stderr, "# %s %.2f %.2f\n",
-			   commands[i].prog, ut, st);
+	  if (ut + st != 0)
+	    {
+	      if (report_times)
+		fnotice (stderr, "# %s %.2f %.2f\n",
+			 commands[i].prog, ut, st);
 
-		if (report_times_to_file)
-		  {
-		    int c = 0;
-		    const char *const *j;
+	      if (report_times_to_file)
+		{
+		  int c = 0;
+		  const char *const *j;
 
-		    fprintf (report_times_to_file, "%g %g", ut, st);
+		  fprintf (report_times_to_file, "%g %g", ut, st);
 
-		    for (j = &commands[i].prog; *j; j = &commands[i].argv[++c])
-		      {
-			const char *p;
-			for (p = *j; *p; ++p)
-			  if (*p == '"' || *p == '\\' || *p == '$'
-			      || ISSPACE (*p))
-			    break;
+		  for (j = &commands[i].prog; *j; j = &commands[i].argv[++c])
+		    {
+		      const char *p;
+		      for (p = *j; *p; ++p)
+			if (*p == '"' || *p == '\\' || *p == '$'
+			    || ISSPACE (*p))
+			  break;
 
-			if (*p)
-			  {
-			    fprintf (report_times_to_file, " \"");
-			    for (p = *j; *p; ++p)
-			      {
-				if (*p == '"' || *p == '\\' || *p == '$')
-				  fputc ('\\', report_times_to_file);
-				fputc (*p, report_times_to_file);
-			      }
-			    fputc ('"', report_times_to_file);
-			  }
-			else
-			  fprintf (report_times_to_file, " %s", *j);
-		      }
+		      if (*p)
+			{
+			  fprintf (report_times_to_file, " \"");
+			  for (p = *j; *p; ++p)
+			    {
+			      if (*p == '"' || *p == '\\' || *p == '$')
+				fputc ('\\', report_times_to_file);
+			      fputc (*p, report_times_to_file);
+			    }
+			  fputc ('"', report_times_to_file);
+			}
+		      else
+			fprintf (report_times_to_file, " %s", *j);
+		    }
 
-		    fputc ('\n', report_times_to_file);
-		  }
-	      }
-	  }
+		  fputc ('\n', report_times_to_file);
+		}
+	    }
+	}
+    }
+
+  return ret_code;
+}
+
+/* Split a single command with pipes into several commands.  */
+
+static void
+split_commands (vec<const_char_p> *argbuf_p,
+		int n_commands, struct command commands[])
+{
+  int i;
+  const char *arg;
+  vec<const_char_p> &argbuf = *argbuf_p;
+
+  for (n_commands = 1, i = 0; argbuf.iterate (i, &arg); i++)
+    if (arg && strcmp (arg, "|") == 0)
+      {				/* each command.  */
+	const char *string;
+#if defined (__MSDOS__) || defined (OS2) || defined (VMS)
+	fatal_error (input_location, "%<-pipe%> not supported");
+#endif
+	argbuf[i] = 0; /* Termination of command args.  */
+	commands[n_commands].prog = argbuf[i + 1];
+	commands[n_commands].argv
+	  = &(argbuf.address ())[i + 1];
+	string = find_a_file (&exec_prefixes, commands[n_commands].prog,
+			      X_OK, false);
+	if (string)
+	  commands[n_commands].argv[0] = string;
+	n_commands++;
       }
+}
 
-   if (commands[0].argv[0] != commands[0].prog)
-     free (CONST_CAST (char *, commands[0].argv[0]));
+struct command
+*parse_argbuf (vec <const_char_p> *argbuf_p, int *n)
+{
+  int i, n_commands;
+  vec<const_char_p> &argbuf = *argbuf_p;
+  const char *arg;
+  struct command *commands;
 
-    return ret_code;
-  }
+  /* Count # of piped commands.  */
+  for (n_commands = 1, i = 0; argbuf.iterate (i, &arg); i++)
+    if (strcmp (arg, "|") == 0)
+      n_commands++;
+
+  /* Get storage for each command.  */
+  commands = XNEWVEC (struct command, n_commands);
+
+  /* Split argbuf into its separate piped processes,
+     and record info about each one.
+     Also search for the programs that are to be run.  */
+
+  argbuf.safe_push (0);
+
+  commands[0].prog = argbuf[0]; /* first command.  */
+  commands[0].argv = argbuf.address ();
+
+  split_commands (argbuf_p, n_commands, commands);
+
+  *n = n_commands;
+  return commands;
+}
+
+/* Execute the command specified by the arguments on the current line of spec.
+   When using pipes, this includes several piped-together commands
+   with `|' between them.
+
+   Return 0 if successful, -1 if failed.  */
+
+static int
+execute (void)
+{
+  struct pex_obj *pex;
+  struct command *commands;	 /* each command buffer with program to call
+				    and arguments.  */
+  int n_commands;		 /* # of command.  */
+  int ret = 0;
+
+  gcc_assert (!processing_spec_function);
+
+  if (wrapper_string)
+    {
+      char *string = find_a_file (&exec_prefixes, argbuf[0], X_OK, false);
+      if (string)
+	argbuf[0] = string;
+      insert_wrapper (wrapper_string);
+    }
+
+  /* Parse the argbuf into several commands.  */
+  commands = parse_argbuf (&argbuf, &n_commands);
+
+  if (!wrapper_string)
+    {
+      char *string = find_a_file (&exec_prefixes, commands[0].prog,
+				  X_OK, false);
+      if (string)
+	commands[0].argv[0] = string;
+    }
+
+  /* If -v, print what we are about to do, and maybe query.  */
+
+  if (verbose_flag)
+    {
+      int ret_verbose = print_verbose (n_commands, commands);
+      if (ret_verbose > 0)
+	{
+	  ret = 0;
+	  goto cleanup;
+	}
+    }
+
+#ifdef ENABLE_VALGRIND_CHECKING
+  /* Stack of strings to be released on function return.  */
+  struct obstack to_be_released;
+  obstack_init (&to_be_released);
+  append_valgrind (&to_be_released, n_commands, commands);
+#endif
+
+  /* Run each piped subprocess.  */
+
+  pex = pex_init (PEX_USE_PIPES | ((report_times || report_times_to_file)
+				   ? PEX_RECORD_TIMES : 0),
+		  progname, temp_filename);
+  if (pex == NULL)
+    fatal_error (input_location, "%<pex_init%> failed: %m");
+
+  /* Lauch the commands.  */
+  async_launch_commands (pex, n_commands, commands);
+
+  /* Await them to be done.  */
+  ret = await_commands_to_finish (pex, n_commands, commands);
+
+  /* Cleanup.  */
+  pex_free (pex);
+
+#ifdef ENABLE_VALGRIND_CHECKING
+  obstack_free (&to_be_released, NULL);
+#endif
+
+cleanup:
+  if (commands[0].argv[0] != commands[0].prog)
+    free (CONST_CAST (char *, commands[0].argv[0]));
+
+  free (commands);
+
+  return ret;
 }
 
 /* Find all the switches given to us
-- 
2.26.2