Merge topic 'realpath-windows'

0a5efe8489 cmSystemTools: Fix GetRealPath implementation on Windows
5910bf0b40 cmSystemTools: Restore GetRealPathResolvingWindowsSubst

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: buildbot <buildbot@kitware.com>
Merge-request: !10452
This commit is contained in:
Brad King 2025-03-11 13:42:09 +00:00 committed by Kitware Robot
commit 27ee7ed289
3 changed files with 73 additions and 12 deletions

View File

@ -1305,23 +1305,77 @@ bool FileModeGuard::HasErrors() const
return filepath_.empty(); return filepath_.empty();
} }
std::string cmSystemTools::GetRealPathResolvingWindowsSubst(
std::string const& path, std::string* errorMessage)
{
#ifdef _WIN32
// uv_fs_realpath uses Windows Vista API so fallback to kwsys if not found
std::string resolved_path;
uv_fs_t req;
int err = uv_fs_realpath(nullptr, &req, path.c_str(), nullptr);
if (!err) {
resolved_path = std::string((char*)req.ptr);
cmSystemTools::ConvertToUnixSlashes(resolved_path);
} else if (err == UV_ENOSYS) {
resolved_path = cmsys::SystemTools::GetRealPath(path, errorMessage);
} else if (errorMessage) {
cmsys::Status status =
cmsys::Status::Windows(uv_fs_get_system_error(&req));
*errorMessage = status.GetString();
resolved_path.clear();
} else {
resolved_path = path;
}
// Normalize to upper-case drive letter as cm::PathResolver does.
if (resolved_path.size() > 1 && resolved_path[1] == ':') {
resolved_path[0] = toupper(resolved_path[0]);
}
return resolved_path;
#else
return cmsys::SystemTools::GetRealPath(path, errorMessage);
#endif
}
std::string cmSystemTools::GetRealPath(std::string const& path, std::string cmSystemTools::GetRealPath(std::string const& path,
std::string* errorMessage) std::string* errorMessage)
{ {
#ifdef _WIN32 #ifdef _WIN32
std::string resolved_path; std::string resolved_path =
using namespace cm::PathResolver; cmSystemTools::GetRealPathResolvingWindowsSubst(path, errorMessage);
// IWYU pragma: no_forward_declare cm::PathResolver::Policies::RealPath
static Resolver<Policies::RealPath> const resolver(RealOS); // If the original path used a subst drive and the real path starts
cmsys::Status status = resolver.Resolve(path, resolved_path); // with the substitution, restore the subst drive prefix. This may
if (!status) { // incorrectly restore a subst drive if the underlying drive was
if (errorMessage) { // encountered via an absolute symlink, but this is an acceptable
*errorMessage = status.GetString(); // limitation to otherwise preserve susbt drives.
resolved_path.clear(); if (resolved_path.size() >= 2 && resolved_path[1] == ':' &&
} else { path.size() >= 2 && path[1] == ':' &&
resolved_path = path; toupper(resolved_path[0]) != toupper(path[0])) {
// FIXME: Add thread_local or mutex if we use threads.
static std::map<char, std::string> substMap;
char const drive = static_cast<char>(toupper(path[0]));
std::string maybe_subst = cmStrCat(drive, ":/");
auto smi = substMap.find(drive);
if (smi == substMap.end()) {
smi = substMap
.emplace(
drive,
cmSystemTools::GetRealPathResolvingWindowsSubst(maybe_subst))
.first;
}
std::string const& resolved_subst = smi->second;
std::string::size_type const ns = resolved_subst.size();
if (ns > 0) {
std::string::size_type const np = resolved_path.size();
if (ns == np && resolved_path == resolved_subst) {
resolved_path = maybe_subst;
} else if (ns > 0 && ns < np && resolved_path[ns] == '/' &&
resolved_path.compare(0, ns, resolved_subst) == 0) {
resolved_path.replace(0, ns + 1, maybe_subst);
}
} }
} }
return resolved_path; return resolved_path;
#else #else
return cmsys::SystemTools::GetRealPath(path, errorMessage); return cmsys::SystemTools::GetRealPath(path, errorMessage);

View File

@ -651,6 +651,12 @@ public:
static std::string GetComspec(); static std::string GetComspec();
#endif #endif
/** Get the real path for a given path, removing all symlinks.
This variant of GetRealPath also works on Windows but will
resolve subst drives too. */
static std::string GetRealPathResolvingWindowsSubst(
std::string const& path, std::string* errorMessage = nullptr);
/** Get the real path for a given path, removing all symlinks. */ /** Get the real path for a given path, removing all symlinks. */
static std::string GetRealPath(std::string const& path, static std::string GetRealPath(std::string const& path,
std::string* errorMessage = nullptr); std::string* errorMessage = nullptr);

View File

@ -63,7 +63,8 @@ std::string cmTimestamp::FileModificationTime(char const* path,
std::string const& formatString, std::string const& formatString,
bool utcFlag) const bool utcFlag) const
{ {
std::string real_path = cmSystemTools::GetRealPath(path); std::string real_path =
cmSystemTools::GetRealPathResolvingWindowsSubst(path);
if (!cmsys::SystemTools::FileExists(real_path)) { if (!cmsys::SystemTools::FileExists(real_path)) {
return std::string(); return std::string();