diff options
author | Michael Spencer <bigcheesegs@gmail.com> | 2024-01-30 15:39:18 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-30 15:39:18 -0800 |
commit | 7847e44594aa932c0a5f5d2cd15940d2a815c059 (patch) | |
tree | debc338a0ad7726ec484fab51fcf6504d8f3b357 /clang | |
parent | 16c15b5f84836d81084e9987701ca011da5a864f (diff) | |
download | llvm-7847e44594aa932c0a5f5d2cd15940d2a815c059.zip llvm-7847e44594aa932c0a5f5d2cd15940d2a815c059.tar.gz llvm-7847e44594aa932c0a5f5d2cd15940d2a815c059.tar.bz2 |
[clang][DependencyScanner] Remove unused -ivfsoverlay files (#73734)
`-ivfsoverlay` files are unused when building most modules. Enable
removing them by,
* adding a way to visit the filesystem tree with extensible RTTI to
access each `RedirectingFileSystem`.
* Adding tracking to `RedirectingFileSystem` to record when it
actually redirects a file access.
* Storing this information in each PCM.
Usage tracking is only enabled when iterating over the source manager
and affecting modulemaps. Here each path is stated to cause an access.
During scanning these stats all hit the cache.
Diffstat (limited to 'clang')
23 files changed, 788 insertions, 71 deletions
diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index 11c706e..4c4659e 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -44,6 +44,9 @@ def err_pch_diagopt_mismatch : Error<"%0 is currently enabled, but was not in " "the PCH file">; def err_pch_modulecache_mismatch : Error<"PCH was compiled with module cache " "path '%0', but the path is currently '%1'">; +def err_pch_vfsoverlay_mismatch : Error<"PCH was compiled with different VFS overlay files than are currently in use">; +def note_pch_vfsoverlay_files : Note<"%select{PCH|current translation unit}0 has the following VFS overlays:\n%1">; +def note_pch_vfsoverlay_empty : Note<"%select{PCH|current translation unit}0 has no VFS overlays">; def err_pch_version_too_old : Error< "PCH file uses an older PCH format that is no longer supported">; diff --git a/clang/include/clang/Basic/FileManager.h b/clang/include/clang/Basic/FileManager.h index 56cb093..997c17a 100644 --- a/clang/include/clang/Basic/FileManager.h +++ b/clang/include/clang/Basic/FileManager.h @@ -248,6 +248,10 @@ public: return FS; } + /// Enable or disable tracking of VFS usage. Used to not track full header + /// search and implicit modulemap lookup. + void trackVFSUsage(bool Active); + void setVirtualFileSystem(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) { this->FS = std::move(FS); } diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h index a2c3384..705dcfa 100644 --- a/clang/include/clang/Lex/HeaderSearch.h +++ b/clang/include/clang/Lex/HeaderSearch.h @@ -576,6 +576,13 @@ public: /// Note: implicit module maps don't contribute to entry usage. std::vector<bool> computeUserEntryUsage() const; + /// Collect which HeaderSearchOptions::VFSOverlayFiles have been meaningfully + /// used so far and mark their index with 'true' in the resulting bit vector. + /// + /// Note: this ignores VFSs that redirect non-affecting files such as unused + /// modulemaps. + std::vector<bool> collectVFSUsageAndClear() const; + /// This method returns a HeaderMap for the specified /// FileEntry, uniquing them through the 'HeaderMaps' datastructure. const HeaderMap *CreateHeaderMap(FileEntryRef FE); diff --git a/clang/include/clang/Lex/HeaderSearchOptions.h b/clang/include/clang/Lex/HeaderSearchOptions.h index fa2d0b5..637dc77 100644 --- a/clang/include/clang/Lex/HeaderSearchOptions.h +++ b/clang/include/clang/Lex/HeaderSearchOptions.h @@ -263,6 +263,10 @@ public: LLVM_PREFERRED_TYPE(bool) unsigned ModulesStrictContextHash : 1; + /// Whether to include ivfsoverlay usage information in written AST files. + LLVM_PREFERRED_TYPE(bool) + unsigned ModulesIncludeVFSUsage : 1; + HeaderSearchOptions(StringRef _Sysroot = "/") : Sysroot(_Sysroot), ModuleFormat("raw"), DisableModuleHash(false), ImplicitModuleMaps(false), ModuleMapFileHomeIsCwd(false), @@ -277,7 +281,7 @@ public: ModulesSkipDiagnosticOptions(false), ModulesSkipHeaderSearchPaths(false), ModulesSkipPragmaDiagnosticMappings(false), ModulesHashContent(false), - ModulesStrictContextHash(false) {} + ModulesStrictContextHash(false), ModulesIncludeVFSUsage(false) {} /// AddPath - Add the \p Path path to the specified \p Group list. void AddPath(StringRef Path, frontend::IncludeDirGroup Group, diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index ea084b3..9de9251 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -405,6 +405,9 @@ enum UnhashedControlBlockRecordTypes { /// Record code for the indices of used header search entries. HEADER_SEARCH_ENTRY_USAGE, + + /// Record code for the indices of used VFSs. + VFS_USAGE, }; /// Record code for extension blocks. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index ba06ab0..7328c4a 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1780,12 +1780,13 @@ public: /// Read the control block for the named AST file. /// /// \returns true if an error occurred, false otherwise. - static bool readASTFileControlBlock(StringRef Filename, FileManager &FileMgr, - const InMemoryModuleCache &ModuleCache, - const PCHContainerReader &PCHContainerRdr, - bool FindModuleFileExtensions, - ASTReaderListener &Listener, - bool ValidateDiagnosticOptions); + static bool readASTFileControlBlock( + StringRef Filename, FileManager &FileMgr, + const InMemoryModuleCache &ModuleCache, + const PCHContainerReader &PCHContainerRdr, bool FindModuleFileExtensions, + ASTReaderListener &Listener, bool ValidateDiagnosticOptions, + unsigned ClientLoadCapabilities = ARR_ConfigurationMismatch | + ARR_OutOfDate); /// Determine whether the given AST file is acceptable to load into a /// translation unit with the given language and target options. @@ -2270,6 +2271,9 @@ public: SourceRange ReadSourceRange(ModuleFile &F, const RecordData &Record, unsigned &Idx, LocSeq *Seq = nullptr); + static llvm::BitVector ReadBitVector(const RecordData &Record, + const StringRef Blob); + // Read a string static std::string ReadString(const RecordDataImpl &Record, unsigned &Idx); diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index de69f99..5e2f305 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -467,10 +467,10 @@ private: std::vector<SourceRange> NonAffectingRanges; std::vector<SourceLocation::UIntTy> NonAffectingOffsetAdjustments; - /// Collects input files that didn't affect compilation of the current module, + /// Computes input files that didn't affect compilation of the current module, /// and initializes data structures necessary for leaving those files out /// during \c SourceManager serialization. - void collectNonAffectingInputFiles(); + void computeNonAffectingInputFiles(); /// Returns an adjusted \c FileID, accounting for any non-affecting input /// files. diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h index 9a14129..bc0aa89 100644 --- a/clang/include/clang/Serialization/ModuleFile.h +++ b/clang/include/clang/Serialization/ModuleFile.h @@ -189,6 +189,9 @@ public: /// The bit vector denoting usage of each header search entry (true = used). llvm::BitVector SearchPathUsage; + /// The bit vector denoting usage of each VFS entry (true = used). + llvm::BitVector VFSUsage; + /// Whether this module has been directly imported by the /// user. bool DirectlyImported = false; diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h index 9a2aea5..846fdc7 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h @@ -280,8 +280,12 @@ public: /// This is not a thread safe VFS. A single instance is meant to be used only in /// one thread. Multiple instances are allowed to service multiple threads /// running in parallel. -class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem { +class DependencyScanningWorkerFilesystem + : public llvm::RTTIExtends<DependencyScanningWorkerFilesystem, + llvm::vfs::ProxyFileSystem> { public: + static const char ID; + DependencyScanningWorkerFilesystem( DependencyScanningFilesystemSharedCache &SharedCache, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS); diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h index dcdf1c1..4f98672 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h @@ -45,6 +45,9 @@ enum class ScanningOutputFormat { P1689, }; +#define DSS_LAST_BITMASK_ENUM(Id) \ + LLVM_MARK_AS_BITMASK_ENUM(Id), All = llvm::NextPowerOf2(Id) - 1 + enum class ScanningOptimizations { None = 0, @@ -54,11 +57,15 @@ enum class ScanningOptimizations { /// Remove warnings from system modules. SystemWarnings = 2, - LLVM_MARK_AS_BITMASK_ENUM(SystemWarnings), - All = HeaderSearch | SystemWarnings, + /// Remove unused -ivfsoverlay arguments. + VFS = 4, + + DSS_LAST_BITMASK_ENUM(VFS), Default = All }; +#undef DSS_LAST_BITMASK_ENUM + /// The dependency scanning service contains shared configuration and state that /// is used by the individual dependency scanning workers. class DependencyScanningService { diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp index 4e77b17..6097a27 100644 --- a/clang/lib/Basic/FileManager.cpp +++ b/clang/lib/Basic/FileManager.cpp @@ -363,6 +363,13 @@ llvm::Expected<FileEntryRef> FileManager::getSTDIN() { return *STDIN; } +void FileManager::trackVFSUsage(bool Active) { + FS->visit([Active](llvm::vfs::FileSystem &FileSys) { + if (auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys)) + RFS->setUsageTrackingActive(Active); + }); +} + const FileEntry *FileManager::getVirtualFile(StringRef Filename, off_t Size, time_t ModificationTime) { return &getVirtualFileRef(Filename, Size, ModificationTime).getFileEntry(); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index feb4de2..8d7b75b 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -4763,6 +4763,7 @@ std::string CompilerInvocation::getModuleHash() const { if (hsOpts.ModulesStrictContextHash) { HBuilder.addRange(hsOpts.SystemHeaderPrefixes); HBuilder.addRange(hsOpts.UserEntries); + HBuilder.addRange(hsOpts.VFSOverlayFiles); const DiagnosticOptions &diagOpts = getDiagnosticOpts(); #define DIAGOPT(Name, Bits, Default) HBuilder.add(diagOpts.Name); diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp index 0f10901..fcc2b56 100644 --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -141,6 +141,28 @@ std::vector<bool> HeaderSearch::computeUserEntryUsage() const { return UserEntryUsage; } +std::vector<bool> HeaderSearch::collectVFSUsageAndClear() const { + std::vector<bool> VFSUsage; + if (!getHeaderSearchOpts().ModulesIncludeVFSUsage) + return VFSUsage; + + llvm::vfs::FileSystem &RootFS = FileMgr.getVirtualFileSystem(); + // TODO: This only works if the `RedirectingFileSystem`s were all created by + // `createVFSFromOverlayFiles`. + RootFS.visit([&](llvm::vfs::FileSystem &FS) { + if (auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FS)) { + VFSUsage.push_back(RFS->hasBeenUsed()); + RFS->clearHasBeenUsed(); + } + }); + assert(VFSUsage.size() == getHeaderSearchOpts().VFSOverlayFiles.size() && + "A different number of RedirectingFileSystem's were present than " + "-ivfsoverlay options passed to Clang!"); + // VFS visit order is the opposite of VFSOverlayFiles order. + std::reverse(VFSUsage.begin(), VFSUsage.end()); + return VFSUsage; +} + /// CreateHeaderMap - This method returns a HeaderMap for the specified /// FileEntry, uniquing them through the 'HeaderMaps' datastructure. const HeaderMap *HeaderSearch::CreateHeaderMap(FileEntryRef FE) { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 2abe5e4..04ab42f 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -4977,7 +4977,7 @@ ASTReader::ASTReadResult ASTReader::readUnhashedControlBlockImpl( } case HEADER_SEARCH_PATHS: { bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch) == 0; - if (!AllowCompatibleConfigurationMismatch && + if (Listener && !AllowCompatibleConfigurationMismatch && ParseHeaderSearchPaths(Record, Complain, *Listener)) Result = ConfigurationMismatch; break; @@ -4992,15 +4992,12 @@ ASTReader::ASTReadResult ASTReader::readUnhashedControlBlockImpl( Record.begin(), Record.end()); break; case HEADER_SEARCH_ENTRY_USAGE: - if (!F) - break; - unsigned Count = Record[0]; - const char *Byte = Blob.data(); - F->SearchPathUsage = llvm::BitVector(Count, false); - for (unsigned I = 0; I < Count; ++Byte) - for (unsigned Bit = 0; Bit < 8 && I < Count; ++Bit, ++I) - if (*Byte & (1 << Bit)) - F->SearchPathUsage[I] = true; + if (F) + F->SearchPathUsage = ReadBitVector(Record, Blob); + break; + case VFS_USAGE: + if (F) + F->VFSUsage = ReadBitVector(Record, Blob); break; } } @@ -5398,7 +5395,8 @@ bool ASTReader::readASTFileControlBlock( StringRef Filename, FileManager &FileMgr, const InMemoryModuleCache &ModuleCache, const PCHContainerReader &PCHContainerRdr, bool FindModuleFileExtensions, - ASTReaderListener &Listener, bool ValidateDiagnosticOptions) { + ASTReaderListener &Listener, bool ValidateDiagnosticOptions, + unsigned ClientLoadCapabilities) { // Open the AST file. std::unique_ptr<llvm::MemoryBuffer> OwnedBuffer; llvm::MemoryBuffer *Buffer = ModuleCache.lookupPCM(Filename); @@ -5453,7 +5451,7 @@ bool ASTReader::readASTFileControlBlock( switch (Entry.ID) { case OPTIONS_BLOCK_ID: { std::string IgnoredSuggestedPredefines; - if (ReadOptionsBlock(Stream, ARR_ConfigurationMismatch | ARR_OutOfDate, + if (ReadOptionsBlock(Stream, ClientLoadCapabilities, /*AllowCompatibleConfigurationMismatch*/ false, Listener, IgnoredSuggestedPredefines) != Success) return true; @@ -5679,7 +5677,7 @@ bool ASTReader::readASTFileControlBlock( // Scan for the UNHASHED_CONTROL_BLOCK_ID block. if (readUnhashedControlBlockImpl( - nullptr, Bytes, ARR_ConfigurationMismatch | ARR_OutOfDate, + nullptr, Bytes, ClientLoadCapabilities, /*AllowCompatibleConfigurationMismatch*/ false, &Listener, ValidateDiagnosticOptions) != Success) return true; @@ -9316,6 +9314,18 @@ SourceRange ASTReader::ReadSourceRange(ModuleFile &F, const RecordData &Record, return SourceRange(beg, end); } +llvm::BitVector ASTReader::ReadBitVector(const RecordData &Record, + const StringRef Blob) { + unsigned Count = Record[0]; + const char *Byte = Blob.data(); + llvm::BitVector Ret = llvm::BitVector(Count, false); + for (unsigned I = 0; I < Count; ++Byte) + for (unsigned Bit = 0; Bit < 8 && I < Count; ++Bit, ++I) + if (*Byte & (1 << Bit)) + Ret[I] = true; + return Ret; +} + /// Read a floating-point value llvm::APFloat ASTRecordReader::readAPFloat(const llvm::fltSemantics &Sem) { return llvm::APFloat(Sem, readAPInt()); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index dcb18ad..9eefa7d 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1270,18 +1270,30 @@ void ASTWriter::writeUnhashedControlBlock(Preprocessor &PP, WritePragmaDiagnosticMappings(Diags, /* isModule = */ WritingModule); // Header search entry usage. - auto HSEntryUsage = PP.getHeaderSearchInfo().computeUserEntryUsage(); - auto Abbrev = std::make_shared<BitCodeAbbrev>(); - Abbrev->Add(BitCodeAbbrevOp(HEADER_SEARCH_ENTRY_USAGE)); - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Number of bits. - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Bit vector. - unsigned HSUsageAbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); { + auto HSEntryUsage = PP.getHeaderSearchInfo().computeUserEntryUsage(); + auto Abbrev = std::make_shared<BitCodeAbbrev>(); + Abbrev->Add(BitCodeAbbrevOp(HEADER_SEARCH_ENTRY_USAGE)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Number of bits. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Bit vector. + unsigned HSUsageAbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); RecordData::value_type Record[] = {HEADER_SEARCH_ENTRY_USAGE, HSEntryUsage.size()}; Stream.EmitRecordWithBlob(HSUsageAbbrevCode, Record, bytes(HSEntryUsage)); } + // VFS usage. + { + auto VFSUsage = PP.getHeaderSearchInfo().collectVFSUsageAndClear(); + auto Abbrev = std::make_shared<BitCodeAbbrev>(); + Abbrev->Add(BitCodeAbbrevOp(VFS_USAGE)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Number of bits. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Bit vector. + unsigned VFSUsageAbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + RecordData::value_type Record[] = {VFS_USAGE, VFSUsage.size()}; + Stream.EmitRecordWithBlob(VFSUsageAbbrevCode, Record, bytes(VFSUsage)); + } + // Leave the options block. Stream.ExitBlock(); UnhashedControlBlockRange.second = Stream.GetCurrentBitNo() >> 3; @@ -4672,7 +4684,7 @@ static void AddLazyVectorDecls(ASTWriter &Writer, Vector &Vec, } } -void ASTWriter::collectNonAffectingInputFiles() { +void ASTWriter::computeNonAffectingInputFiles() { SourceManager &SrcMgr = PP->getSourceManager(); unsigned N = SrcMgr.local_sloc_entry_size(); @@ -4732,6 +4744,30 @@ void ASTWriter::collectNonAffectingInputFiles() { NonAffectingFileIDAdjustments.push_back(FileIDAdjustment); NonAffectingOffsetAdjustments.push_back(OffsetAdjustment); } + + if (!PP->getHeaderSearchInfo().getHeaderSearchOpts().ModulesIncludeVFSUsage) + return; + + FileManager &FileMgr = PP->getFileManager(); + FileMgr.trackVFSUsage(true); + // Lookup the paths in the VFS to trigger `-ivfsoverlay` usage tracking. + for (StringRef Path : + PP->getHeaderSearchInfo().getHeaderSearchOpts().VFSOverlayFiles) + FileMgr.getVirtualFileSystem().exists(Path); + for (unsigned I = 1; I != N; ++I) { + if (IsSLocAffecting[I]) { + const SrcMgr::SLocEntry *SLoc = &SrcMgr.getLocalSLocEntry(I); + if (!SLoc->isFile()) + continue; + const SrcMgr::FileInfo &File = SLoc->getFile(); + const SrcMgr::ContentCache *Cache = &File.getContentCache(); + if (!Cache->OrigEntry) + continue; + FileMgr.getVirtualFileSystem().exists( + Cache->OrigEntry->getNameAsRequested()); + } + } + FileMgr.trackVFSUsage(false); } ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, @@ -4749,7 +4785,7 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, // This needs to be done very early, since everything that writes // SourceLocations or FileIDs depends on it. - collectNonAffectingInputFiles(); + computeNonAffectingInputFiles(); writeUnhashedControlBlock(PP, Context); diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp index 6f71650..1b750ce 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp @@ -194,7 +194,9 @@ static bool shouldCacheStatFailures(StringRef Filename) { DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem( DependencyScanningFilesystemSharedCache &SharedCache, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) - : ProxyFileSystem(std::move(FS)), SharedCache(SharedCache), + : llvm::RTTIExtends<DependencyScanningWorkerFilesystem, + llvm::vfs::ProxyFileSystem>(std::move(FS)), + SharedCache(SharedCache), WorkingDirForCacheLookup(llvm::errc::invalid_argument) { updateWorkingDirForCacheLookup(); } @@ -379,3 +381,5 @@ void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() { assert(!WorkingDirForCacheLookup || llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup)); } + +const char DependencyScanningWorkerFilesystem::ID = 0; diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp index 7ab4a69..390cbe5 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp @@ -9,6 +9,7 @@ #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" #include "clang/Basic/DiagnosticDriver.h" #include "clang/Basic/DiagnosticFrontend.h" +#include "clang/Basic/DiagnosticSerialization.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" @@ -59,6 +60,31 @@ private: DependencyConsumer &C; }; +static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts, + const HeaderSearchOptions &ExistingHSOpts, + DiagnosticsEngine *Diags, + const LangOptions &LangOpts) { + if (LangOpts.Modules) { + if (HSOpts.VFSOverlayFiles != ExistingHSOpts.VFSOverlayFiles) { + if (Diags) { + Diags->Report(diag::err_pch_vfsoverlay_mismatch); + auto VFSNote = [&](int Type, ArrayRef<std::string> VFSOverlays) { + if (VFSOverlays.empty()) { + Diags->Report(diag::note_pch_vfsoverlay_empty) << Type; + } else { + std::string Files = llvm::join(VFSOverlays, "\n"); + Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files; + } + }; + VFSNote(0, HSOpts.VFSOverlayFiles); + VFSNote(1, ExistingHSOpts.VFSOverlayFiles); + } + return true; + } + } + return false; +} + using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles); /// A listener that collects the imported modules and optionally the input @@ -66,9 +92,12 @@ using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles); class PrebuiltModuleListener : public ASTReaderListener { public: PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles, - llvm::SmallVector<std::string> &NewModuleFiles) + llvm::SmallVector<std::string> &NewModuleFiles, + const HeaderSearchOptions &HSOpts, + const LangOptions &LangOpts, DiagnosticsEngine &Diags) : PrebuiltModuleFiles(PrebuiltModuleFiles), - NewModuleFiles(NewModuleFiles) {} + NewModuleFiles(NewModuleFiles), ExistingHSOpts(HSOpts), + ExistingLangOpts(LangOpts), Diags(Diags) {} bool needsImportVisitation() const override { return true; } @@ -77,26 +106,47 @@ public: NewModuleFiles.push_back(Filename.str()); } + bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts, + bool Complain) override { + return checkHeaderSearchPaths( + HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts); + } + private: PrebuiltModuleFilesT &PrebuiltModuleFiles; llvm::SmallVector<std::string> &NewModuleFiles; + const HeaderSearchOptions &ExistingHSOpts; + const LangOptions &ExistingLangOpts; + DiagnosticsEngine &Diags; }; /// Visit the given prebuilt module and collect all of the modules it /// transitively imports and contributing input files. -static void visitPrebuiltModule(StringRef PrebuiltModuleFilename, +static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename, CompilerInstance &CI, - PrebuiltModuleFilesT &ModuleFiles) { + PrebuiltModuleFilesT &ModuleFiles, + DiagnosticsEngine &Diags) { // List of module files to be processed. - llvm::SmallVector<std::string> Worklist{PrebuiltModuleFilename.str()}; - PrebuiltModuleListener Listener(ModuleFiles, Worklist); - - while (!Worklist.empty()) - ASTReader::readASTFileControlBlock( - Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(), - CI.getPCHContainerReader(), - /*FindModuleFileExtensions=*/false, Listener, - /*ValidateDiagnosticOptions=*/false); + llvm::SmallVector<std::string> Worklist; + PrebuiltModuleListener Listener( + ModuleFiles, Worklist, CI.getHeaderSearchOpts(), CI.getLangOpts(), Diags); + + if (ASTReader::readASTFileControlBlock( + PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(), + CI.getPCHContainerReader(), + /*FindModuleFileExtensions=*/false, Listener, + /*ValidateDiagnosticOptions=*/false, ASTReader::ARR_OutOfDate)) + return true; + + while (!Worklist.empty()) { + if (ASTReader::readASTFileControlBlock( + Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(), + CI.getPCHContainerReader(), + /*FindModuleFileExtensions=*/false, Listener, + /*ValidateDiagnosticOptions=*/false)) + return true; + } + return false; } /// Transform arbitrary file name into an object-like file name. @@ -183,6 +233,7 @@ public: ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false; ScanInstance.getFrontendOpts().ModulesShareFileManager = false; ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw"; + ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage = true; ScanInstance.setFileManager(FileMgr); // Support for virtual file system overlays. @@ -196,9 +247,12 @@ public: // will prevent the implicit build to create duplicate modules and will // force reuse of the existing prebuilt module files instead. if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty()) - visitPrebuiltModule( - ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance, - ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles); + if (visitPrebuiltModule( + ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, + ScanInstance, + ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles, + ScanInstance.getDiagnostics())) + return false; // Use the dependency scanning optimized file system if requested to do so. if (DepFS) { diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index bfaa897..b807dc8 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -31,25 +31,55 @@ const std::vector<std::string> &ModuleDeps::getBuildArguments() { static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, ASTReader &Reader, - const serialization::ModuleFile &MF) { - // Only preserve search paths that were used during the dependency scan. - std::vector<HeaderSearchOptions::Entry> Entries = Opts.UserEntries; - Opts.UserEntries.clear(); - - llvm::BitVector SearchPathUsage(Entries.size()); - llvm::DenseSet<const serialization::ModuleFile *> Visited; - std::function<void(const serialization::ModuleFile *)> VisitMF = - [&](const serialization::ModuleFile *MF) { - SearchPathUsage |= MF->SearchPathUsage; - Visited.insert(MF); - for (const serialization::ModuleFile *Import : MF->Imports) - if (!Visited.contains(Import)) - VisitMF(Import); - }; - VisitMF(&MF); - - for (auto Idx : SearchPathUsage.set_bits()) - Opts.UserEntries.push_back(Entries[Idx]); + const serialization::ModuleFile &MF, + ScanningOptimizations OptimizeArgs) { + if (any(OptimizeArgs & ScanningOptimizations::HeaderSearch)) { + // Only preserve search paths that were used during the dependency scan. + std::vector<HeaderSearchOptions::Entry> Entries; + std::swap(Opts.UserEntries, Entries); + + llvm::BitVector SearchPathUsage(Entries.size()); + llvm::DenseSet<const serialization::ModuleFile *> Visited; + std::function<void(const serialization::ModuleFile *)> VisitMF = + [&](const serialization::ModuleFile *MF) { + SearchPathUsage |= MF->SearchPathUsage; + Visited.insert(MF); + for (const serialization::ModuleFile *Import : MF->Imports) + if (!Visited.contains(Import)) + VisitMF(Import); + }; + VisitMF(&MF); + + if (SearchPathUsage.size() != Entries.size()) + llvm::report_fatal_error( + "Inconsistent search path options between modules detected"); + + for (auto Idx : SearchPathUsage.set_bits()) + Opts.UserEntries.push_back(std::move(Entries[Idx])); + } + if (any(OptimizeArgs & ScanningOptimizations::VFS)) { + std::vector<std::string> VFSOverlayFiles; + std::swap(Opts.VFSOverlayFiles, VFSOverlayFiles); + + llvm::BitVector VFSUsage(VFSOverlayFiles.size()); + llvm::DenseSet<const serialization::ModuleFile *> Visited; + std::function<void(const serialization::ModuleFile *)> VisitMF = + [&](const serialization::ModuleFile *MF) { + VFSUsage |= MF->VFSUsage; + Visited.insert(MF); + for (const serialization::ModuleFile *Import : MF->Imports) + if (!Visited.contains(Import)) + VisitMF(Import); + }; + VisitMF(&MF); + + if (VFSUsage.size() != VFSOverlayFiles.size()) + llvm::report_fatal_error( + "Inconsistent -ivfsoverlay options between modules detected"); + + for (auto Idx : VFSUsage.set_bits()) + Opts.VFSOverlayFiles.push_back(std::move(VFSOverlayFiles[Idx])); + } } static void optimizeDiagnosticOpts(DiagnosticOptions &Opts, @@ -558,9 +588,11 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { CowCompilerInvocation CI = MDC.getInvocationAdjustedForModuleBuildWithoutOutputs( MD, [&](CowCompilerInvocation &BuildInvocation) { - if (any(MDC.OptimizeArgs & ScanningOptimizations::HeaderSearch)) + if (any(MDC.OptimizeArgs & (ScanningOptimizations::HeaderSearch | + ScanningOptimizations::VFS))) optimizeHeaderSearchOpts(BuildInvocation.getMutHeaderSearchOpts(), - *MDC.ScanInstance.getASTReader(), *MF); + *MDC.ScanInstance.getASTReader(), *MF, + MDC.OptimizeArgs); if (any(MDC.OptimizeArgs & ScanningOptimizations::SystemWarnings)) optimizeDiagnosticOpts( BuildInvocation.getMutDiagnosticOpts(), diff --git a/clang/test/ClangScanDeps/optimize-vfs-edgecases.m b/clang/test/ClangScanDeps/optimize-vfs-edgecases.m new file mode 100644 index 0000000..0f18571 --- /dev/null +++ b/clang/test/ClangScanDeps/optimize-vfs-edgecases.m @@ -0,0 +1,84 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/build/compile-commands.json.in > %t/build/compile-commands.json +// RUN: sed -e "s|DIR|%/t|g" %t/build/vfsoverlay.yaml.in > %t/build/vfsoverlay.yaml +// RUN: sed -e "s|DIR|%/t|g" %t/build/vfs.notyaml.in > %t/build/vfs.notyaml +// RUN: clang-scan-deps -compilation-database %t/build/compile-commands.json \ +// RUN: -j 1 -format experimental-full --optimize-args=vfs,header-search > %t/deps.db + +// RUN: %deps-to-rsp %t/deps.db --module-name=A > %t/A.rsp +// RUN: cd %t && %clang @%t/A.rsp + +// Check that the following edge cases are handled by ivfsoverlay tracking +// * `-ivfsoverlay` args that depend on earlier `-ivfsoverlay` args. + +//--- build/compile-commands.json.in + +[ +{ + "directory": "DIR", + "command": "clang -c DIR/0.m -Imodules/A -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps -ivfsoverlay build/vfsoverlay.yaml -ivfsoverlay build/vfs.yaml", + "file": "DIR/0.m" +} +] + +//--- build/vfsoverlay.yaml.in + +{ + "version":0, + "case-sensitive":"false", + "roots":[ + { + "contents":[ + { + "external-contents":"DIR/build/vfs.notyaml", + "name":"vfs.yaml", + "type":"file" + } + ], + "name":"DIR/build", + "type":"directory" + } + ] +} + +//--- build/vfs.notyaml.in + +{ + "version":0, + "case-sensitive":"false", + "roots":[ + { + "contents":[ + { + "external-contents":"DIR/build/module.modulemap", + "name":"module.modulemap", + "type":"file" + }, + { + "external-contents":"DIR/build/A.h", + "name":"A.h", + "type":"file" + } + ], + "name":"DIR/modules/A", + "type":"directory" + } + ] +} + +//--- build/module.modulemap + +module A { + umbrella header "A.h" +} + +//--- build/A.h + +typedef int A_t; + +//--- 0.m + +#include <A.h> + +A_t a = 0; diff --git a/clang/test/ClangScanDeps/optimize-vfs-leak.m b/clang/test/ClangScanDeps/optimize-vfs-leak.m new file mode 100644 index 0000000..beda44c --- /dev/null +++ b/clang/test/ClangScanDeps/optimize-vfs-leak.m @@ -0,0 +1,105 @@ +// This test checks that VFS usage doesn't leak between modules. + +// RUN: rm -rf %t && split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/build/cdb.json.in > %t/build/cdb.json +// RUN: sed -e "s|DIR|%/t|g" %t/build/vfs.yaml.in > %t/build/vfs.yaml +// RUN: clang-scan-deps -compilation-database %t/build/cdb.json \ +// RUN: -format experimental-full --optimize-args=vfs > %t/deps.json +// RUN: cat %t/deps.json | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t + +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "B" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "C" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/moduleA/module.modulemap", +// CHECK-NEXT: "command-line": [ +// Module A needs the VFS overlay because its dependency, module B, needs it. +// CHECK: "-ivfsoverlay" +// CHECK-NEXT: "[[PREFIX]]/build/vfs.yaml" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK: ], +// CHECK-NEXT: "name": "A" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/moduleB/module.modulemap", +// CHECK-NEXT: "command-line": [ +// Module B needs the VFS overlay because it provides the header referred to by the module map. +// CHECK: "-ivfsoverlay" +// CHECK-NEXT: "[[PREFIX]]/build/vfs.yaml" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK: ], +// CHECK-NEXT: "name": "B" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/moduleC/module.modulemap", +// CHECK-NEXT: "command-line": [ +// Module C doesn't need the VFS overlay. +// CHECK-NOT: "-ivfsoverlay" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK: ], +// CHECK-NEXT: "name": "C" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "translation-units": [ +// CHECK: ] +// CHECK: } + +//--- build/cdb.json.in +[{ + "directory": "DIR", + "command": "clang -c DIR/tu.m -I DIR/moduleA -I DIR/moduleB -I DIR/moduleC -fmodules -fmodules-cache-path=DIR/cache -fimplicit-module-maps -ivfsoverlay DIR/build/vfs.yaml", + "file": "DIR/tu.m" +}] + +//--- build/vfs.yaml.in +{ + "version": 0, + "case-sensitive": "false", + "roots": [ + { + "contents": [ + { + "external-contents": "DIR/build/B.h", + "name": "B.h", + "type": "file" + } + ], + "name": "DIR/moduleB", + "type": "directory" + } + ] +} + +//--- tu.m +@import A; + +//--- moduleA/module.modulemap +module A { header "A.h" } +//--- moduleA/A.h +@import B; +@import C; + +//--- moduleB/module.modulemap +module B { header "B.h" } +//--- build/B.h + +//--- moduleC/module.modulemap +module C { header "C.h" } +//--- moduleC/C.h
\ No newline at end of file diff --git a/clang/test/ClangScanDeps/optimize-vfs-pch.m b/clang/test/ClangScanDeps/optimize-vfs-pch.m new file mode 100644 index 0000000..e6acb73 --- /dev/null +++ b/clang/test/ClangScanDeps/optimize-vfs-pch.m @@ -0,0 +1,129 @@ +// Check that tracking of VFSs works with PCH. + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/build/compile-commands-pch.json.in > %t/build/compile-commands-pch.json +// RUN: sed -e "s|DIR|%/t|g" %t/build/compile-commands-tu.json.in > %t/build/compile-commands-tu.json +// RUN: sed -e "s|DIR|%/t|g" %t/build/compile-commands-tu-no-vfs.json.in > %t/build/compile-commands-tu-no-vfs.json +// RUN: sed -e "s|DIR|%/t|g" %t/build/pch-overlay.yaml.in > %t/build/pch-overlay.yaml + +// RUN: clang-scan-deps -compilation-database %t/build/compile-commands-pch.json \ +// RUN: -j 1 -format experimental-full --optimize-args=vfs,header-search > %t/pch-deps.db +// RUN: %deps-to-rsp %t/pch-deps.db --module-name=A > %t/A.rsp +// RUN: %deps-to-rsp %t/pch-deps.db --module-name=B > %t/B.rsp +// RUN: %deps-to-rsp %t/pch-deps.db --tu-index=0 > %t/pch.rsp +// RUN: %clang @%t/A.rsp +// RUN: %clang @%t/B.rsp +// RUN: %clang @%t/pch.rsp + +// RUN: clang-scan-deps -compilation-database %t/build/compile-commands-tu.json \ +// RUN: -j 1 -format experimental-full --optimize-args=vfs,header-search > %t/tu-deps.db +// RUN: %deps-to-rsp %t/tu-deps.db --module-name=C > %t/C.rsp +// RUN: %deps-to-rsp %t/tu-deps.db --tu-index=0 > %t/tu.rsp +// RUN: %clang @%t/C.rsp +// RUN: %clang @%t/tu.rsp + +// RUN: not clang-scan-deps -compilation-database %t/build/compile-commands-tu-no-vfs.json \ +// RUN: -j 1 -format experimental-full --optimize-args=vfs,header-search 2>&1 | FileCheck %s + +// CHECK: error: PCH was compiled with different VFS overlay files than are currently in use +// CHECK: note: current translation unit has no VFS overlays + +//--- build/compile-commands-pch.json.in + +[ +{ + "directory": "DIR", + "command": "clang -x objective-c-header DIR/pch.h -I DIR/modules/A -I DIR/modules/B -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -o DIR/pch.h.pch -ivfsoverlay DIR/build/pch-overlay.yaml", + "file": "DIR/pch.h" +} +] + +//--- build/compile-commands-tu.json.in + +[ +{ + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.m -I DIR/modules/A -I DIR/modules/B -I DIR/modules/C -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -include DIR/pch.h -o DIR/tu.o -ivfsoverlay DIR/build/pch-overlay.yaml", + "file": "DIR/tu.m" +} +] + +//--- build/compile-commands-tu-no-vfs.json.in + +[ +{ + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.m -I DIR/modules/A -I DIR/modules/B -I DIR/modules/C -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -include DIR/pch.h -o DIR/tu.o", + "file": "DIR/tu.m" +} +] + +//--- build/pch-overlay.yaml.in + +{ + "version":0, + "case-sensitive":"false", + "roots":[ + { + "contents":[ + { + "external-contents":"DIR/build/module.modulemap", + "name":"module.modulemap", + "type":"file" + }, + { + "external-contents":"DIR/build/A.h", + "name":"A.h", + "type":"file" + } + ], + "name":"DIR/modules/A", + "type":"directory" + } + ] +} + +//--- pch.h +#include <B.h> + +//--- build/module.modulemap + +module A { + umbrella header "A.h" +} + +//--- build/A.h + +typedef int A_t; + +//--- modules/B/module.modulemap + +module B { + umbrella header "B.h" + export * +} + +//--- modules/B/B.h +#include <A.h> + +typedef int B_t; + +//--- modules/C/module.modulemap + +module C { + umbrella header "C.h" +} + +//--- modules/C/C.h +#include <B.h> + +typedef int C_t; + +//--- tu.m + +#include <C.h> + +A_t a = 0; +B_t b = 0; +C_t c = 0; diff --git a/clang/test/ClangScanDeps/optimize-vfs.m b/clang/test/ClangScanDeps/optimize-vfs.m new file mode 100644 index 0000000..20c9795 --- /dev/null +++ b/clang/test/ClangScanDeps/optimize-vfs.m @@ -0,0 +1,193 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/build/compile-commands.json.in > %t/build/compile-commands.json +// RUN: sed -e "s|DIR|%/t|g" %t/build/vfs.yaml.in > %t/build/vfs.yaml +// RUN: sed -e "s|DIR|%/t|g" %t/build/unused-vfs.yaml.in > %t/build/unused-vfs.yaml +// RUN: sed -e "s|DIR|%/t|g" %t/build/unused-vfs.yaml.in > %t/build/unused2-vfs.yaml +// RUN: clang-scan-deps -compilation-database %t/build/compile-commands.json \ +// RUN: -j 1 -format experimental-full --optimize-args=vfs,header-search > %t/deps.db +// RUN: cat %t/deps.db | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t + +// Check that unused -ivfsoverlay arguments are removed, and that used ones are +// not. + +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/modules/A/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NOT: "build/unused-vfs.yaml" +// CHECK: "-ivfsoverlay" +// CHECK-NEXT: "build/vfs.yaml" +// CHECK-NOT: "build/unused-vfs.yaml" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/build/A.h", +// CHECK-NEXT: "[[PREFIX]]/build/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "A" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/modules/B/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NOT: "-ivfsoverlay" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/modules/B/B.h", +// CHECK-NEXT: "[[PREFIX]]/modules/B/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "B" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "B" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/modules/C/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NOT: "-ivfsoverlay" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/modules/B/module.modulemap", +// CHECK-NEXT: "[[PREFIX]]/modules/C/C.h", +// CHECK-NEXT: "[[PREFIX]]/modules/C/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "C" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "translation-units": [ +// CHECK: ] +// CHECK: } + +//--- build/compile-commands.json.in + +[ +{ + "directory": "DIR", + "command": "clang -c DIR/0.m -Imodules/A -Imodules/B -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps -ivfsoverlay build/unused-vfs.yaml -ivfsoverlay build/unused2-vfs.yaml -ivfsoverlay build/vfs.yaml", + "file": "DIR/0.m" +}, +{ + "directory": "DIR", + "command": "clang -c DIR/A.m -Imodules/A -Imodules/B -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps -ivfsoverlay build/vfs.yaml -ivfsoverlay build/unused-vfs.yaml", + "file": "DIR/A.m" +}, +{ + "directory": "DIR", + "command": "clang -c DIR/B.m -Imodules/B -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps -ivfsoverlay build/unused-vfs.yaml -ivfsoverlay build/vfs.yaml", + "file": "DIR/B.m" +}, +{ + "directory": "DIR", + "command": "clang -c DIR/C.m -Imodules/A -Imodules/B -Imodules/C -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-module-maps -ivfsoverlay build/vfs.yaml -ivfsoverlay build/unused-vfs.yaml", + "file": "DIR/C.m" +} +] + +//--- build/vfs.yaml.in + +{ + "version":0, + "case-sensitive":"false", + "roots":[ + { + "contents":[ + { + "external-contents":"DIR/build/module.modulemap", + "name":"module.modulemap", + "type":"file" + }, + { + "external-contents":"DIR/build/A.h", + "name":"A.h", + "type":"file" + } + ], + "name":"DIR/modules/A", + "type":"directory" + } + ] +} + +//--- build/unused-vfs.yaml.in + +{ + "version":0, + "case-sensitive":"false", + "roots":[ + { + "contents":[ + { + "external-contents":"DIR/build/module.modulemap", + "name":"module.modulemap", + "type":"file" + } + ], + "name":"DIR/modules/D", + "type":"directory" + } + ] +} + +//--- build/module.modulemap + +module A { + umbrella header "A.h" +} + +//--- build/A.h + +typedef int A_t; + +//--- modules/B/module.modulemap + +module B { + umbrella header "B.h" +} + +//--- modules/B/B.h + +typedef int B_t; + +//--- modules/C/module.modulemap + +module C { + umbrella header "C.h" +} + +//--- modules/C/C.h + +@import B; + +typedef B_t C_t; + +//--- 0.m + +#include <A.h> + +A_t a = 0; + +//--- A.m + +#include <A.h> + +A_t a = 0; + +//--- B.m + +#include <B.h> + +B_t b = 0; + +//--- C.m + +#include <C.h> + +C_t b = 0; diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp index fb42dc1..7633766 100644 --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -156,6 +156,7 @@ static void ParseArgs(int argc, char **argv) { .Case("none", ScanningOptimizations::None) .Case("header-search", ScanningOptimizations::HeaderSearch) .Case("system-warnings", ScanningOptimizations::SystemWarnings) + .Case("vfs", ScanningOptimizations::VFS) .Case("all", ScanningOptimizations::All) .Default(std::nullopt); if (!Optimization) { |