浏览代码

cmake: Look up on-disk case of input paths on macOS

Follow up commit 1a6015e5fc (PathResolver: Add helper to compute
normalized paths, 2024-10-30) to cover on-disk case lookup on macOS.

Fixes: #26333
YunQiang Su 11 月之前
父节点
当前提交
08040ced86
共有 4 个文件被更改,包括 117 次插入10 次删除
  1. 5 5
      Source/cmPathResolver.cxx
  2. 2 0
      Source/cmPathResolver.h
  3. 58 1
      Source/cmSystemTools.cxx
  4. 52 4
      Tests/CMakeLib/testPathResolver.cxx

+ 5 - 5
Source/cmPathResolver.cxx

@@ -396,7 +396,7 @@ Control Impl<Policy>::ResolveComponent(Root root,
     }
   }
 
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__APPLE__)
   bool exists = false;
   if (Policy::ActualCase == Options::ActualCase::Yes) {
     std::string name;
@@ -418,7 +418,7 @@ Control Impl<Policy>::ResolveComponent(Root root,
 #endif
 
   if (Policy::Existence == Options::Existence::Required
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__APPLE__)
       && !exists
 #endif
   ) {
@@ -486,7 +486,7 @@ cmsys::Status Impl<Policy>::Resolve(std::string in, std::string& out)
 namespace Policies {
 struct NaivePath
 {
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__APPLE__)
   static constexpr Options::ActualCase ActualCase = Options::ActualCase::No;
 #endif
   static constexpr Options::Symlinks Symlinks = Options::Symlinks::None;
@@ -494,7 +494,7 @@ struct NaivePath
 };
 struct RealPath
 {
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__APPLE__)
   static constexpr Options::ActualCase ActualCase = Options::ActualCase::Yes;
 #endif
   static constexpr Options::Symlinks Symlinks = Options::Symlinks::Eager;
@@ -502,7 +502,7 @@ struct RealPath
 };
 struct LogicalPath
 {
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__APPLE__)
   static constexpr Options::ActualCase ActualCase = Options::ActualCase::Yes;
 #endif
   static constexpr Options::Symlinks Symlinks = Options::Symlinks::Lazy;

+ 2 - 0
Source/cmPathResolver.h

@@ -64,7 +64,9 @@ public:
   /** Get the process's working directory on a Windows drive letter.
       This is a legacy DOS concept supported by 'cmd' shells.  */
   virtual std::string GetWorkingDirectoryOnDrive(char drive_letter) = 0;
+#endif
 
+#if defined(_WIN32) || defined(__APPLE__)
   /** Read the on-disk spelling of the last component of a file path.  */
   virtual cmsys::Status ReadName(std::string const& path,
                                  std::string& name) = 0;

+ 58 - 1
Source/cmSystemTools.cxx

@@ -22,7 +22,7 @@
 
 #include <iterator>
 
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__APPLE__)
 #  include <unordered_map>
 #endif
 
@@ -191,6 +191,61 @@ cmsys::Status ReadNameOnDisk(std::string const& path, std::string& name)
   CloseHandle(h);
   return cmsys::Status::Success();
 }
+#elif defined(__APPLE__)
+cmsys::Status ReadNameOnDiskIterateDir(std::string const& path,
+                                       std::string& name)
+{
+  // Read contents of the parent directory to find the
+  // entry matching the given path.
+  std::string const bn = cmSystemTools::GetFilenameName(path);
+  std::string const dn = cmSystemTools::GetFilenamePath(path);
+  DIR* d = opendir(dn.c_str());
+  while (struct dirent* dr = readdir(d)) {
+    if (strcasecmp(dr->d_name, bn.c_str()) == 0) {
+      name = dr->d_name;
+      closedir(d);
+      return cmsys::Status::Success();
+    }
+  }
+  closedir(d);
+  return cmsys::Status::POSIX(ENOENT);
+}
+
+cmsys::Status ReadNameOnDiskFcntlGetPath(std::string const& path,
+                                         std::string& name)
+{
+  // macOS (and *BSD) offer a syscall to get an on-disk path to
+  // a descriptor's file.
+  int fd = open(path.c_str(), O_SYMLINK | O_RDONLY);
+  if (fd == -1) {
+    return cmsys::Status::POSIX(errno);
+  }
+  char out[MAXPATHLEN + 1];
+  if (fcntl(fd, F_GETPATH, out) == -1) {
+    int e = errno;
+    close(fd);
+    return cmsys::Status::POSIX(e);
+  }
+  close(fd);
+  name = cmSystemTools::GetFilenameName(out);
+  return cmsys::Status::Success();
+}
+
+cmsys::Status ReadNameOnDisk(std::string const& path, std::string& name)
+{
+  struct stat stat_path;
+  if (lstat(path.c_str(), &stat_path) != 0) {
+    return cmsys::Status::POSIX(errno);
+  }
+  // macOS (and *BSD) use namei(9) to cache file paths.  Use it unless
+  // the inode has multiple hardlinks: if it is opened through multiple
+  // paths, the results may be unpredictable.
+  if (S_ISDIR(stat_path.st_mode) || stat_path.st_nlink < 2) {
+    return ReadNameOnDiskFcntlGetPath(path, name);
+  }
+  // Fall back to reading the parent directory.
+  return ReadNameOnDiskIterateDir(path, name);
+}
 #endif
 
 class RealSystem : public cm::PathResolver::System
