[PATCH 2/3] driver: Search PATH ourselves rather than by way of PEX_SEARCH

John Ericson <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
Resolving a program was split between two places.  find_a_program
searched the compiler's own directories; where it came up empty, execute
left the bare name in argv[0] and passed PEX_SEARCH, handing the rest of
the job to execvp.

Do that last step in find_a_program instead, walking PATH a directory at
a time, and stop passing PEX_SEARCH.

This is the first caller of for_each_env_path that wants to stop
before the end, so the helper gains the same protocol for_each_path
already uses: the callback returns null to carry on, or the answer to
stop there.  The existing callers visit every directory and simply
return null.

This is not new ground for GCC: collect2 and gcc-ar have always folded
PATH into their own prefix lists and searched it directly, rather than
delegating to execvp.

Reimplementing a search the C library had been doing for us means we no
longer inherit its behaviour, and the full consequences of that are not
knowable from here: how execvp treats PATH differs between C libraries,
and the driver is built against whichever one the host happens to
provide.

What that buys is predictability.  The search is now GCC's own, so it is
the same search on every host, rather than whatever the local libc
implements.  It is also the same search the driver already performs for
its own directories, instead of one set of rules there and another once
PATH is reached.

It also makes -print-prog-name give a useful answer.  Until now, for a
program the driver could only reach through PATH, it printed the name
back unchanged -- 'ld' in, 'ld' out -- because the driver genuinely did
not know, having delegated the lookup to execvp.  A build system asking
the compiler where its linker is therefore learned nothing, and had to
go looking itself, possibly settling on a different file than the one
GCC would run.  Now it prints the file GCC will actually execute.

This also leaves resolution as one job in one function, which the next
commit needs: the search can then be taught about machine-prefixed names
in a single place.

One final note: an alternative implementation strategy would be to
simply add PATH to exec_prefixes.  The for_each_path helper would then
process it along with the other exec prefixes, and PATH would only be
parsed up front.  Both of these sound nice.  I did not do it because it
would introduce more user-visible behaviour changes: that list is
exported to subprocesses as COMPILER_PATH, and printed by
-print-search-dirs, so PATH would leak into both.  for_each_path also
probes each prefix with the machine and multilib suffixes, whereas PATH
means precisely the directories named.

gcc/ChangeLog:

	* gcc.cc (find_a_program): Search PATH once the exec prefixes are
	exhausted.
	(execute): Do not pass PEX_SEARCH.
	(for_each_env_path): Return the callback's value, stopping at
	the first non-null one.
	(process_command): Adjust callbacks accordingly.
	* doc/invoke.texi (Developer Options): Document that
	-print-prog-name searches PATH.

Signed-off-by: John Ericson <[email protected]>
---
 gcc/doc/invoke.texi |  2 ++
 gcc/gcc.cc          | 60 ++++++++++++++++++++++++++++++++++++---------
 2 files changed, 51 insertions(+), 11 deletions(-)

diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 7c65c924271..43469bf3d5e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -21230,6 +21230,8 @@ relative to some @file{lib} subdirectory.
 @itemx --print-prog-name=@var{program}
 @itemx --print-prog-name @var{program}
 Like @option{-print-file-name}, but searches for a program such as @command{cpp}.
+The compiler's own directories are searched first, then @env{PATH}.  If no
+such program is found, @var{program} is printed back unchanged.
 
 @opindex print-libgcc-file-name
 @item -print-libgcc-file-name
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index 08a192ade76..c494ddd0665 100644
--- a/gcc/gcc.cc
+++ b/gcc/gcc.cc
@@ -2990,14 +2990,19 @@ for_each_path (const struct path_prefix *paths,
    An empty element denotes the current directory.  Each directory is passed
    with a trailing directory separator, as add_prefix expects.  The buffer
    handed to CALLBACK is reused between iterations, so CALLBACK must copy
-   anything it wants to keep -- add_prefix does.  */
+   anything it wants to keep -- add_prefix does.
+
+   As with for_each_path, CALLBACK returns null to carry on to the next
+   directory, and anything else to stop there.  That value is returned.  */
 
 template<typename fun>
-static void
+static auto *
 for_each_env_path (const char *value, fun callback)
 {
+  decltype (callback (nullptr)) ret = nullptr;
+
   if (!value)
-    return;
+    return ret;
 
   char *nstore = (char *) alloca (strlen (value) + 3);
 
@@ -3020,12 +3025,16 @@ for_each_env_path (const char *value, fun callback)
 	    nstore[endp - startp] = 0;
 	}
 
-      callback (nstore);
+      ret = callback (nstore);
+      if (ret)
+	break;
 
       if (*endp == 0)
 	break;
       startp = endp + 1;
     }
