cmake: Preserve symlinks in references to itself when possible

CMake's tools search relative to their own locations to find their
resources.  Previously they always started at their own `realpath()`.
The intention was to support invocation through symbolic links to the
binaries where the resources can only be found under the real prefix:

    /logical/prefix/bin/cmake   -> /real/prefix/bin/cmake
                                   /real/prefix/share/cmake

The generated build system refers to CMake's own tools and resources
through the `/real/prefix`.  This is not desirable in the case that the
`/logical/prefix` is meant as the canonical location and uses symbolic
links for both tools and resources as an implementation detail:

    /logical/prefix/bin/cmake   -> /real/prefix/bin/cmake
    /logical/prefix/share/cmake -> /real/prefix/share/cmake

In this case, the generated build system should refer to CMake's own
tools and resources through the `/logical/prefix`.  This way the
`/real/prefix` can be changed, and the symbolic links updated,
without breaking already-generated build systems.

Fixes: #19849
Fixes: #21059
Inspired-by: Carlo Cabrera <github@carlo.cab>
Inspired-by: Rodger Combs <rodger.combs@gmail.com>
This commit is contained in:
Brad King
2024-11-11 08:29:35 -05:00
parent 0925abb09b
commit 0994bc7929

View File

@@ -2695,7 +2695,6 @@ std::string FindOwnExecutable(const char* argv0)
wchar_t modulepath[_MAX_PATH];
::GetModuleFileNameW(nullptr, modulepath, sizeof(modulepath));
std::string exe = cmsys::Encoding::ToNarrow(modulepath);
exe = cmSystemTools::GetRealPath(exe);
#elif defined(__APPLE__)
static_cast<void>(argv0);
# define CM_EXE_PATH_LOCAL_SIZE 16384
@@ -2715,7 +2714,6 @@ std::string FindOwnExecutable(const char* argv0)
if (exe_path != exe_path_local) {
free(exe_path);
}
exe = cmSystemTools::GetRealPath(exe);
if (IsCMakeAppBundleExe(exe)) {
// The executable is inside an application bundle.
// The install tree has "..<CMAKE_BIN_DIR>/cmake-gui".
@@ -2735,12 +2733,34 @@ std::string FindOwnExecutable(const char* argv0)
if (!cmSystemTools::FindProgramPath(argv0, exe, errorMsg)) {
// ???
}
exe = cmSystemTools::GetRealPath(exe);
#endif
exe = cmSystemTools::ToNormalizedPathOnDisk(std::move(exe));
return exe;
}
#ifndef CMAKE_BOOTSTRAP
bool ResolveSymlinkToOwnExecutable(std::string& exe, std::string& exe_dir)
{
std::string linked_exe;
if (!cmSystemTools::ReadSymlink(exe, linked_exe)) {
return false;
}
# if defined(__APPLE__)
// Ignore "cmake-gui -> ../MacOS/CMake".
if (IsCMakeAppBundleExe(linked_exe)) {
return false;
}
# endif
if (cmSystemTools::FileIsFullPath(linked_exe)) {
exe = std::move(linked_exe);
} else {
exe = cmStrCat(exe_dir, '/', std::move(linked_exe));
}
exe = cmSystemTools::ToNormalizedPathOnDisk(std::move(exe));
exe_dir = cmSystemTools::GetFilenamePath(exe);
return true;
}
bool FindCMakeResourcesInInstallTree(std::string const& exe_dir)
{
// Install tree has
@@ -2808,7 +2828,11 @@ void cmSystemTools::FindCMakeResources(const char* argv0)
#else
// Find resources relative to our own executable.
std::string exe_dir = cmSystemTools::GetFilenamePath(exe);
if (!FindCMakeResourcesInInstallTree(exe_dir)) {
bool found = false;
do {
found = FindCMakeResourcesInInstallTree(exe_dir);
} while (!found && ResolveSymlinkToOwnExecutable(exe, exe_dir));
if (!found) {
FindCMakeResourcesInBuildTree(exe_dir);
}
cmSystemToolsCMakeCommand =