[clang] Expose normalized module cache path in HeaderSearch (#185746)

Previously, the normalized module cache path was only accessible via
`HeaderSearch::getSpecificModuleCachePath()` which may or may not also
contain the context hash. Clients would need to parse the result to
learn the normalized module cache path. What `ASTWriter` does instead is
normalize the as-written module cache path redundantly.

Instead, this PR exposes the normalized module cache path in the
`HeaderSearch` interface and moves the computation of specific module
cache path into the clangLex library.

This is motivated by another patch that would've needed to redundantly
perform the module cache path canonicalization or parse the specific
module cache path.
This commit is contained in:
Jan Svoboda 2026-03-12 13:52:42 -07:00 committed by GitHub
parent 4334fed5c9
commit 983269bee7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 118 additions and 91 deletions

View File

@ -746,11 +746,6 @@ public:
GetDependencyDirectives = std::move(Getter);
}
std::string getSpecificModuleCachePath(StringRef ContextHash);
std::string getSpecificModuleCachePath() {
return getSpecificModuleCachePath(getInvocation().computeContextHash());
}
/// Create the AST context.
void createASTContext();

View File

@ -282,6 +282,10 @@ class HeaderSearch {
/// The specific module cache path containing ContextHash (unless suppressed).
std::string SpecificModuleCachePath;
/// The length of the normalized module cache path at the start of \c
/// SpecificModuleCachePath.
size_t NormalizedModuleCachePathLen = 0;
/// All of the preprocessor-specific data about files that are
/// included, indexed by the FileEntry's UID.
mutable std::vector<HeaderFileInfo> FileInfo;
@ -467,20 +471,23 @@ public:
return {};
}
/// Set the context hash to use for module cache paths.
void setContextHash(StringRef Hash) { ContextHash = std::string(Hash); }
/// Initialize the module cache path.
void initializeModuleCachePath(std::string ContextHash);
/// Set the module cache path with the context hash (unless suppressed).
void setSpecificModuleCachePath(StringRef Path) {
SpecificModuleCachePath = std::string(Path);
/// Retrieve the specific module cache path. This is the normalized module
/// cache path plus the context hash (unless suppressed).
StringRef getSpecificModuleCachePath() const {
return SpecificModuleCachePath;
}
/// Retrieve the context hash.
StringRef getContextHash() const { return ContextHash; }
/// Retrieve the module cache path with the context hash (unless suppressed).
StringRef getSpecificModuleCachePath() const {
return SpecificModuleCachePath;
/// Retrieve the normalized module cache path. This is the path as provided on
/// the command line, but absolute, without './' components, and with
/// preferred path separators. Note that this does not have the context hash.
StringRef getNormalizedModuleCachePath() const {
return getSpecificModuleCachePath().substr(0, NormalizedModuleCachePathLen);
}
/// Forget everything we know about headers so far.
@ -1042,6 +1049,11 @@ void ApplyHeaderSearchOptions(HeaderSearch &HS,
void normalizeModuleCachePath(FileManager &FileMgr, StringRef Path,
SmallVectorImpl<char> &NormalizedPath);
std::string createSpecificModuleCachePath(FileManager &FileMgr,
StringRef ModuleCachePath,
bool DisableModuleHash,
std::string ContextHash);
} // namespace clang
#endif // LLVM_CLANG_LEX_HEADERSEARCH_H

View File

@ -44,7 +44,7 @@ namespace serialization {
/// Version 4 of AST files also requires that the version control branch and
/// revision match exactly, since there is no backward compatibility of
/// AST files at this time.
const unsigned VERSION_MAJOR = 35;
const unsigned VERSION_MAJOR = 36;
/// AST file minor version number supported by this version of
/// Clang.

View File

@ -185,8 +185,7 @@ public:
/// otherwise.
virtual bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
StringRef ModuleFilename,
StringRef SpecificModuleCachePath,
bool Complain) {
StringRef ContextHash, bool Complain) {
return false;
}
@ -304,8 +303,7 @@ public:
bool Complain) override;
bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
StringRef ModuleFilename,
StringRef SpecificModuleCachePath,
StringRef ModuleFilename, StringRef ContextHash,
bool Complain) override;
bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts,
StringRef ModuleFilename, bool ReadMacros,
@ -349,8 +347,7 @@ public:
bool Complain,
std::string &SuggestedPredefines) override;
bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
StringRef ModuleFilename,
StringRef SpecificModuleCachePath,
StringRef ModuleFilename, StringRef ContextHash,
bool Complain) override;
void ReadCounter(const serialization::ModuleFile &M, uint32_t Value) override;
};

