Skip to content

Commit 9a81fcb

Browse files
bradkingkwrobot
authored andcommitted
Merge topic 'realpath-windows' into release-4.0
0a5efe8 cmSystemTools: Fix GetRealPath implementation on Windows 5910bf0 cmSystemTools: Restore GetRealPathResolvingWindowsSubst Acked-by: Kitware Robot <[email protected]> Acked-by: buildbot <[email protected]> Merge-request: !10452
2 parents dfbcaa9 + 0a5efe8 commit 9a81fcb

File tree

3 files changed

+73
-12
lines changed

3 files changed

+73
-12
lines changed

Source/cmSystemTools.cxx

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,23 +1305,77 @@ bool FileModeGuard::HasErrors() const
13051305
return filepath_.empty();
13061306
}
13071307

1308+
std::string cmSystemTools::GetRealPathResolvingWindowsSubst(
1309+
std::string const& path, std::string* errorMessage)
1310+
{
1311+
#ifdef _WIN32
1312+
// uv_fs_realpath uses Windows Vista API so fallback to kwsys if not found
1313+
std::string resolved_path;
1314+
uv_fs_t req;
1315+
int err = uv_fs_realpath(nullptr, &req, path.c_str(), nullptr);
1316+
if (!err) {
1317+
resolved_path = std::string((char*)req.ptr);
1318+
cmSystemTools::ConvertToUnixSlashes(resolved_path);
1319+
} else if (err == UV_ENOSYS) {
1320+
resolved_path = cmsys::SystemTools::GetRealPath(path, errorMessage);
1321+
} else if (errorMessage) {
1322+
cmsys::Status status =
1323+
cmsys::Status::Windows(uv_fs_get_system_error(&req));
1324+
*errorMessage = status.GetString();
1325+
resolved_path.clear();
1326+
} else {
1327+
resolved_path = path;
1328+
}
1329+
// Normalize to upper-case drive letter as cm::PathResolver does.
1330+
if (resolved_path.size() > 1 && resolved_path[1] == ':') {
1331+
resolved_path[0] = toupper(resolved_path[0]);
1332+
}
1333+
return resolved_path;
1334+
#else
1335+
return cmsys::SystemTools::GetRealPath(path, errorMessage);
1336+
#endif
1337+
}
1338+
13081339
std::string cmSystemTools::GetRealPath(std::string const& path,
13091340
std::string* errorMessage)
13101341
{
13111342
#ifdef _WIN32
1312-
std::string resolved_path;
1313-
using namespace cm::PathResolver;
1314-
// IWYU pragma: no_forward_declare cm::PathResolver::Policies::RealPath
1315-
static Resolver<Policies::RealPath> const resolver(RealOS);
1316-
cmsys::Status status = resolver.Resolve(path, resolved_path);
1317-
if (!status) {
1318-
if (errorMessage) {
1319-
*errorMessage = status.GetString();
1320-
resolved_path.clear();
1321-
} else {
1322-
resolved_path = path;
1343+
std::string resolved_path =
1344+
cmSystemTools::GetRealPathResolvingWindowsSubst(path, errorMessage);
1345+
1346+
// If the original path used a subst drive and the real path starts
1347+
// with the substitution, restore the subst drive prefix. This may
1348+
// incorrectly restore a subst drive if the underlying drive was
1349+
// encountered via an absolute symlink, but this is an acceptable
1350+
// limitation to otherwise preserve susbt drives.
1351+
if (resolved_path.size() >= 2 && resolved_path[1] == ':' &&
1352+
path.size() >= 2 && path[1] == ':' &&
1353+
toupper(resolved_path[0]) != toupper(path[0])) {
1354+
// FIXME: Add thread_local or mutex if we use threads.
1355+
static std::map<char, std::string> substMap;
1356+
char const drive = static_cast<char>(toupper(path[0]));
1357+
std::string maybe_subst = cmStrCat(drive, ":/");
1358+
auto smi = substMap.find(drive);
1359+
if (smi == substMap.end()) {
1360+
smi = substMap
1361+
.emplace(
1362+
drive,
1363+
cmSystemTools::GetRealPathResolvingWindowsSubst(maybe_subst))
1364+
.first;
1365+
}
1366+
std::string const& resolved_subst = smi->second;
1367+
std::string::size_type const ns = resolved_subst.size();
1368+
if (ns > 0) {
1369+
std::string::size_type const np = resolved_path.size();
1370+
if (ns == np && resolved_path == resolved_subst) {
1371+
resolved_path = maybe_subst;
1372+
} else if (ns > 0 && ns < np && resolved_path[ns] == '/' &&
1373+
resolved_path.compare(0, ns, resolved_subst) == 0) {
1374+
resolved_path.replace(0, ns + 1, maybe_subst);
1375+
}
13231376
}
13241377
}
1378+
13251379
return resolved_path;
13261380
#else
13271381
return cmsys::SystemTools::GetRealPath(path, errorMessage);

Source/cmSystemTools.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,12 @@ class cmSystemTools : public cmsys::SystemTools
651651
static std::string GetComspec();
652652
#endif
653653

654+
/** Get the real path for a given path, removing all symlinks.
655+
This variant of GetRealPath also works on Windows but will
656+
resolve subst drives too. */
657+
static std::string GetRealPathResolvingWindowsSubst(
658+
std::string const& path, std::string* errorMessage = nullptr);
659+
654660
/** Get the real path for a given path, removing all symlinks. */
655661
static std::string GetRealPath(std::string const& path,
656662
std::string* errorMessage = nullptr);

Source/cmTimestamp.cxx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ std::string cmTimestamp::FileModificationTime(char const* path,
6262
std::string const& formatString,
6363
bool utcFlag) const
6464
{
65-
std::string real_path = cmSystemTools::GetRealPath(path);
65+
std::string real_path =
66+
cmSystemTools::GetRealPathResolvingWindowsSubst(path);
6667

6768
if (!cmsys::SystemTools::FileExists(real_path)) {
6869
return std::string();

0 commit comments

Comments
 (0)