AutoGen: Use directory listings to rule out missing headers

AutoGen checked the disk for a header with every header extension, with
and without a "_p" suffix, for each source file processed by moc or uic.
Most of these files do not exist.  Use a listing of each directory,
cached while scanning sources, to rule them out, and check the disk only
for names that are present or that Windows may resolve without a
matching directory entry.
This commit is contained in:
Cristian Adam committed 2026-10-14 07:33:47 +02:00
1 parent b8dc198329
commit 0af0ee077f
3 files changed
+56 -2

No files matched your search

+45
View File
@@ -2,12 +2,15 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmQtAutoGenGlobalInitializer.h"
#include <algorithm>
#include <set>
#include <utility>
#include <cm/memory>
#include <cm/optional>
#include "cmsys/Directory.hxx"
#include "cmCustomCommand.h"
#include "cmDiagnostics.h"
#include "cmDuration.h"
@@ -162,6 +165,46 @@ cmQtAutoGenGlobalInitializer::cmQtAutoGenGlobalInitializer(
cmQtAutoGenGlobalInitializer::~cmQtAutoGenGlobalInitializer() = default;
bool cmQtAutoGenGlobalInitializer::CachedFileExists(std::string const& path)
{
// Most of the header paths probed for a source file do not exist.
// A file whose name is not in the directory, compared without regard
// to case, cannot exist. Names that are present, and names whose case
// cannot be folded reliably, are checked on disk as before. So are
// names Windows may resolve without a matching directory entry: short
// 8.3 names, and names with a trailing dot or space.
//
// The listings are kept only while the per target initializers scan
// their sources. Nothing creates files in the probed directories
// meanwhile; AUTOGEN writes only to its own build directories, and
// file(GENERATE) outputs are written later.
std::string const name = cmSystemTools::GetFilenameName(path);
bool const listable = !name.empty() && name.back() != '.' &&
name.back() != ' ' && name.find('~') == std::string::npos &&
std::all_of(name.begin(), name.end(),
[](char c) { return static_cast<unsigned char>(c) < 0x80; });
if (listable) {
std::string const dir = cmSystemTools::GetFilenamePath(path);
auto it = this->DirectoryNames_.find(dir);
if (it == this->DirectoryNames_.end()) {
cmsys::Directory d;
if (d.Load(dir)) {
std::unordered_set<std::string> names;
unsigned long const n = d.GetNumberOfFiles();
for (unsigned long i = 0; i < n; ++i) {
names.insert(cmSystemTools::LowerCase(d.GetFileName(i)));
}
it = this->DirectoryNames_.emplace(dir, std::move(names)).first;
}
}
if (it != this->DirectoryNames_.end() &&
!it->second.count(cmSystemTools::LowerCase(name))) {
return false;
}
}
return cmSystemTools::FileExists(path);
}
void cmQtAutoGenGlobalInitializer::GetOrCreateGlobalTarget(
cmLocalGenerator* localGen, std::string const& name,
std::string const& comment)
@@ -344,6 +387,8 @@ bool cmQtAutoGenGlobalInitializer::InitializeCustomTargets()
return false;
}
}
// The directory listings are only valid while scanning sources.
this->DirectoryNames_.clear();
return true;
}
+9
View File
@@ -8,6 +8,7 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "cmQtAutoGen.h"
@@ -66,6 +67,11 @@ private:
void AddToGlobalAutoRcc(cmLocalGenerator* localGen,
std::string const& targetName);
/// @brief Check whether a file exists, using a cached listing of its
/// directory to rule out files that do not exist without querying
/// the file system for each of them.
bool CachedFileExists(std::string const& path);
cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>
GetCompilerFeatures(std::string const& generator,
cmQtAutoGen::ConfigString const& executable,
@@ -79,4 +85,7 @@ private:
std::unordered_map<std::string, cmQtAutoGen::CompilerFeaturesHandle>>
CompilerFeatures_;
Keywords const Keywords_;
/// Lower case names in directories, see CachedFileExists.
std::unordered_map<std::string, std::unordered_set<std::string>>
DirectoryNames_;
};
+2 -2
View File
@@ -1112,10 +1112,10 @@ bool cmQtAutoGenInitializer::InitScanFiles()
}
// We only accept not-GENERATED files that do exist.
if (!sf->GetIsGenerated() &&
!cmSystemTools::FileExists(fullPath)) {
!this->GlobalInitializer->CachedFileExists(fullPath)) {
continue;
}
} else if (cmSystemTools::FileExists(fullPath)) {
} else if (this->GlobalInitializer->CachedFileExists(fullPath)) {
// Create a new source file for the existing file
sf = this->Makefile->CreateSource(fullPath, false, locationKind);
}