View File

@ -150,8 +150,7 @@ public:
/// Check the header search options for a given module when considering
/// if the module comes from stable directories.
bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
StringRef ModuleFilename,
StringRef SpecificModuleCachePath,
StringRef ModuleFilename, StringRef ContextHash,
bool Complain) override {
auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);

View File

@ -505,7 +505,7 @@ namespace {
/// a Preprocessor.
class ASTInfoCollector : public ASTReaderListener {
HeaderSearchOptions &HSOpts;
std::string &SpecificModuleCachePath;
std::string &ContextHash;
PreprocessorOptions &PPOpts;
LangOptions &LangOpts;
CodeGenOptions &CodeGenOpts;
@ -513,14 +513,13 @@ class ASTInfoCollector : public ASTReaderListener {
uint32_t &Counter;
public:
ASTInfoCollector(HeaderSearchOptions &HSOpts,
std::string &SpecificModuleCachePath,
ASTInfoCollector(HeaderSearchOptions &HSOpts, std::string &ContextHash,
PreprocessorOptions &PPOpts, LangOptions &LangOpts,
CodeGenOptions &CodeGenOpts, TargetOptions &TargetOpts,
uint32_t &Counter)
: HSOpts(HSOpts), SpecificModuleCachePath(SpecificModuleCachePath),
PPOpts(PPOpts), LangOpts(LangOpts), CodeGenOpts(CodeGenOpts),
TargetOpts(TargetOpts), Counter(Counter) {}
: HSOpts(HSOpts), ContextHash(ContextHash), PPOpts(PPOpts),
LangOpts(LangOpts), CodeGenOpts(CodeGenOpts), TargetOpts(TargetOpts),
Counter(Counter) {}
bool ReadLanguageOptions(const LangOptions &NewLangOpts,
StringRef ModuleFilename, bool Complain,
@ -538,10 +537,10 @@ public:
bool ReadHeaderSearchOptions(const HeaderSearchOptions &NewHSOpts,
StringRef ModuleFilename,
StringRef NewSpecificModuleCachePath,
StringRef NewContextHash,
bool Complain) override {
HSOpts = NewHSOpts;
SpecificModuleCachePath = NewSpecificModuleCachePath;
ContextHash = NewContextHash;
return false;
}
@ -733,13 +732,13 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
AST->ModCache = createCrossProcessModuleCache();
// Gather info for preprocessor construction later on.
std::string SpecificModuleCachePath;
std::string ContextHash;
unsigned Counter = 0;
// Using a temporary FileManager since the AST file might specify custom
// HeaderSearchOptions::VFSOverlayFiles that affect the underlying VFS.
FileManager TmpFileMgr(FileSystemOpts, VFS);
ASTInfoCollector Collector(*AST->HSOpts, SpecificModuleCachePath,
*AST->PPOpts, *AST->LangOpts, *AST->CodeGenOpts,
ASTInfoCollector Collector(*AST->HSOpts, ContextHash, *AST->PPOpts,
*AST->LangOpts, *AST->CodeGenOpts,
*AST->TargetOpts, Counter);
if (ASTReader::readASTFileControlBlock(
Filename, TmpFileMgr, *AST->ModCache, PCHContainerRdr,
@ -763,7 +762,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
AST->getHeaderSearchOpts(), AST->getSourceManager(),
AST->getDiagnostics(), AST->getLangOpts(),
/*Target=*/nullptr);
AST->HeaderInfo->setSpecificModuleCachePath(SpecificModuleCachePath);
AST->HeaderInfo->initializeModuleCachePath(std::move(ContextHash));
AST->PP = std::make_shared<Preprocessor>(
*AST->PPOpts, AST->getDiagnostics(), *AST->LangOpts,

View File

@ -487,10 +487,11 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) {
PP->setPreprocessedOutput(getPreprocessorOutputOpts().ShowCPP);
if (PP->getLangOpts().Modules && PP->getLangOpts().ImplicitModules) {
std::string ContextHash = getInvocation().computeContextHash();
PP->getHeaderSearchInfo().setContextHash(ContextHash);
PP->getHeaderSearchInfo().setSpecificModuleCachePath(
getSpecificModuleCachePath(ContextHash));
// FIXME: We already might've computed the context hash and the specific
// module cache path in `FrontendAction::BeginSourceFile()` when turning
// "-include-pch <DIR>" into "-include-pch <DIR>/<FILE>". Reuse those here.
PP->getHeaderSearchInfo().initializeModuleCachePath(
getInvocation().computeContextHash());
}
// Handle generating dependencies, if requested.
@ -546,19 +547,6 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) {
PP->setDependencyDirectivesGetter(*GetDependencyDirectives);
}
std::string
CompilerInstance::getSpecificModuleCachePath(StringRef ContextHash) {
assert(FileMgr && "Specific module cache path requires a FileManager");
// Set up the module path, including the hash for the module-creation options.
SmallString<256> SpecificModuleCache;
normalizeModuleCachePath(*FileMgr, getHeaderSearchOpts().ModuleCachePath,
SpecificModuleCache);
if (!SpecificModuleCache.empty() && !getHeaderSearchOpts().DisableModuleHash)
llvm::sys::path::append(SpecificModuleCache, ContextHash);
return std::string(SpecificModuleCache);
}
// ASTContext
void CompilerInstance::createASTContext() {

View File

@ -1040,7 +1040,10 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
llvm::sys::path::native(PCHDir->getName(), DirNative);
bool Found = false;
llvm::vfs::FileSystem &FS = FileMgr.getVirtualFileSystem();
std::string SpecificModuleCachePath = CI.getSpecificModuleCachePath();
std::string SpecificModuleCachePath = createSpecificModuleCachePath(
CI.getFileManager(), CI.getHeaderSearchOpts().ModuleCachePath,
CI.getHeaderSearchOpts().DisableModuleHash,
CI.getInvocation().computeContextHash());
for (llvm::vfs::directory_iterator Dir = FS.dir_begin(DirNative, EC),
DirEnd;
Dir != DirEnd && !EC; Dir.increment(EC)) {

View File

@ -623,9 +623,11 @@ namespace {
/// file.
class DumpModuleInfoListener : public ASTReaderListener {
llvm::raw_ostream &Out;
FileManager &FileMgr;
public:
DumpModuleInfoListener(llvm::raw_ostream &Out) : Out(Out) { }
DumpModuleInfoListener(llvm::raw_ostream &Out, FileManager &FileMgr)
: Out(Out), FileMgr(FileMgr) {}
#define DUMP_BOOLEAN(Value, Text) \
Out.indent(4) << Text << ": " << (Value? "Yes" : "No") << "\n"
@ -716,8 +718,12 @@ namespace {
bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
StringRef ModuleFilename,
StringRef SpecificModuleCachePath,
StringRef ContextHash,
bool Complain) override {
std::string SpecificModuleCachePath = createSpecificModuleCachePath(
FileMgr, HSOpts.ModuleCachePath, HSOpts.DisableModuleHash,
std::string(ContextHash));
Out.indent(2) << "Header search options:\n";
Out.indent(4) << "System root [-isysroot=]: '" << HSOpts.Sysroot << "'\n";
Out.indent(4) << "Resource dir [ -resource-dir=]: '" << HSOpts.ResourceDir << "'\n";
@ -905,7 +911,7 @@ void DumpModuleInfoAction::ExecuteAction() {
Out << " Module format: " << (IsRaw ? "raw" : "obj") << "\n";
Preprocessor &PP = CI.getPreprocessor();
DumpModuleInfoListener Listener(Out);
DumpModuleInfoListener Listener(Out, CI.getFileManager());
const HeaderSearchOptions &HSOpts =
PP.getHeaderSearchInfo().getHeaderSearchOpts();

View File

@ -2471,3 +2471,31 @@ void clang::normalizeModuleCachePath(FileManager &FileMgr, StringRef Path,
llvm::sys::path::remove_dots(NormalizedPath);
}
}
static std::string createSpecificModuleCachePathImpl(
FileManager &FileMgr, StringRef ModuleCachePath, bool DisableModuleHash,
std::string ContextHash, size_t &NormalizedModuleCachePathLen) {
SmallString<256> SpecificModuleCachePath;
normalizeModuleCachePath(FileMgr, ModuleCachePath, SpecificModuleCachePath);
NormalizedModuleCachePathLen = SpecificModuleCachePath.size();
if (!SpecificModuleCachePath.empty() && !DisableModuleHash)
llvm::sys::path::append(SpecificModuleCachePath, ContextHash);
return std::string(SpecificModuleCachePath);
}
void HeaderSearch::initializeModuleCachePath(std::string NewContextHash) {
ContextHash = std::move(NewContextHash);
SpecificModuleCachePath = createSpecificModuleCachePathImpl(
FileMgr, HSOpts.ModuleCachePath, HSOpts.DisableModuleHash, ContextHash,
NormalizedModuleCachePathLen);
}
std::string clang::createSpecificModuleCachePath(FileManager &FileMgr,
StringRef ModuleCachePath,
bool DisableModuleHash,
std::string ContextHash) {
size_t NormalizedModuleCachePathLen;
return createSpecificModuleCachePathImpl(
FileMgr, ModuleCachePath, DisableModuleHash, std::move(ContextHash),
NormalizedModuleCachePathLen);
}

View File

@ -208,11 +208,11 @@ ChainedASTReaderListener::ReadFileSystemOptions(const FileSystemOptions &FSOpts,
bool ChainedASTReaderListener::ReadHeaderSearchOptions(
const HeaderSearchOptions &HSOpts, StringRef ModuleFilename,
StringRef SpecificModuleCachePath, bool Complain) {
return First->ReadHeaderSearchOptions(HSOpts, ModuleFilename,
SpecificModuleCachePath, Complain) ||
Second->ReadHeaderSearchOptions(HSOpts, ModuleFilename,
SpecificModuleCachePath, Complain);
StringRef ContextHash, bool Complain) {
return First->ReadHeaderSearchOptions(HSOpts, ModuleFilename, ContextHash,
Complain) ||
Second->ReadHeaderSearchOptions(HSOpts, ModuleFilename, ContextHash,
Complain);
}
bool ChainedASTReaderListener::ReadPreprocessorOptions(
@ -960,25 +960,31 @@ bool SimpleASTReaderListener::ReadPreprocessorOptions(
///
/// \param Diags If non-null, produce diagnostics for any mismatches incurred.
/// \returns true when the module cache paths differ.
static bool checkModuleCachePath(
llvm::vfs::FileSystem &VFS, StringRef SpecificModuleCachePath,
StringRef ExistingSpecificModuleCachePath, StringRef ASTFilename,
DiagnosticsEngine *Diags, const LangOptions &LangOpts,
const PreprocessorOptions &PPOpts, const HeaderSearchOptions &HSOpts,
const HeaderSearchOptions &ASTFileHSOpts) {
static bool checkModuleCachePath(FileManager &FileMgr, StringRef ContextHash,
StringRef ExistingSpecificModuleCachePath,
StringRef ASTFilename,
DiagnosticsEngine *Diags,
const LangOptions &LangOpts,
const PreprocessorOptions &PPOpts,
const HeaderSearchOptions &HSOpts,
const HeaderSearchOptions &ASTFileHSOpts) {
std::string SpecificModuleCachePath = createSpecificModuleCachePath(
FileMgr, ASTFileHSOpts.ModuleCachePath, ASTFileHSOpts.DisableModuleHash,
std::string(ContextHash));
if (!LangOpts.Modules || PPOpts.AllowPCHWithDifferentModulesCachePath ||
SpecificModuleCachePath == ExistingSpecificModuleCachePath)
return false;
auto EqualOrErr =
VFS.equivalent(SpecificModuleCachePath, ExistingSpecificModuleCachePath);
auto EqualOrErr = FileMgr.getVirtualFileSystem().equivalent(
SpecificModuleCachePath, ExistingSpecificModuleCachePath);
if (EqualOrErr && *EqualOrErr)
return false;
if (Diags) {
// If the module cache arguments provided from the command line are the
// same, the mismatch must come from other arguments of the configuration
// and not directly the cache path.
EqualOrErr =
VFS.equivalent(ASTFileHSOpts.ModuleCachePath, HSOpts.ModuleCachePath);
EqualOrErr = FileMgr.getVirtualFileSystem().equivalent(
ASTFileHSOpts.ModuleCachePath, HSOpts.ModuleCachePath);
if (EqualOrErr && *EqualOrErr)
Diags->Report(clang::diag::warn_ast_file_config_mismatch) << ASTFilename;
else
@ -991,14 +997,14 @@ static bool checkModuleCachePath(
bool PCHValidator::ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
StringRef ASTFilename,
StringRef SpecificModuleCachePath,
StringRef ContextHash,
bool Complain) {
const HeaderSearch &HeaderSearchInfo = PP.getHeaderSearchInfo();
return checkModuleCachePath(
Reader.getFileManager().getVirtualFileSystem(), SpecificModuleCachePath,
HeaderSearchInfo.getSpecificModuleCachePath(), ASTFilename,
Complain ? &Reader.Diags : nullptr, PP.getLangOpts(),
PP.getPreprocessorOpts(), HeaderSearchInfo.getHeaderSearchOpts(), HSOpts);
return checkModuleCachePath(Reader.getFileManager(), ContextHash,
HeaderSearchInfo.getSpecificModuleCachePath(),
ASTFilename, Complain ? &Reader.Diags : nullptr,
PP.getLangOpts(), PP.getPreprocessorOpts(),
HeaderSearchInfo.getHeaderSearchOpts(), HSOpts);
}
void PCHValidator::ReadCounter(const ModuleFile &M, uint32_t Value) {
@ -5877,13 +5883,11 @@ namespace {
}
bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
StringRef ASTFilename,
StringRef SpecificModuleCachePath,
StringRef ASTFilename, StringRef ContextHash,
bool Complain) override {
return checkModuleCachePath(
FileMgr.getVirtualFileSystem(), SpecificModuleCachePath,
ExistingSpecificModuleCachePath, ASTFilename, nullptr,
ExistingLangOpts, ExistingPPOpts, ExistingHSOpts, HSOpts);
FileMgr, ContextHash, ExistingSpecificModuleCachePath, ASTFilename,
nullptr, ExistingLangOpts, ExistingPPOpts, ExistingHSOpts, HSOpts);
}
bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts,
@ -6705,10 +6709,10 @@ bool ASTReader::ParseHeaderSearchOptions(const RecordData &Record,
HSOpts.UseStandardSystemIncludes = Record[Idx++];
HSOpts.UseStandardCXXIncludes = Record[Idx++];
HSOpts.UseLibcxx = Record[Idx++];
std::string SpecificModuleCachePath = ReadString(Record, Idx);
std::string ContextHash = ReadString(Record, Idx);
return Listener.ReadHeaderSearchOptions(HSOpts, ModuleFilename,
SpecificModuleCachePath, Complain);
return Listener.ReadHeaderSearchOptions(HSOpts, ModuleFilename, ContextHash,
Complain);
}
bool ASTReader::ParseHeaderSearchPaths(const RecordData &Record, bool Complain,

View File

@ -1694,9 +1694,8 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) {
const HeaderSearchOptions &HSOpts =
PP.getHeaderSearchInfo().getHeaderSearchOpts();
SmallString<256> HSOpts_ModuleCachePath;
normalizeModuleCachePath(PP.getFileManager(), HSOpts.ModuleCachePath,
HSOpts_ModuleCachePath);
StringRef HSOpts_ModuleCachePath =
PP.getHeaderSearchInfo().getNormalizedModuleCachePath();
AddString(HSOpts.Sysroot, Record);
AddString(HSOpts.ResourceDir, Record);
@ -1710,10 +1709,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) {
Record.push_back(HSOpts.UseStandardSystemIncludes);
Record.push_back(HSOpts.UseStandardCXXIncludes);
Record.push_back(HSOpts.UseLibcxx);
// Write out the specific module cache path that contains the module files.
// FIXME: We already wrote out the normalized cache path. Just write the
// context hash (unless suppressed).
AddString(PP.getHeaderSearchInfo().getSpecificModuleCachePath(), Record);
AddString(PP.getHeaderSearchInfo().getContextHash(), Record);
Stream.EmitRecord(HEADER_SEARCH_OPTIONS, Record);
// Preprocessor options.