@@ -215,7 +270,9 @@ public:
   {
     return GetDosDriveWorkingDirectory(letter);
   }
+#endif
 
+#if defined(_WIN32) || defined(__APPLE__)
   struct NameOnDisk
   {
     cmsys::Status Status;

+ 52 - 4
Tests/CMakeLib/testPathResolver.cxx

@@ -8,13 +8,15 @@
 #include <string>
 #include <utility>
 
+#ifdef _WIN32
+#  include <cctype>
+#endif
+
 #include <cmsys/Status.hxx>
 
 #include "cmPathResolver.h"
 
-#ifdef _WIN32
-#  include <cctype>
-
+#if defined(_WIN32) || defined(__APPLE__)
 #  include "cmSystemTools.h"
 #endif
 
@@ -46,7 +48,7 @@ public:
 
   static std::string AdjustCase(std::string const& path)
   {
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__APPLE__)
     return cmSystemTools::LowerCase(path);
 #else
     return path;
@@ -95,7 +97,9 @@ public:
     }
     return result;
   }
+#endif
 
+#if defined(_WIN32) || defined(__APPLE__)
   cmsys::Status ReadName(std::string const& path, std::string& name) override
   {
     auto i = this->Paths.find(AdjustCase(path));
@@ -242,6 +246,47 @@ bool posixSymlink()
   return true;
 }
 
+#ifdef __APPLE__
+bool macosActualCase()
+{
+  std::cout << "macosActualCase()\n";
+  MockSystem os;
+  os.SetPaths({
+    { "/", { {}, {} } },
+    { "/mixed", { "MiXeD", {} } },
+    { "/mixed/link-mixed", { "LiNk-MiXeD", "mixed" } },
+    { "/mixed/mixed", { "MiXeD", {} } },
+    { "/mixed/link-c-mixed", { "LiNk-C-MiXeD", "/mIxEd" } },
+    { "/upper", { "UPPER", {} } },
+    { "/upper/link-upper", { "LINK-UPPER", "upper" } },
+    { "/upper/upper", { "UPPER", {} } },
+    { "/upper/link-c-upper", { "LINK-C-UPPER", "/upper" } },
+  });
+
+  {
+    Resolver<Policies::LogicalPath> const r(os);
+    EXPECT_RESOLVE("/mIxEd/MiSsInG", "/MiXeD/MiSsInG");
+    EXPECT_RESOLVE("/mIxEd/link-MiXeD", "/MiXeD/LiNk-MiXeD");
+    EXPECT_RESOLVE("/mIxEd/link-c-MiXeD", "/MiXeD/LiNk-C-MiXeD");
+    EXPECT_RESOLVE("/upper/mIsSiNg", "/UPPER/mIsSiNg");
+    EXPECT_RESOLVE("/upper/link-upper", "/UPPER/LINK-UPPER");
+    EXPECT_RESOLVE("/upper/link-c-upper", "/UPPER/LINK-C-UPPER");
+  }
+
+  {
+    Resolver<Policies::RealPath> const r(os);
+    EXPECT_ENOENT("/mIxEd/MiSsInG", "/MiXeD/MiSsInG");
+    EXPECT_RESOLVE("/mIxEd/link-MiXeD", "/MiXeD/MiXeD");
+    EXPECT_RESOLVE("/mIxEd/link-c-MiXeD", "/MiXeD");
+    EXPECT_ENOENT("/upper/mIsSiNg", "/UPPER/mIsSiNg");
+    EXPECT_RESOLVE("/upper/link-upper", "/UPPER/UPPER");
+    EXPECT_RESOLVE("/upper/link-c-upper", "/UPPER");
+  }
+
+  return true;
+}
+#endif
+
 #ifdef _WIN32
 bool windowsRoot()
 {
@@ -464,6 +509,9 @@ int testPathResolver(int /*unused*/, char* /*unused*/[])
     posixAbsolutePath,
     posixWorkingDirectory,
     posixSymlink,
+#ifdef __APPLE__
+    macosActualCase,
+#endif
 #ifdef _WIN32
     windowsRoot,
     windowsAbsolutePath,