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