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 >