Browse Source

Merge topic 'icase_linux'

1897686702 find_library: Handle case-insensitive filesystems on Linux
01d2a64980 cmSystemTools: Add GetDirCase helper function

Acked-by: Kitware Robot <[email protected]>
Merge-request: !10089
Brad King 1 year ago
parent
commit
c60098c763
3 changed files with 112 additions and 29 deletions
  1. 65 29
      Source/cmFindLibraryCommand.cxx
  2. 38 0
      Source/cmSystemTools.cxx
  3. 9 0
      Source/cmSystemTools.h

+ 65 - 29
Source/cmFindLibraryCommand.cxx

@@ -3,11 +3,14 @@
 #include "cmFindLibraryCommand.h"
 
 #include <algorithm>
+#include <cctype>
 #include <cstdio>
 #include <cstring>
 #include <set>
 #include <utility>
 
+#include <cm/optional>
+
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmGlobalGenerator.h"
@@ -200,7 +203,9 @@ struct cmFindLibraryHelper
   cmList Prefixes;
   cmList Suffixes;
   std::string PrefixRegexStr;
+  std::string ICasePrefixRegexStr; // case insensitive
   std::string SuffixRegexStr;
+  std::string ICaseSuffixRegexStr; // case insensitive
 
   // Keep track of the best library file found so far.
   using size_type = std::vector<std::string>::size_type;
@@ -217,11 +222,14 @@ struct cmFindLibraryHelper
     bool TryRaw = false;
     std::string Raw;
     cmsys::RegularExpression Regex;
+    cmsys::RegularExpression ICaseRegex; // case insensitive
   };
   std::vector<Name> Names;
 