+
+  return ret;
 }
 
 /* Add or change the value of an environment variable, outputting the
@@ -3186,9 +3195,9 @@ find_a_program (const char *name)
   /* Callback appends the file name to the directory path.  If the
      resulting file exists in the right mode, return the full pathname
      to the file.  */
-  return for_each_path (&exec_prefixes, false,
-			prefix_len + name_len + suffix_len,
-			[=](char *path, bool machine_specific) -> char*
+  char *ret = for_each_path (&exec_prefixes, false,
+			     prefix_len + name_len + suffix_len,
+			     [=](char *path, bool machine_specific) -> char*
     {
       size_t path_len = strlen (path);
 
@@ -3227,6 +3236,34 @@ find_a_program (const char *name)
 
       return search(path_len);
     });
+
+  if (ret)
+    return ret;
+
+  /* Nothing among our own directories, so fall back to PATH.
+
+     Searching it here rather than via execvp means we know which file
+     we picked, so -print-prog-name can report it.  */
+
+  return for_each_env_path (env.get ("PATH"), [&] (const char *dir) -> char*
+    {
+      /* Some systems have a suffix for executable files.  As above, try
+	 appending that first.  */
+      if (suffix_len)
+	{
+	  char *candidate = concat (dir, name, suffix, NULL);
+	  if (access_check (candidate, X_OK) == 0)
+	    return candidate;
+	  free (candidate);
+	}
+
+      char *candidate = concat (dir, name, NULL);
+      if (access_check (candidate, X_OK) == 0)
+	return candidate;
+
+      free (candidate);
+      return NULL;
+    });
 }
 
 /* Ranking of prefixes in the sort list. -B prefixes are put before
@@ -3548,8 +3585,7 @@ execute (void)
       const char *string = commands[i].argv[0];
 
       errmsg = pex_run (pex,
-			((i + 1 == n_commands ? PEX_LAST : 0)
-			 | (string == commands[i].prog ? PEX_SEARCH : 0)),
+			(i + 1 == n_commands ? PEX_LAST : 0),
 			string, const_cast<char **> (commands[i].argv),
 			NULL, NULL, &err);
       if (errmsg != NULL)
@@ -5007,18 +5043,20 @@ process_command (unsigned int decoded_options_count,
   /* COMPILER_PATH and LIBRARY_PATH have values
      that are lists of directory names with colons.  */
 
-  for_each_env_path (env.get ("COMPILER_PATH"), [] (const char *dir)
+  for_each_env_path (env.get ("COMPILER_PATH"), [] (const char *dir) -> void*
     {
       add_prefix (&exec_prefixes, dir, 0, PREFIX_PRIORITY_LAST, 0, 0);
       add_prefix (&include_prefixes, dir, 0, PREFIX_PRIORITY_LAST, 0, 0);
+      return NULL;
     });
 
   if (*cross_compile == '0')
     {
-      auto add_startfile_prefix = [] (const char *dir)
+      auto add_startfile_prefix = [] (const char *dir) -> void*
 	{
 	  add_prefix (&startfile_prefixes, dir, NULL,
 		      PREFIX_PRIORITY_LAST, 0, 1);
+	  return NULL;
 	};
 
       for_each_env_path (env.get (LIBRARY_PATH_ENV), add_startfile_prefix);
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.