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

Andrea Pinski <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <CALvbMcBdf5uXHWmvrupcR8FqF1v+ThZkRuHn5ThMApp4QmWdXg@mail.gmail.com>
On Mon, Aug 10, 2026 at 12:04 AM John Ericson <[email protected]> wrote:
>
> 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;

I am not a fan of this at all.  Is there a better way? Like requiring
the callback function returning `const char*` or `char*`?

> +
>    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*

I Know this was not done before but can we have an explicit list of captures?

>      {
>        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*

List out the captures so it is more obvious why you need to capture by
reference here.

> +    {
> +      /* 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.