-  void RegexFromLiteral(std::string& out, std::string const& in);
-  void RegexFromList(std::string& out, cmList const& in);
+  void RegexFromLiteral(std::string& out, std::string const& in,
+                        cmSystemTools::DirCase dirCase);
+  void RegexFromList(std::string& out, cmList const& in,
+                     cmSystemTools::DirCase dirCase);
   size_type GetPrefixIndex(std::string const& prefix)
   {
     return std::find(this->Prefixes.begin(), this->Prefixes.end(), prefix) -
@@ -313,8 +321,14 @@ cmFindLibraryHelper::cmFindLibraryHelper(std::string debugName, cmMakefile* mf,
 
   this->Prefixes.assign(prefixes_list, cmList::EmptyElements::Yes);
   this->Suffixes.assign(suffixes_list, cmList::EmptyElements::Yes);
-  this->RegexFromList(this->PrefixRegexStr, this->Prefixes);
-  this->RegexFromList(this->SuffixRegexStr, this->Suffixes);
+  this->RegexFromList(this->PrefixRegexStr, this->Prefixes,
+                      cmSystemTools::DirCase::Sensitive);
+  this->RegexFromList(this->ICasePrefixRegexStr, this->Prefixes,
+                      cmSystemTools::DirCase::Insensitive);
+  this->RegexFromList(this->SuffixRegexStr, this->Suffixes,
+                      cmSystemTools::DirCase::Sensitive);
+  this->RegexFromList(this->ICaseSuffixRegexStr, this->Suffixes,
+                      cmSystemTools::DirCase::Insensitive);
 
   // Check whether to use OpenBSD-style library version comparisons.
   this->IsOpenBSD = this->Makefile->GetState()->GetGlobalPropertyAsBool(
@@ -322,7 +336,8 @@ cmFindLibraryHelper::cmFindLibraryHelper(std::string debugName, cmMakefile* mf,
 }
 
 void cmFindLibraryHelper::RegexFromLiteral(std::string& out,
-                                           std::string const& in)
+                                           std::string const& in,
+                                           cmSystemTools::DirCase dirCase)
 {
   for (char ch : in) {
     if (ch == '[' || ch == ']' || ch == '(' || ch == ')' || ch == '\\' ||
@@ -330,15 +345,16 @@ void cmFindLibraryHelper::RegexFromLiteral(std::string& out,
         ch == '^' || ch == '$') {
       out += "\\";
     }
-#if defined(_WIN32) || defined(__APPLE__)
-    out += static_cast<char>(tolower(ch));
-#else
-    out += ch;
-#endif
+    if (dirCase == cmSystemTools::DirCase::Insensitive) {
+      out += static_cast<char>(tolower(ch));
+    } else {
+      out += ch;
+    }
   }
 }
 
-void cmFindLibraryHelper::RegexFromList(std::string& out, cmList const& in)
+void cmFindLibraryHelper::RegexFromList(std::string& out, cmList const& in,
+                                        cmSystemTools::DirCase dirCase)
 {
   // Surround the list in parens so the '|' does not apply to anything
   // else and the result can be checked after matching.
@@ -350,7 +366,7 @@ void cmFindLibraryHelper::RegexFromList(std::string& out, cmList const& in)
     sep = "|";
 
     // Append this item.
-    this->RegexFromLiteral(out, s);
+    this->RegexFromLiteral(out, s, dirCase);
   }
   out += ")";
 }
@@ -384,14 +400,29 @@ void cmFindLibraryHelper::AddName(std::string const& name)
   entry.Raw = name;
 
   // Build a regular expression to match library names.
-  std::string regex = cmStrCat('^', this->PrefixRegexStr);
-  this->RegexFromLiteral(regex, name);
-  regex += this->SuffixRegexStr;
-  if (this->IsOpenBSD) {
-    regex += "(\\.[0-9]+\\.[0-9]+)?";
-  }
-  regex += "$";
-  entry.Regex.compile(regex);
+  {
+    std::string regex = cmStrCat('^', this->PrefixRegexStr);
+    this->RegexFromLiteral(regex, name, cmSystemTools::DirCase::Sensitive);
+    regex += this->SuffixRegexStr;
+    if (this->IsOpenBSD) {
+      regex += "(\\.[0-9]+\\.[0-9]+)?";
+    }
+    regex += "$";
+    entry.Regex.compile(regex);
+  }
+
+  // case insensitive version
+  {
+    std::string regex = cmStrCat('^', this->ICasePrefixRegexStr);
+    this->RegexFromLiteral(regex, name, cmSystemTools::DirCase::Insensitive);
+    regex += this->ICaseSuffixRegexStr;
+    if (this->IsOpenBSD) {
+      regex += "(\\.[0-9]+\\.[0-9]+)?";
+    }
+    regex += "$";
+    entry.ICaseRegex.compile(regex);
+  }
+
   this->Names.push_back(std::move(entry));
 }
 
@@ -440,14 +471,19 @@ bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path,
   unsigned int bestMinor = 0;
 
   // Search for a file matching the library name regex.
+  cm::optional<cmSystemTools::DirCase> dirCase =
+    cmSystemTools::GetDirCase(path).value_or(
+      cmSystemTools::DirCase::Sensitive);
+  cmsys::RegularExpression& regex =
+    dirCase == cmSystemTools::DirCase::Insensitive ? name.ICaseRegex
+                                                   : name.Regex;
   std::set<std::string> const& files = this->GG->GetDirectoryContent(path);
   for (std::string const& origName : files) {
-#if defined(_WIN32) || defined(__APPLE__)
-    std::string testName = cmSystemTools::LowerCase(origName);
-#else
-    std::string const& testName = origName;
-#endif
-    if (name.Regex.find(testName)) {
+    std::string testName = dirCase == cmSystemTools::DirCase::Insensitive
+      ? cmSystemTools::LowerCase(origName)
+      : origName;
+
+    if (regex.find(testName)) {
       std::string testPath = cmStrCat(path, origName);
       // Make sure the path is readable and is not a directory.
       if (cmSystemTools::FileExists(testPath, true)) {
@@ -461,12 +497,12 @@ bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path,
         // best name found so far.  Earlier prefixes are preferred,
         // followed by earlier suffixes.  For OpenBSD, shared library
         // version extensions are compared.
-        size_type prefix = this->GetPrefixIndex(name.Regex.match(1));
-        size_type suffix = this->GetSuffixIndex(name.Regex.match(2));
+        size_type prefix = this->GetPrefixIndex(regex.match(1));
+        size_type suffix = this->GetSuffixIndex(regex.match(2));
         unsigned int major = 0;
         unsigned int minor = 0;
         if (this->IsOpenBSD) {
-          sscanf(name.Regex.match(3).c_str(), ".%u.%u", &major, &minor);
+          sscanf(regex.match(3).c_str(), ".%u.%u", &major, &minor);
         }
         if (this->BestPath.empty() || prefix < bestPrefix ||
             (prefix == bestPrefix && suffix < bestSuffix) ||

+ 38 - 0
Source/cmSystemTools.cxx

@@ -118,6 +118,12 @@
 #  include <malloc.h> /* for malloc/free on QNX */
 #endif
 
+#ifdef __linux__
+#  include <linux/fs.h>
+
+#  include <sys/ioctl.h>
+#endif
+
 #if !defined(_WIN32) && !defined(__ANDROID__)
 #  include <sys/utsname.h>
 #endif
@@ -3941,6 +3947,38 @@ std::string cmSystemTools::EncodeURL(std::string const& in, bool escapeSlashes)
   return out;
 }
 
+cm::optional<cmSystemTools::DirCase> cmSystemTools::GetDirCase(
+  std::string const& dir)
+{
+  if (!cmSystemTools::FileIsDirectory(dir)) {
+    return cm::nullopt;
+  }
+#if defined(_WIN32) || defined(__APPLE__)
+  return DirCase::Insensitive;
+#elif defined(__linux__)
+  int fd = open(dir.c_str(), O_RDONLY);
+  if (fd == -1) {
+    // cannot open dir but it exists, assume dir is case sensitive.
+    return DirCase::Sensitive;
+  }
+  int attr = 0;
+  int ioctl_res = ioctl(fd, FS_IOC_GETFLAGS, &attr);
+  close(fd);
+
+  if (ioctl_res == -1) {
+    return DirCase::Sensitive;
+  }
+
+  // FS_CASEFOLD_FD from linux/fs.h, in Linux libc-dev 5.4+
+  // For compat with old libc-dev, define it here.
+  const int CMAKE_FS_CASEFOLD_FL = 0x40000000;
+  return (attr & CMAKE_FS_CASEFOLD_FL) != 0 ? DirCase::Insensitive
+                                            : DirCase::Sensitive;
+#else
+  return DirCase::Sensitive;
+#endif
+}
+
 cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName,
                                            std::string const& newName)
 {

+ 9 - 0
Source/cmSystemTools.h

@@ -584,6 +584,15 @@ public:
   static std::string EncodeURL(std::string const& in,
                                bool escapeSlashes = true);
 
+  enum class DirCase
+  {
+    Sensitive,
+    Insensitive,
+  };
+
+  /** Returns nullopt when `dir` is not a valid directory */
+  static cm::optional<DirCase> GetDirCase(std::string const& dir);
+
 #ifdef _WIN32
   struct WindowsFileRetry
   {