From 01d2a64980833b8b25c70489079b6e107f0e0bd5 Mon Sep 17 00:00:00 2001 From: Russell Greene Date: Wed, 11 Dec 2024 10:23:53 -0700 Subject: [PATCH 1/2] cmSystemTools: Add GetDirCase helper function On Linux, one can set case insensitivity on a per-directory level. --- Source/cmSystemTools.cxx | 38 ++++++++++++++++++++++++++++++++++++++ Source/cmSystemTools.h | 9 +++++++++ 2 files changed, 47 insertions(+) diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 24ab7ae949..65802eb036 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -118,6 +118,12 @@ # include /* for malloc/free on QNX */ #endif +#ifdef __linux__ +# include + +# include +#endif + #if !defined(_WIN32) && !defined(__ANDROID__) # include #endif @@ -3941,6 +3947,38 @@ std::string cmSystemTools::EncodeURL(std::string const& in, bool escapeSlashes) return out; } +cm::optional 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) { diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 4f62aee639..6b2bdaff35 100644 --- a/Source/cmSystemTools.h +++ b/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 GetDirCase(std::string const& dir); + #ifdef _WIN32 struct WindowsFileRetry { From 1897686702bab16fbc72ecb97145c877bbe40fe3 Mon Sep 17 00:00:00 2001 From: Russell Greene Date: Wed, 11 Dec 2024 10:24:19 -0700 Subject: [PATCH 2/2] find_library: Handle case-insensitive filesystems on Linux Linux 5.2+ supports case-insensitive ext4 filesystems. Teach `find_library` pattern matching to account for this. Fixes: #26526 --- Source/cmFindLibraryCommand.cxx | 92 +++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 28 deletions(-) diff --git a/Source/cmFindLibraryCommand.cxx b/Source/cmFindLibraryCommand.cxx index c5a60383f8..15f2d9d6b6 100644 --- a/Source/cmFindLibraryCommand.cxx +++ b/Source/cmFindLibraryCommand.cxx @@ -3,11 +3,14 @@ #include "cmFindLibraryCommand.h" #include +#include #include #include #include #include +#include + #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::size_type; @@ -217,11 +222,14 @@ struct cmFindLibraryHelper bool TryRaw = false; std::string Raw; cmsys::RegularExpression Regex; + cmsys::RegularExpression ICaseRegex; // case insensitive }; std::vector 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(tolower(ch)); -#else - out += ch; -#endif + if (dirCase == cmSystemTools::DirCase::Insensitive) { + out += static_cast(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]+)?"; + { + 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); } - 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 dirCase = + cmSystemTools::GetDirCase(path).value_or( + cmSystemTools::DirCase::Sensitive); + cmsys::RegularExpression& regex = + dirCase == cmSystemTools::DirCase::Insensitive ? name.ICaseRegex + : name.Regex; std::set 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) ||