diff --git a/Source/cmGetFilenameComponentCommand.cxx b/Source/cmGetFilenameComponentCommand.cxx index dee5c3f009..96666140f1 100644 --- a/Source/cmGetFilenameComponentCommand.cxx +++ b/Source/cmGetFilenameComponentCommand.cxx @@ -8,6 +8,7 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmValue.h" +#include "cmake.h" // cmGetFilenameComponentCommand bool cmGetFilenameComponentCommand(std::vector const& args, @@ -113,7 +114,8 @@ bool cmGetFilenameComponentCommand(std::vector const& args, // Resolve symlinks if possible result = cmSystemTools::GetRealPath(result); } - result = cmSystemTools::GetActualCaseForPath(result); + result = cmSystemTools::GetActualCaseForPath( + result, status.GetMakefile().GetCMakeInstance()->GetActualCaseCache()); } else { std::string err = "unknown component " + args[2]; status.SetError(err); diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index af18f6cf41..e1d9a22488 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -2047,7 +2047,8 @@ std::string cmSystemTools::RelativeIfUnder(std::string const& top, return out; } -std::string cmSystemTools::GetActualCaseForPath(std::string const& p) +std::string cmSystemTools::GetActualCaseForPath( + std::string const& p, std::unordered_map* cache) { #ifdef _WIN32 std::string casePath; @@ -2101,15 +2102,32 @@ std::string cmSystemTools::GetActualCaseForPath(std::string const& p) std::string test_str = casePath; test_str += path_components[idx]; - WIN32_FIND_DATAW findData; - HANDLE hFind = ::FindFirstFileW( - cmsys::Encoding::ToWide(test_str).c_str(), &findData); - if (INVALID_HANDLE_VALUE != hFind) { - auto case_file_name = cmsys::Encoding::ToNarrow(findData.cFileName); - path_components[idx] = std::move(case_file_name); - ::FindClose(hFind); + // Only components found to exist are cached, so that a path + // created later is still converted. + std::string const* cached = nullptr; + if (cache) { + auto it = cache->find(test_str); + if (it != cache->end()) { + cached = &it->second; + } + } + if (cached) { + path_components[idx] = *cached; } else { - converting = false; + WIN32_FIND_DATAW findData; + HANDLE hFind = ::FindFirstFileW( + cmsys::Encoding::ToWide(test_str).c_str(), &findData); + if (INVALID_HANDLE_VALUE != hFind) { + auto case_file_name = + cmsys::Encoding::ToNarrow(findData.cFileName); + ::FindClose(hFind); + if (cache) { + cache->emplace(std::move(test_str), case_file_name); + } + path_components[idx] = std::move(case_file_name); + } else { + converting = false; + } } } } @@ -2118,6 +2136,7 @@ std::string cmSystemTools::GetActualCaseForPath(std::string const& p) } return casePath; #else + static_cast(cache); return p; #endif } diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 689becea8e..5f7eba5d6a 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -433,7 +434,15 @@ public: static std::string RelativeIfUnder(std::string const& top, std::string const& in); - static std::string GetActualCaseForPath(std::string const& p); + /** + * Get the path with the actual case of all existing components on disk. + * If a cache is given, it is used to remember the actual case of + * components found to exist, to avoid querying the disk again for + * paths sharing the same directories. + */ + static std::string GetActualCaseForPath( + std::string const& p, + std::unordered_map* cache = nullptr); static cm::optional GetEnvVar(std::string const& var); static std::vector GetEnvPathNormalized(std::string const& var); diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 30e079b65a..ae8fc03ab9 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -2577,6 +2577,7 @@ int cmake::Configure() void cmake::InvalidateFileSystemCaches() { + this->ActualCaseCache.clear(); if (this->GlobalGenerator) { this->GlobalGenerator->ClearSourceFileExistsCache(); } diff --git a/Source/cmake.h b/Source/cmake.h index 225bc97f9a..4fa7b686fc 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -476,6 +477,12 @@ public: return *this->GeneratorExpressionParseCache; } + //! Cache for cmSystemTools::GetActualCaseForPath. + std::unordered_map* GetActualCaseCache() + { + return &this->ActualCaseCache; + } + //! Forget what is known about the file system, e.g. after files may //! have been removed or renamed. void InvalidateFileSystemCaches(); @@ -830,6 +837,7 @@ private: std::unique_ptr FileTimeCache; std::unique_ptr GeneratorExpressionParseCache; + std::unordered_map ActualCaseCache; std::string GraphVizFile; InstalledFilesMap InstalledFiles; #ifndef CMAKE_BOOTSTRAP