Browse Source

Merge topic 'realpath-windows'

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

Acked-by: Kitware Robot <[email protected]>
Acked-by: buildbot <[email protected]>
Merge-request: !10452
Brad King 8 months ago
parent
commit
27ee7ed289
3 changed files with 73 additions and 12 deletions
  1. 65 11
      Source/cmSystemTools.cxx
  2. 6 0
      Source/cmSystemTools.h
  3. 2 1
      Source/cmTimestamp.cxx

+ 65 - 11
Source/cmSystemTools.cxx

@@ -1305,23 +1305,77 @@ bool FileModeGuard::HasErrors() const
   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* errorMessage)
 {
 #ifdef _WIN32
-  std::string resolved_path;
-  using namespace cm::PathResolver;
-  // IWYU pragma: no_forward_declare cm::PathResolver::Policies::RealPath
-  static Resolver<Policies::RealPath> const resolver(RealOS);
-  cmsys::Status status = resolver.Resolve(path, resolved_path);
-  if (!status) {
-    if (errorMessage) {
-      *errorMessage = status.GetString();
-      resolved_path.clear();
-    } else {
-      resolved_path = path;
+  std::string resolved_path =
+    cmSystemTools::GetRealPathResolvingWindowsSubst(path, errorMessage);
+
+  // If the original path used a subst drive and the real path starts
+  // with the substitution, restore the subst drive prefix.  This may
+  // incorrectly restore a subst drive if the underlying drive was
+  // encountered via an absolute symlink, but this is an acceptable
+  // limitation to otherwise preserve susbt drives.
+  if (resolved_path.size() >= 2 && resolved_path[1] == ':' &&
+      path.size() >= 2 && path[1] == ':' &&
+      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;
 #else
   return cmsys::SystemTools::GetRealPath(path, errorMessage);

+ 6 - 0
Source/cmSystemTools.h

@@ -651,6 +651,12 @@ public:
   static std::string GetComspec();
 #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.  */
   static std::string GetRealPath(std::string const& path,
                                  std::string* errorMessage = nullptr);

+ 2 - 1
Source/cmTimestamp.cxx

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