diff options
Diffstat (limited to 'clang/lib/Tooling/DependencyScanning')
9 files changed, 0 insertions, 2961 deletions
diff --git a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt deleted file mode 100644 index 76bdc50..0000000 --- a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -set(LLVM_LINK_COMPONENTS - Core - Option - Support - TargetParser - ) - -add_clang_library(clangDependencyScanning - DependencyScanningFilesystem.cpp - DependencyScanningService.cpp - DependencyScanningWorker.cpp - DependencyScanningTool.cpp - DependencyScannerImpl.cpp - InProcessModuleCache.cpp - ModuleDepCollector.cpp - - DEPENDS - ClangDriverOptions - - LINK_LIBS - clangAST - clangBasic - clangDriver - clangFrontend - clangLex - clangSerialization - ${LLVM_PTHREAD_LIB} - ) diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp deleted file mode 100644 index eebecdb..0000000 --- a/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp +++ /dev/null @@ -1,706 +0,0 @@ -//===- DependencyScanner.cpp - Performs module dependency scanning --------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "DependencyScannerImpl.h" -#include "clang/Basic/DiagnosticFrontend.h" -#include "clang/Basic/DiagnosticSerialization.h" -#include "clang/Driver/Driver.h" -#include "clang/Frontend/FrontendActions.h" -#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" -#include "llvm/TargetParser/Host.h" - -using namespace clang; -using namespace tooling; -using namespace dependencies; - -namespace { -/// Forwards the gatherered dependencies to the consumer. -class DependencyConsumerForwarder : public DependencyFileGenerator { -public: - DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts, - StringRef WorkingDirectory, DependencyConsumer &C) - : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory), - Opts(std::move(Opts)), C(C) {} - - void finishedMainFile(DiagnosticsEngine &Diags) override { - C.handleDependencyOutputOpts(*Opts); - llvm::SmallString<256> CanonPath; - for (const auto &File : getDependencies()) { - CanonPath = File; - llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true); - llvm::sys::path::make_absolute(WorkingDirectory, CanonPath); - C.handleFileDependency(CanonPath); - } - } - -private: - StringRef WorkingDirectory; - std::unique_ptr<DependencyOutputOptions> Opts; - 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::warn_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 false; -} - -using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles); - -/// A listener that collects the imported modules and the input -/// files. While visiting, collect vfsoverlays and file inputs that determine -/// whether prebuilt modules fully resolve in stable directories. -class PrebuiltModuleListener : public ASTReaderListener { -public: - PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles, - llvm::SmallVector<std::string> &NewModuleFiles, - PrebuiltModulesAttrsMap &PrebuiltModulesASTMap, - const HeaderSearchOptions &HSOpts, - const LangOptions &LangOpts, DiagnosticsEngine &Diags, - const ArrayRef<StringRef> StableDirs) - : PrebuiltModuleFiles(PrebuiltModuleFiles), - NewModuleFiles(NewModuleFiles), - PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts), - ExistingLangOpts(LangOpts), Diags(Diags), StableDirs(StableDirs) {} - - bool needsImportVisitation() const override { return true; } - bool needsInputFileVisitation() override { return true; } - bool needsSystemInputFileVisitation() override { return true; } - - /// Accumulate the modules are transitively depended on by the initial - /// prebuilt module. - void visitImport(StringRef ModuleName, StringRef Filename) override { - if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second) - NewModuleFiles.push_back(Filename.str()); - - auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(Filename); - PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second; - if (PrebuiltMapEntry.second) - PrebuiltModule.setInStableDir(!StableDirs.empty()); - - if (auto It = PrebuiltModulesASTMap.find(CurrentFile); - It != PrebuiltModulesASTMap.end() && CurrentFile != Filename) - PrebuiltModule.addDependent(It->getKey()); - } - - /// For each input file discovered, check whether it's external path is in a - /// stable directory. Traversal is stopped if the current module is not - /// considered stable. - bool visitInputFileAsRequested(StringRef FilenameAsRequested, - StringRef Filename, bool isSystem, - bool isOverridden, - bool isExplicitModule) override { - if (StableDirs.empty()) - return false; - auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile); - if ((PrebuiltEntryIt == PrebuiltModulesASTMap.end()) || - (!PrebuiltEntryIt->second.isInStableDir())) - return false; - - PrebuiltEntryIt->second.setInStableDir( - isPathInStableDir(StableDirs, Filename)); - return PrebuiltEntryIt->second.isInStableDir(); - } - - /// Update which module that is being actively traversed. - void visitModuleFile(StringRef Filename, - serialization::ModuleKind Kind) override { - // If the CurrentFile is not - // considered stable, update any of it's transitive dependents. - auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile); - if ((PrebuiltEntryIt != PrebuiltModulesASTMap.end()) && - !PrebuiltEntryIt->second.isInStableDir()) - PrebuiltEntryIt->second.updateDependentsNotInStableDirs( - PrebuiltModulesASTMap); - CurrentFile = Filename; - } - - /// 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, - bool Complain) override { - - auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile); - PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second; - if (PrebuiltMapEntry.second) - PrebuiltModule.setInStableDir(!StableDirs.empty()); - - if (PrebuiltModule.isInStableDir()) - PrebuiltModule.setInStableDir(areOptionsInStableDir(StableDirs, HSOpts)); - - return false; - } - - /// Accumulate vfsoverlays used to build these prebuilt modules. - bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts, - bool Complain) override { - - auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile); - PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second; - if (PrebuiltMapEntry.second) - PrebuiltModule.setInStableDir(!StableDirs.empty()); - - PrebuiltModule.setVFS( - llvm::StringSet<>(llvm::from_range, HSOpts.VFSOverlayFiles)); - - return checkHeaderSearchPaths( - HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts); - } - -private: - PrebuiltModuleFilesT &PrebuiltModuleFiles; - llvm::SmallVector<std::string> &NewModuleFiles; - PrebuiltModulesAttrsMap &PrebuiltModulesASTMap; - const HeaderSearchOptions &ExistingHSOpts; - const LangOptions &ExistingLangOpts; - DiagnosticsEngine &Diags; - std::string CurrentFile; - const ArrayRef<StringRef> StableDirs; -}; - -/// Visit the given prebuilt module and collect all of the modules it -/// transitively imports and contributing input files. -static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename, - CompilerInstance &CI, - PrebuiltModuleFilesT &ModuleFiles, - PrebuiltModulesAttrsMap &PrebuiltModulesASTMap, - DiagnosticsEngine &Diags, - const ArrayRef<StringRef> StableDirs) { - // List of module files to be processed. - llvm::SmallVector<std::string> Worklist; - - PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModulesASTMap, - CI.getHeaderSearchOpts(), CI.getLangOpts(), - Diags, StableDirs); - - Listener.visitModuleFile(PrebuiltModuleFilename, - serialization::MK_ExplicitModule); - if (ASTReader::readASTFileControlBlock( - PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(), - CI.getPCHContainerReader(), - /*FindModuleFileExtensions=*/false, Listener, - /*ValidateDiagnosticOptions=*/false, ASTReader::ARR_OutOfDate)) - return true; - - while (!Worklist.empty()) { - Listener.visitModuleFile(Worklist.back(), serialization::MK_ExplicitModule); - 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. -static std::string makeObjFileName(StringRef FileName) { - SmallString<128> ObjFileName(FileName); - llvm::sys::path::replace_extension(ObjFileName, "o"); - return std::string(ObjFileName); -} - -/// Deduce the dependency target based on the output file and input files. -static std::string -deduceDepTarget(const std::string &OutputFile, - const SmallVectorImpl<FrontendInputFile> &InputFiles) { - if (OutputFile != "-") - return OutputFile; - - if (InputFiles.empty() || !InputFiles.front().isFile()) - return "clang-scan-deps\\ dependency"; - - return makeObjFileName(InputFiles.front().getFile()); -} - -// Clang implements -D and -U by splatting text into a predefines buffer. This -// allows constructs such as `-DFඞ=3 "-D F\u{0D9E} 4 3 2”` to be accepted and -// define the same macro, or adding C++ style comments before the macro name. -// -// This function checks that the first non-space characters in the macro -// obviously form an identifier that can be uniqued on without lexing. Failing -// to do this could lead to changing the final definition of a macro. -// -// We could set up a preprocessor and actually lex the name, but that's very -// heavyweight for a situation that will almost never happen in practice. -static std::optional<StringRef> getSimpleMacroName(StringRef Macro) { - StringRef Name = Macro.split("=").first.ltrim(" \t"); - std::size_t I = 0; - - auto FinishName = [&]() -> std::optional<StringRef> { - StringRef SimpleName = Name.slice(0, I); - if (SimpleName.empty()) - return std::nullopt; - return SimpleName; - }; - - for (; I != Name.size(); ++I) { - switch (Name[I]) { - case '(': // Start of macro parameter list - case ' ': // End of macro name - case '\t': - return FinishName(); - case '_': - continue; - default: - if (llvm::isAlnum(Name[I])) - continue; - return std::nullopt; - } - } - return FinishName(); -} - -static void canonicalizeDefines(PreprocessorOptions &PPOpts) { - using MacroOpt = std::pair<StringRef, std::size_t>; - std::vector<MacroOpt> SimpleNames; - SimpleNames.reserve(PPOpts.Macros.size()); - std::size_t Index = 0; - for (const auto &M : PPOpts.Macros) { - auto SName = getSimpleMacroName(M.first); - // Skip optimizing if we can't guarantee we can preserve relative order. - if (!SName) - return; - SimpleNames.emplace_back(*SName, Index); - ++Index; - } - - llvm::stable_sort(SimpleNames, llvm::less_first()); - // Keep the last instance of each macro name by going in reverse - auto NewEnd = std::unique( - SimpleNames.rbegin(), SimpleNames.rend(), - [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; }); - SimpleNames.erase(SimpleNames.begin(), NewEnd.base()); - - // Apply permutation. - decltype(PPOpts.Macros) NewMacros; - NewMacros.reserve(SimpleNames.size()); - for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) { - std::size_t OriginalIndex = SimpleNames[I].second; - // We still emit undefines here as they may be undefining a predefined macro - NewMacros.push_back(std::move(PPOpts.Macros[OriginalIndex])); - } - std::swap(PPOpts.Macros, NewMacros); -} - -class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter { - DependencyScanningWorkerFilesystem *DepFS; - -public: - ScanningDependencyDirectivesGetter(FileManager &FileMgr) : DepFS(nullptr) { - FileMgr.getVirtualFileSystem().visit([&](llvm::vfs::FileSystem &FS) { - auto *DFS = llvm::dyn_cast<DependencyScanningWorkerFilesystem>(&FS); - if (DFS) { - assert(!DepFS && "Found multiple scanning VFSs"); - DepFS = DFS; - } - }); - assert(DepFS && "Did not find scanning VFS"); - } - - std::unique_ptr<DependencyDirectivesGetter> - cloneFor(FileManager &FileMgr) override { - return std::make_unique<ScanningDependencyDirectivesGetter>(FileMgr); - } - - std::optional<ArrayRef<dependency_directives_scan::Directive>> - operator()(FileEntryRef File) override { - return DepFS->getDirectiveTokens(File.getName()); - } -}; - -/// Sanitize diagnostic options for dependency scan. -void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) { - // Don't print 'X warnings and Y errors generated'. - DiagOpts.ShowCarets = false; - // Don't write out diagnostic file. - DiagOpts.DiagnosticSerializationFile.clear(); - // Don't emit warnings except for scanning specific warnings. - // TODO: It would be useful to add a more principled way to ignore all - // warnings that come from source code. The issue is that we need to - // ignore warnings that could be surpressed by - // `#pragma clang diagnostic`, while still allowing some scanning - // warnings for things we're not ready to turn into errors yet. - // See `test/ClangScanDeps/diagnostic-pragmas.c` for an example. - llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) { - return llvm::StringSwitch<bool>(Warning) - .Cases({"pch-vfs-diff", "error=pch-vfs-diff"}, false) - .StartsWith("no-error=", false) - .Default(true); - }); -} -} // namespace - -namespace clang::tooling::dependencies { -std::unique_ptr<DiagnosticOptions> -createDiagOptions(ArrayRef<std::string> CommandLine) { - std::vector<const char *> CLI; - for (const std::string &Arg : CommandLine) - CLI.push_back(Arg.c_str()); - auto DiagOpts = CreateAndPopulateDiagOpts(CLI); - sanitizeDiagOpts(*DiagOpts); - return DiagOpts; -} - -DignosticsEngineWithDiagOpts::DignosticsEngineWithDiagOpts( - ArrayRef<std::string> CommandLine, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC) { - std::vector<const char *> CCommandLine(CommandLine.size(), nullptr); - llvm::transform(CommandLine, CCommandLine.begin(), - [](const std::string &Str) { return Str.c_str(); }); - DiagOpts = CreateAndPopulateDiagOpts(CCommandLine); - sanitizeDiagOpts(*DiagOpts); - DiagEngine = CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DC, - /*ShouldOwnClient=*/false); -} - -std::pair<std::unique_ptr<driver::Driver>, std::unique_ptr<driver::Compilation>> -buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - llvm::BumpPtrAllocator &Alloc) { - SmallVector<const char *, 256> Argv; - Argv.reserve(ArgStrs.size()); - for (const std::string &Arg : ArgStrs) - Argv.push_back(Arg.c_str()); - - std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>( - Argv[0], llvm::sys::getDefaultTargetTriple(), Diags, - "clang LLVM compiler", FS); - Driver->setTitle("clang_based_tool"); - - bool CLMode = driver::IsClangCL( - driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1))); - - if (llvm::Error E = - driver::expandResponseFiles(Argv, CLMode, Alloc, FS.get())) { - Diags.Report(diag::err_drv_expand_response_file) - << llvm::toString(std::move(E)); - return std::make_pair(nullptr, nullptr); - } - - std::unique_ptr<driver::Compilation> Compilation( - Driver->BuildCompilation(Argv)); - if (!Compilation) - return std::make_pair(nullptr, nullptr); - - if (Compilation->containsError()) - return std::make_pair(nullptr, nullptr); - - return std::make_pair(std::move(Driver), std::move(Compilation)); -} - -std::unique_ptr<CompilerInvocation> -createCompilerInvocation(ArrayRef<std::string> CommandLine, - DiagnosticsEngine &Diags) { - llvm::opt::ArgStringList Argv; - for (const std::string &Str : ArrayRef(CommandLine).drop_front()) - Argv.push_back(Str.c_str()); - - auto Invocation = std::make_unique<CompilerInvocation>(); - if (!CompilerInvocation::CreateFromArgs(*Invocation, Argv, Diags)) { - // FIXME: Should we just go on like cc1_main does? - return nullptr; - } - return Invocation; -} - -std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>> -initVFSForTUBuferScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, - ArrayRef<std::string> CommandLine, - StringRef WorkingDirectory, - llvm::MemoryBufferRef TUBuffer) { - // Reset what might have been modified in the previous worker invocation. - BaseFS->setCurrentWorkingDirectory(WorkingDirectory); - - IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS; - auto OverlayFS = - llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS); - auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); - InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory); - auto InputPath = TUBuffer.getBufferIdentifier(); - InMemoryFS->addFile( - InputPath, 0, llvm::MemoryBuffer::getMemBufferCopy(TUBuffer.getBuffer())); - IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS; - - OverlayFS->pushOverlay(InMemoryOverlay); - ModifiedFS = OverlayFS; - std::vector<std::string> ModifiedCommandLine(CommandLine); - ModifiedCommandLine.emplace_back(InputPath); - - return std::make_pair(ModifiedFS, ModifiedCommandLine); -} - -std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>> -initVFSForByNameScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, - ArrayRef<std::string> CommandLine, - StringRef WorkingDirectory, StringRef ModuleName) { - // Reset what might have been modified in the previous worker invocation. - BaseFS->setCurrentWorkingDirectory(WorkingDirectory); - - // If we're scanning based on a module name alone, we don't expect the client - // to provide us with an input file. However, the driver really wants to have - // one. Let's just make it up to make the driver happy. - auto OverlayFS = - llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS); - auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); - InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory); - SmallString<128> FakeInputPath; - // TODO: We should retry the creation if the path already exists. - llvm::sys::fs::createUniquePath(ModuleName + "-%%%%%%%%.input", FakeInputPath, - /*MakeAbsolute=*/false); - InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer("")); - IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS; - OverlayFS->pushOverlay(InMemoryOverlay); - - std::vector<std::string> ModifiedCommandLine(CommandLine); - ModifiedCommandLine.emplace_back(FakeInputPath); - - return std::make_pair(OverlayFS, ModifiedCommandLine); -} - -bool initializeScanCompilerInstance( - CompilerInstance &ScanInstance, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service, - IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS) { - ScanInstance.setBuildingModule(false); - - ScanInstance.createVirtualFileSystem(FS, DiagConsumer); - - // Create the compiler's actual diagnostics engine. - sanitizeDiagOpts(ScanInstance.getDiagnosticOpts()); - ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false); - if (!ScanInstance.hasDiagnostics()) - return false; - - ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath = - true; - - if (ScanInstance.getHeaderSearchOpts().ModulesValidateOncePerBuildSession) - ScanInstance.getHeaderSearchOpts().BuildSessionTimestamp = - Service.getBuildSessionTimestamp(); - - ScanInstance.getFrontendOpts().DisableFree = false; - ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false; - ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false; - ScanInstance.getFrontendOpts().GenReducedBMI = false; - ScanInstance.getFrontendOpts().ModuleOutputPath.clear(); - // This will prevent us compiling individual modules asynchronously since - // FileManager is not thread-safe, but it does improve performance for now. - ScanInstance.getFrontendOpts().ModulesShareFileManager = true; - ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw"; - ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage = - any(Service.getOptimizeArgs() & ScanningOptimizations::VFS); - - // Create a new FileManager to match the invocation's FileSystemOptions. - ScanInstance.createFileManager(); - - // Use the dependency scanning optimized file system if requested to do so. - if (DepFS) { - DepFS->resetBypassedPathPrefix(); - SmallString<256> ModulesCachePath; - normalizeModuleCachePath(ScanInstance.getFileManager(), - ScanInstance.getHeaderSearchOpts().ModuleCachePath, - ModulesCachePath); - if (!ModulesCachePath.empty()) - DepFS->setBypassedPathPrefix(ModulesCachePath); - - ScanInstance.setDependencyDirectivesGetter( - std::make_unique<ScanningDependencyDirectivesGetter>( - ScanInstance.getFileManager())); - } - - ScanInstance.createSourceManager(); - - // Consider different header search and diagnostic options to create - // different modules. This avoids the unsound aliasing of module PCMs. - // - // TODO: Implement diagnostic bucketing to reduce the impact of strict - // context hashing. - ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true; - ScanInstance.getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true; - ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true; - ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true; - ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings = true; - ScanInstance.getHeaderSearchOpts().ModulesForceValidateUserHeaders = false; - - // Avoid some checks and module map parsing when loading PCM files. - ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false; - - return true; -} - -llvm::SmallVector<StringRef> -getInitialStableDirs(const CompilerInstance &ScanInstance) { - // Create a collection of stable directories derived from the ScanInstance - // for determining whether module dependencies would fully resolve from - // those directories. - llvm::SmallVector<StringRef> StableDirs; - const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot; - if (!Sysroot.empty() && (llvm::sys::path::root_directory(Sysroot) != Sysroot)) - StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts().ResourceDir}; - return StableDirs; -} - -std::optional<PrebuiltModulesAttrsMap> -computePrebuiltModulesASTMap(CompilerInstance &ScanInstance, - llvm::SmallVector<StringRef> &StableDirs) { - // Store a mapping of prebuilt module files and their properties like header - // search options. This will prevent the implicit build to create duplicate - // modules and will force reuse of the existing prebuilt module files - // instead. - PrebuiltModulesAttrsMap PrebuiltModulesASTMap; - - if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty()) - if (visitPrebuiltModule( - ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance, - ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles, - PrebuiltModulesASTMap, ScanInstance.getDiagnostics(), StableDirs)) - return {}; - - return PrebuiltModulesASTMap; -} - -std::unique_ptr<DependencyOutputOptions> -takeDependencyOutputOptionsFrom(CompilerInstance &ScanInstance) { - // This function moves the existing dependency output options from the - // invocation to the collector. The options in the invocation are reset, - // which ensures that the compiler won't create new dependency collectors, - // and thus won't write out the extra '.d' files to disk. - auto Opts = std::make_unique<DependencyOutputOptions>(); - std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts()); - // We need at least one -MT equivalent for the generator of make dependency - // files to work. - if (Opts->Targets.empty()) - Opts->Targets = {deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile, - ScanInstance.getFrontendOpts().Inputs)}; - Opts->IncludeSystemHeaders = true; - - return Opts; -} - -std::shared_ptr<ModuleDepCollector> initializeScanInstanceDependencyCollector( - CompilerInstance &ScanInstance, - std::unique_ptr<DependencyOutputOptions> DepOutputOpts, - StringRef WorkingDirectory, DependencyConsumer &Consumer, - DependencyScanningService &Service, CompilerInvocation &Inv, - DependencyActionController &Controller, - PrebuiltModulesAttrsMap PrebuiltModulesASTMap, - llvm::SmallVector<StringRef> &StableDirs) { - std::shared_ptr<ModuleDepCollector> MDC; - switch (Service.getFormat()) { - case ScanningOutputFormat::Make: - ScanInstance.addDependencyCollector( - std::make_shared<DependencyConsumerForwarder>( - std::move(DepOutputOpts), WorkingDirectory, Consumer)); - break; - case ScanningOutputFormat::P1689: - case ScanningOutputFormat::Full: - MDC = std::make_shared<ModuleDepCollector>( - Service, std::move(DepOutputOpts), ScanInstance, Consumer, Controller, - Inv, std::move(PrebuiltModulesASTMap), StableDirs); - ScanInstance.addDependencyCollector(MDC); - break; - } - - return MDC; -} -} // namespace clang::tooling::dependencies - -bool DependencyScanningAction::runInvocation( - std::unique_ptr<CompilerInvocation> Invocation, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - std::shared_ptr<PCHContainerOperations> PCHContainerOps, - DiagnosticConsumer *DiagConsumer) { - // Making sure that we canonicalize the defines before we create the deep - // copy to avoid unnecessary variants in the scanner and in the resulting - // explicit command lines. - if (any(Service.getOptimizeArgs() & ScanningOptimizations::Macros)) - canonicalizeDefines(Invocation->getPreprocessorOpts()); - - // Make a deep copy of the original Clang invocation. - CompilerInvocation OriginalInvocation(*Invocation); - - if (Scanned) { - // Scanning runs once for the first -cc1 invocation in a chain of driver - // jobs. For any dependent jobs, reuse the scanning result and just - // update the LastCC1Arguments to correspond to the new invocation. - // FIXME: to support multi-arch builds, each arch requires a separate scan - setLastCC1Arguments(std::move(OriginalInvocation)); - return true; - } - - Scanned = true; - - // Create a compiler instance to handle the actual work. - auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries()); - ScanInstanceStorage.emplace(std::move(Invocation), std::move(PCHContainerOps), - ModCache.get()); - CompilerInstance &ScanInstance = *ScanInstanceStorage; - - assert(!DiagConsumerFinished && "attempt to reuse finished consumer"); - if (!initializeScanCompilerInstance(ScanInstance, FS, DiagConsumer, Service, - DepFS)) - return false; - - llvm::SmallVector<StringRef> StableDirs = getInitialStableDirs(ScanInstance); - auto MaybePrebuiltModulesASTMap = - computePrebuiltModulesASTMap(ScanInstance, StableDirs); - if (!MaybePrebuiltModulesASTMap) - return false; - - auto DepOutputOpts = takeDependencyOutputOptionsFrom(ScanInstance); - - MDC = initializeScanInstanceDependencyCollector( - ScanInstance, std::move(DepOutputOpts), WorkingDirectory, Consumer, - Service, OriginalInvocation, Controller, *MaybePrebuiltModulesASTMap, - StableDirs); - - std::unique_ptr<FrontendAction> Action; - - if (Service.getFormat() == ScanningOutputFormat::P1689) - Action = std::make_unique<PreprocessOnlyAction>(); - else if (ModuleName) - Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName); - else - Action = std::make_unique<ReadPCHAndPreprocessAction>(); - - if (ScanInstance.getDiagnostics().hasErrorOccurred()) - return false; - - const bool Result = ScanInstance.ExecuteAction(*Action); - - // ExecuteAction is responsible for calling finish. - DiagConsumerFinished = true; - - if (Result) - setLastCC1Arguments(std::move(OriginalInvocation)); - - return Result; -} diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.h b/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.h deleted file mode 100644 index 5657317..0000000 --- a/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.h +++ /dev/null @@ -1,157 +0,0 @@ -//===- DependencyScanner.h - Performs module dependency scanning *- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNER_H -#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNER_H - -#include "clang/Driver/Compilation.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/CompilerInvocation.h" -#include "clang/Frontend/TextDiagnosticPrinter.h" -#include "clang/Serialization/ObjectFilePCHContainerReader.h" -#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h" -#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" - -namespace clang { -class DiagnosticConsumer; - -namespace tooling { -namespace dependencies { -class DependencyScanningService; -class DependencyConsumer; -class DependencyActionController; -class DependencyScanningWorkerFilesystem; - -class DependencyScanningAction { -public: - DependencyScanningAction( - DependencyScanningService &Service, StringRef WorkingDirectory, - DependencyConsumer &Consumer, DependencyActionController &Controller, - IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS, - std::optional<StringRef> ModuleName = std::nullopt) - : Service(Service), WorkingDirectory(WorkingDirectory), - Consumer(Consumer), Controller(Controller), DepFS(std::move(DepFS)), - ModuleName(ModuleName) {} - bool runInvocation(std::unique_ptr<CompilerInvocation> Invocation, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - std::shared_ptr<PCHContainerOperations> PCHContainerOps, - DiagnosticConsumer *DiagConsumer); - - bool hasScanned() const { return Scanned; } - bool hasDiagConsumerFinished() const { return DiagConsumerFinished; } - - /// Take the cc1 arguments corresponding to the most recent invocation used - /// with this action. Any modifications implied by the discovered dependencies - /// will have already been applied. - std::vector<std::string> takeLastCC1Arguments() { - std::vector<std::string> Result; - std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty. - return Result; - } - -private: - void setLastCC1Arguments(CompilerInvocation &&CI) { - if (MDC) - MDC->applyDiscoveredDependencies(CI); - LastCC1Arguments = CI.getCC1CommandLine(); - } - - DependencyScanningService &Service; - StringRef WorkingDirectory; - DependencyConsumer &Consumer; - DependencyActionController &Controller; - IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS; - std::optional<StringRef> ModuleName; - std::optional<CompilerInstance> ScanInstanceStorage; - std::shared_ptr<ModuleDepCollector> MDC; - std::vector<std::string> LastCC1Arguments; - bool Scanned = false; - bool DiagConsumerFinished = false; -}; - -// Helper functions and data types. -std::unique_ptr<DiagnosticOptions> -createDiagOptions(ArrayRef<std::string> CommandLine); - -struct DignosticsEngineWithDiagOpts { - // We need to bound the lifetime of the DiagOpts used to create the - // DiganosticsEngine with the DiagnosticsEngine itself. - std::unique_ptr<DiagnosticOptions> DiagOpts; - IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine; - - DignosticsEngineWithDiagOpts(ArrayRef<std::string> CommandLine, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - DiagnosticConsumer &DC); -}; - -struct TextDiagnosticsPrinterWithOutput { - // We need to bound the lifetime of the data that supports the DiagPrinter - // with it together so they have the same lifetime. - std::string DiagnosticOutput; - llvm::raw_string_ostream DiagnosticsOS; - std::unique_ptr<DiagnosticOptions> DiagOpts; - TextDiagnosticPrinter DiagPrinter; - - TextDiagnosticsPrinterWithOutput(ArrayRef<std::string> CommandLine) - : DiagnosticsOS(DiagnosticOutput), - DiagOpts(createDiagOptions(CommandLine)), - DiagPrinter(DiagnosticsOS, *DiagOpts) {} -}; - -std::pair<std::unique_ptr<driver::Driver>, std::unique_ptr<driver::Compilation>> -buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - llvm::BumpPtrAllocator &Alloc); - -std::unique_ptr<CompilerInvocation> -createCompilerInvocation(ArrayRef<std::string> CommandLine, - DiagnosticsEngine &Diags); - -std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>> -initVFSForTUBuferScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, - ArrayRef<std::string> CommandLine, - StringRef WorkingDirectory, - llvm::MemoryBufferRef TUBuffer); - -std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>> -initVFSForByNameScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS, - ArrayRef<std::string> CommandLine, - StringRef WorkingDirectory, StringRef ModuleName); - -bool initializeScanCompilerInstance( - CompilerInstance &ScanInstance, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service, - IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS); - -SmallVector<StringRef> -getInitialStableDirs(const CompilerInstance &ScanInstance); - -std::optional<PrebuiltModulesAttrsMap> -computePrebuiltModulesASTMap(CompilerInstance &ScanInstance, - SmallVector<StringRef> &StableDirs); - -std::unique_ptr<DependencyOutputOptions> -takeDependencyOutputOptionsFrom(CompilerInstance &ScanInstance); - -/// Create the dependency collector that will collect the produced -/// dependencies. May return the created ModuleDepCollector depending -/// on the scanning format. -std::shared_ptr<ModuleDepCollector> initializeScanInstanceDependencyCollector( - CompilerInstance &ScanInstance, - std::unique_ptr<DependencyOutputOptions> DepOutputOpts, - StringRef WorkingDirectory, DependencyConsumer &Consumer, - DependencyScanningService &Service, CompilerInvocation &Inv, - DependencyActionController &Controller, - PrebuiltModulesAttrsMap PrebuiltModulesASTMap, - llvm::SmallVector<StringRef> &StableDirs); -} // namespace dependencies -} // namespace tooling -} // namespace clang - -#endif diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp deleted file mode 100644 index b641e4a..0000000 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp +++ /dev/null @@ -1,504 +0,0 @@ -//===- DependencyScanningFilesystem.cpp - clang-scan-deps fs --------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Threading.h" -#include <optional> - -using namespace clang; -using namespace tooling; -using namespace dependencies; - -llvm::ErrorOr<DependencyScanningWorkerFilesystem::TentativeEntry> -DependencyScanningWorkerFilesystem::readFile(StringRef Filename) { - // Load the file and its content from the file system. - auto MaybeFile = getUnderlyingFS().openFileForRead(Filename); - if (!MaybeFile) - return MaybeFile.getError(); - auto File = std::move(*MaybeFile); - - auto MaybeStat = File->status(); - if (!MaybeStat) - return MaybeStat.getError(); - auto Stat = std::move(*MaybeStat); - - auto MaybeBuffer = File->getBuffer(Stat.getName()); - if (!MaybeBuffer) - return MaybeBuffer.getError(); - auto Buffer = std::move(*MaybeBuffer); - - // If the file size changed between read and stat, pretend it didn't. - if (Stat.getSize() != Buffer->getBufferSize()) - Stat = llvm::vfs::Status::copyWithNewSize(Stat, Buffer->getBufferSize()); - - return TentativeEntry(Stat, std::move(Buffer)); -} - -bool DependencyScanningWorkerFilesystem::ensureDirectiveTokensArePopulated( - EntryRef Ref) { - auto &Entry = Ref.Entry; - - if (Entry.isError() || Entry.isDirectory()) - return false; - - CachedFileContents *Contents = Entry.getCachedContents(); - assert(Contents && "contents not initialized"); - - // Double-checked locking. - if (Contents->DepDirectives.load()) - return true; - - std::lock_guard<std::mutex> GuardLock(Contents->ValueLock); - - // Double-checked locking. - if (Contents->DepDirectives.load()) - return true; - - SmallVector<dependency_directives_scan::Directive, 64> Directives; - // Scan the file for preprocessor directives that might affect the - // dependencies. - if (scanSourceForDependencyDirectives(Contents->Original->getBuffer(), - Contents->DepDirectiveTokens, - Directives)) { - Contents->DepDirectiveTokens.clear(); - // FIXME: Propagate the diagnostic if desired by the client. - Contents->DepDirectives.store(new std::optional<DependencyDirectivesTy>()); - return false; - } - - // This function performed double-checked locking using `DepDirectives`. - // Assigning it must be the last thing this function does, otherwise other - // threads may skip the critical section (`DepDirectives != nullptr`), leading - // to a data race. - Contents->DepDirectives.store( - new std::optional<DependencyDirectivesTy>(std::move(Directives))); - return true; -} - -DependencyScanningFilesystemSharedCache:: - DependencyScanningFilesystemSharedCache() { - // This heuristic was chosen using a empirical testing on a - // reasonably high core machine (iMacPro 18 cores / 36 threads). The cache - // sharding gives a performance edge by reducing the lock contention. - // FIXME: A better heuristic might also consider the OS to account for - // the different cost of lock contention on different OSes. - NumShards = - std::max(2u, llvm::hardware_concurrency().compute_thread_count() / 4); - CacheShards = std::make_unique<CacheShard[]>(NumShards); -} - -DependencyScanningFilesystemSharedCache::CacheShard & -DependencyScanningFilesystemSharedCache::getShardForFilename( - StringRef Filename) const { - assert(llvm::sys::path::is_absolute_gnu(Filename)); - return CacheShards[llvm::hash_value(Filename) % NumShards]; -} - -DependencyScanningFilesystemSharedCache::CacheShard & -DependencyScanningFilesystemSharedCache::getShardForUID( - llvm::sys::fs::UniqueID UID) const { - auto Hash = llvm::hash_combine(UID.getDevice(), UID.getFile()); - return CacheShards[Hash % NumShards]; -} - -std::vector<DependencyScanningFilesystemSharedCache::OutOfDateEntry> -DependencyScanningFilesystemSharedCache::getOutOfDateEntries( - llvm::vfs::FileSystem &UnderlyingFS) const { - // Iterate through all shards and look for cached stat errors. - std::vector<OutOfDateEntry> InvalidDiagInfo; - for (unsigned i = 0; i < NumShards; i++) { - const CacheShard &Shard = CacheShards[i]; - std::lock_guard<std::mutex> LockGuard(Shard.CacheLock); - for (const auto &[Path, CachedPair] : Shard.CacheByFilename) { - const CachedFileSystemEntry *Entry = CachedPair.first; - llvm::ErrorOr<llvm::vfs::Status> Status = UnderlyingFS.status(Path); - if (Status) { - if (Entry->getError()) { - // This is the case where we have cached the non-existence - // of the file at Path first, and a file at the path is created - // later. The cache entry is not invalidated (as we have no good - // way to do it now), which may lead to missing file build errors. - InvalidDiagInfo.emplace_back(Path.data()); - } else { - llvm::vfs::Status CachedStatus = Entry->getStatus(); - if (Status->getType() == llvm::sys::fs::file_type::regular_file && - Status->getType() == CachedStatus.getType()) { - // We only check regular files. Directory files sizes could change - // due to content changes, and reporting directory size changes can - // lead to false positives. - // TODO: At the moment, we do not detect symlinks to files whose - // size may change. We need to decide if we want to detect cached - // symlink size changes. We can also expand this to detect file - // type changes. - uint64_t CachedSize = CachedStatus.getSize(); - uint64_t ActualSize = Status->getSize(); - if (CachedSize != ActualSize) { - // This is the case where the cached file has a different size - // from the actual file that comes from the underlying FS. - InvalidDiagInfo.emplace_back(Path.data(), CachedSize, ActualSize); - } - } - } - } - } - } - return InvalidDiagInfo; -} - -const CachedFileSystemEntry * -DependencyScanningFilesystemSharedCache::CacheShard::findEntryByFilename( - StringRef Filename) const { - assert(llvm::sys::path::is_absolute_gnu(Filename)); - std::lock_guard<std::mutex> LockGuard(CacheLock); - auto It = CacheByFilename.find(Filename); - return It == CacheByFilename.end() ? nullptr : It->getValue().first; -} - -const CachedFileSystemEntry * -DependencyScanningFilesystemSharedCache::CacheShard::findEntryByUID( - llvm::sys::fs::UniqueID UID) const { - std::lock_guard<std::mutex> LockGuard(CacheLock); - auto It = EntriesByUID.find(UID); - return It == EntriesByUID.end() ? nullptr : It->getSecond(); -} - -const CachedFileSystemEntry & -DependencyScanningFilesystemSharedCache::CacheShard:: - getOrEmplaceEntryForFilename(StringRef Filename, - llvm::ErrorOr<llvm::vfs::Status> Stat) { - std::lock_guard<std::mutex> LockGuard(CacheLock); - auto [It, Inserted] = CacheByFilename.insert({Filename, {nullptr, nullptr}}); - auto &[CachedEntry, CachedRealPath] = It->getValue(); - if (!CachedEntry) { - // The entry is not present in the shared cache. Either the cache doesn't - // know about the file at all, or it only knows about its real path. - assert((Inserted || CachedRealPath) && "existing file with empty pair"); - CachedEntry = - new (EntryStorage.Allocate()) CachedFileSystemEntry(std::move(Stat)); - } - return *CachedEntry; -} - -const CachedFileSystemEntry & -DependencyScanningFilesystemSharedCache::CacheShard::getOrEmplaceEntryForUID( - llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat, - std::unique_ptr<llvm::MemoryBuffer> Contents) { - std::lock_guard<std::mutex> LockGuard(CacheLock); - auto [It, Inserted] = EntriesByUID.try_emplace(UID); - auto &CachedEntry = It->getSecond(); - if (Inserted) { - CachedFileContents *StoredContents = nullptr; - if (Contents) - StoredContents = new (ContentsStorage.Allocate()) - CachedFileContents(std::move(Contents)); - CachedEntry = new (EntryStorage.Allocate()) - CachedFileSystemEntry(std::move(Stat), StoredContents); - } - return *CachedEntry; -} - -const CachedFileSystemEntry & -DependencyScanningFilesystemSharedCache::CacheShard:: - getOrInsertEntryForFilename(StringRef Filename, - const CachedFileSystemEntry &Entry) { - std::lock_guard<std::mutex> LockGuard(CacheLock); - auto [It, Inserted] = CacheByFilename.insert({Filename, {&Entry, nullptr}}); - auto &[CachedEntry, CachedRealPath] = It->getValue(); - if (!Inserted || !CachedEntry) - CachedEntry = &Entry; - return *CachedEntry; -} - -const CachedRealPath * -DependencyScanningFilesystemSharedCache::CacheShard::findRealPathByFilename( - StringRef Filename) const { - assert(llvm::sys::path::is_absolute_gnu(Filename)); - std::lock_guard<std::mutex> LockGuard(CacheLock); - auto It = CacheByFilename.find(Filename); - return It == CacheByFilename.end() ? nullptr : It->getValue().second; -} - -const CachedRealPath &DependencyScanningFilesystemSharedCache::CacheShard:: - getOrEmplaceRealPathForFilename(StringRef Filename, - llvm::ErrorOr<llvm::StringRef> RealPath) { - std::lock_guard<std::mutex> LockGuard(CacheLock); - - const CachedRealPath *&StoredRealPath = CacheByFilename[Filename].second; - if (!StoredRealPath) { - auto OwnedRealPath = [&]() -> CachedRealPath { - if (!RealPath) - return RealPath.getError(); - return RealPath->str(); - }(); - - StoredRealPath = new (RealPathStorage.Allocate()) - CachedRealPath(std::move(OwnedRealPath)); - } - - return *StoredRealPath; -} - -bool DependencyScanningWorkerFilesystem::shouldBypass(StringRef Path) const { - return BypassedPathPrefix && Path.starts_with(*BypassedPathPrefix); -} - -DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem( - DependencyScanningFilesystemSharedCache &SharedCache, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) - : llvm::RTTIExtends<DependencyScanningWorkerFilesystem, - llvm::vfs::ProxyFileSystem>(std::move(FS)), - SharedCache(SharedCache), - WorkingDirForCacheLookup(llvm::errc::invalid_argument) { - updateWorkingDirForCacheLookup(); -} - -const CachedFileSystemEntry & -DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID( - TentativeEntry TEntry) { - auto &Shard = SharedCache.getShardForUID(TEntry.Status.getUniqueID()); - return Shard.getOrEmplaceEntryForUID(TEntry.Status.getUniqueID(), - std::move(TEntry.Status), - std::move(TEntry.Contents)); -} - -const CachedFileSystemEntry * -DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough( - StringRef Filename) { - if (const auto *Entry = LocalCache.findEntryByFilename(Filename)) - return Entry; - auto &Shard = SharedCache.getShardForFilename(Filename); - if (const auto *Entry = Shard.findEntryByFilename(Filename)) - return &LocalCache.insertEntryForFilename(Filename, *Entry); - return nullptr; -} - -llvm::ErrorOr<const CachedFileSystemEntry &> -DependencyScanningWorkerFilesystem::computeAndStoreResult( - StringRef OriginalFilename, StringRef FilenameForLookup) { - llvm::ErrorOr<llvm::vfs::Status> Stat = - getUnderlyingFS().status(OriginalFilename); - if (!Stat) { - const auto &Entry = - getOrEmplaceSharedEntryForFilename(FilenameForLookup, Stat.getError()); - return insertLocalEntryForFilename(FilenameForLookup, Entry); - } - - if (const auto *Entry = findSharedEntryByUID(*Stat)) - return insertLocalEntryForFilename(FilenameForLookup, *Entry); - - auto TEntry = - Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(OriginalFilename); - - const CachedFileSystemEntry *SharedEntry = [&]() { - if (TEntry) { - const auto &UIDEntry = getOrEmplaceSharedEntryForUID(std::move(*TEntry)); - return &getOrInsertSharedEntryForFilename(FilenameForLookup, UIDEntry); - } - return &getOrEmplaceSharedEntryForFilename(FilenameForLookup, - TEntry.getError()); - }(); - - return insertLocalEntryForFilename(FilenameForLookup, *SharedEntry); -} - -llvm::ErrorOr<EntryRef> -DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry( - StringRef OriginalFilename) { - SmallString<256> PathBuf; - auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf); - if (!FilenameForLookup) - return FilenameForLookup.getError(); - - if (const auto *Entry = - findEntryByFilenameWithWriteThrough(*FilenameForLookup)) - return EntryRef(OriginalFilename, *Entry).unwrapError(); - auto MaybeEntry = computeAndStoreResult(OriginalFilename, *FilenameForLookup); - if (!MaybeEntry) - return MaybeEntry.getError(); - return EntryRef(OriginalFilename, *MaybeEntry).unwrapError(); -} - -llvm::ErrorOr<llvm::vfs::Status> -DependencyScanningWorkerFilesystem::status(const Twine &Path) { - SmallString<256> OwnedFilename; - StringRef Filename = Path.toStringRef(OwnedFilename); - - if (shouldBypass(Filename)) - return getUnderlyingFS().status(Path); - - llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename); - if (!Result) - return Result.getError(); - return Result->getStatus(); -} - -bool DependencyScanningWorkerFilesystem::exists(const Twine &Path) { - // While some VFS overlay filesystems may implement more-efficient - // mechanisms for `exists` queries, `DependencyScanningWorkerFilesystem` - // typically wraps `RealFileSystem` which does not specialize `exists`, - // so it is not likely to benefit from such optimizations. Instead, - // it is more-valuable to have this query go through the - // cached-`status` code-path of the `DependencyScanningWorkerFilesystem`. - llvm::ErrorOr<llvm::vfs::Status> Status = status(Path); - return Status && Status->exists(); -} - -namespace { - -/// The VFS that is used by clang consumes the \c CachedFileSystemEntry using -/// this subclass. -class DepScanFile final : public llvm::vfs::File { -public: - DepScanFile(std::unique_ptr<llvm::MemoryBuffer> Buffer, - llvm::vfs::Status Stat) - : Buffer(std::move(Buffer)), Stat(std::move(Stat)) {} - - static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> create(EntryRef Entry); - - llvm::ErrorOr<llvm::vfs::Status> status() override { return Stat; } - - llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> - getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, - bool IsVolatile) override { - return std::move(Buffer); - } - - std::error_code close() override { return {}; } - -private: - std::unique_ptr<llvm::MemoryBuffer> Buffer; - llvm::vfs::Status Stat; -}; - -} // end anonymous namespace - -llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> -DepScanFile::create(EntryRef Entry) { - assert(!Entry.isError() && "error"); - - if (Entry.isDirectory()) - return std::make_error_code(std::errc::is_a_directory); - - auto Result = std::make_unique<DepScanFile>( - llvm::MemoryBuffer::getMemBuffer(Entry.getContents(), - Entry.getStatus().getName(), - /*RequiresNullTerminator=*/false), - Entry.getStatus()); - - return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>( - std::unique_ptr<llvm::vfs::File>(std::move(Result))); -} - -llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> -DependencyScanningWorkerFilesystem::openFileForRead(const Twine &Path) { - SmallString<256> OwnedFilename; - StringRef Filename = Path.toStringRef(OwnedFilename); - - if (shouldBypass(Filename)) - return getUnderlyingFS().openFileForRead(Path); - - llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename); - if (!Result) - return Result.getError(); - return DepScanFile::create(Result.get()); -} - -std::error_code -DependencyScanningWorkerFilesystem::getRealPath(const Twine &Path, - SmallVectorImpl<char> &Output) { - SmallString<256> OwnedFilename; - StringRef OriginalFilename = Path.toStringRef(OwnedFilename); - - if (shouldBypass(OriginalFilename)) - return getUnderlyingFS().getRealPath(Path, Output); - - SmallString<256> PathBuf; - auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf); - if (!FilenameForLookup) - return FilenameForLookup.getError(); - - auto HandleCachedRealPath = - [&Output](const CachedRealPath &RealPath) -> std::error_code { - if (!RealPath) - return RealPath.getError(); - Output.assign(RealPath->begin(), RealPath->end()); - return {}; - }; - - // If we already have the result in local cache, no work required. - if (const auto *RealPath = - LocalCache.findRealPathByFilename(*FilenameForLookup)) - return HandleCachedRealPath(*RealPath); - - // If we have the result in the shared cache, cache it locally. - auto &Shard = SharedCache.getShardForFilename(*FilenameForLookup); - if (const auto *ShardRealPath = - Shard.findRealPathByFilename(*FilenameForLookup)) { - const auto &RealPath = LocalCache.insertRealPathForFilename( - *FilenameForLookup, *ShardRealPath); - return HandleCachedRealPath(RealPath); - } - - // If we don't know the real path, compute it... - std::error_code EC = getUnderlyingFS().getRealPath(OriginalFilename, Output); - llvm::ErrorOr<llvm::StringRef> ComputedRealPath = EC; - if (!EC) - ComputedRealPath = StringRef{Output.data(), Output.size()}; - - // ...and try to write it into the shared cache. In case some other thread won - // this race and already wrote its own result there, just adopt it. Write - // whatever is in the shared cache into the local one. - const auto &RealPath = Shard.getOrEmplaceRealPathForFilename( - *FilenameForLookup, ComputedRealPath); - return HandleCachedRealPath( - LocalCache.insertRealPathForFilename(*FilenameForLookup, RealPath)); -} - -std::error_code DependencyScanningWorkerFilesystem::setCurrentWorkingDirectory( - const Twine &Path) { - std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path); - updateWorkingDirForCacheLookup(); - return EC; -} - -void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() { - llvm::ErrorOr<std::string> CWD = - getUnderlyingFS().getCurrentWorkingDirectory(); - if (!CWD) { - WorkingDirForCacheLookup = CWD.getError(); - } else if (!llvm::sys::path::is_absolute_gnu(*CWD)) { - WorkingDirForCacheLookup = llvm::errc::invalid_argument; - } else { - WorkingDirForCacheLookup = *CWD; - } - assert(!WorkingDirForCacheLookup || - llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup)); -} - -llvm::ErrorOr<StringRef> -DependencyScanningWorkerFilesystem::tryGetFilenameForLookup( - StringRef OriginalFilename, llvm::SmallVectorImpl<char> &PathBuf) const { - StringRef FilenameForLookup; - if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) { - FilenameForLookup = OriginalFilename; - } else if (!WorkingDirForCacheLookup) { - return WorkingDirForCacheLookup.getError(); - } else { - StringRef RelFilename = OriginalFilename; - RelFilename.consume_front("./"); - PathBuf.assign(WorkingDirForCacheLookup->begin(), - WorkingDirForCacheLookup->end()); - llvm::sys::path::append(PathBuf, RelFilename); - FilenameForLookup = StringRef{PathBuf.begin(), PathBuf.size()}; - } - assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup)); - return FilenameForLookup; -} - -const char DependencyScanningWorkerFilesystem::ID = 0; diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp deleted file mode 100644 index 7f40c99..0000000 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp +++ /dev/null @@ -1,21 +0,0 @@ -//===- DependencyScanningService.cpp - clang-scan-deps service ------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/Tooling/DependencyScanning/DependencyScanningService.h" - -using namespace clang; -using namespace tooling; -using namespace dependencies; - -DependencyScanningService::DependencyScanningService( - ScanningMode Mode, ScanningOutputFormat Format, - ScanningOptimizations OptimizeArgs, bool EagerLoadModules, bool TraceVFS, - std::time_t BuildSessionTimestamp) - : Mode(Mode), Format(Format), OptimizeArgs(OptimizeArgs), - EagerLoadModules(EagerLoadModules), TraceVFS(TraceVFS), - BuildSessionTimestamp(BuildSessionTimestamp) {} diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp deleted file mode 100644 index 27734ff..0000000 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp +++ /dev/null @@ -1,196 +0,0 @@ -//===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" -#include "clang/Frontend/Utils.h" -#include <optional> - -using namespace clang; -using namespace tooling; -using namespace dependencies; - -DependencyScanningTool::DependencyScanningTool( - DependencyScanningService &Service, - llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) - : Worker(Service, std::move(FS)) {} - -namespace { -/// Prints out all of the gathered dependencies into a string. -class MakeDependencyPrinterConsumer : public DependencyConsumer { -public: - void handleBuildCommand(Command) override {} - - void - handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override { - this->Opts = std::make_unique<DependencyOutputOptions>(Opts); - } - - void handleFileDependency(StringRef File) override { - Dependencies.push_back(std::string(File)); - } - - // These are ignored for the make format as it can't support the full - // set of deps, and handleFileDependency handles enough for implicitly - // built modules to work. - void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {} - void handleModuleDependency(ModuleDeps MD) override {} - void handleDirectModuleDependency(ModuleID ID) override {} - void handleVisibleModule(std::string ModuleName) override {} - void handleContextHash(std::string Hash) override {} - - void printDependencies(std::string &S) { - assert(Opts && "Handled dependency output options."); - - class DependencyPrinter : public DependencyFileGenerator { - public: - DependencyPrinter(DependencyOutputOptions &Opts, - ArrayRef<std::string> Dependencies) - : DependencyFileGenerator(Opts) { - for (const auto &Dep : Dependencies) - addDependency(Dep); - } - - void printDependencies(std::string &S) { - llvm::raw_string_ostream OS(S); - outputDependencyFile(OS); - } - }; - - DependencyPrinter Generator(*Opts, Dependencies); - Generator.printDependencies(S); - } - -protected: - std::unique_ptr<DependencyOutputOptions> Opts; - std::vector<std::string> Dependencies; -}; -} // anonymous namespace - -llvm::Expected<std::string> DependencyScanningTool::getDependencyFile( - const std::vector<std::string> &CommandLine, StringRef CWD) { - MakeDependencyPrinterConsumer Consumer; - CallbackActionController Controller(nullptr); - auto Result = - Worker.computeDependencies(CWD, CommandLine, Consumer, Controller); - if (Result) - return std::move(Result); - std::string Output; - Consumer.printDependencies(Output); - return Output; -} - -llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile( - const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput, - std::string &MakeformatOutputPath) { - class P1689ModuleDependencyPrinterConsumer - : public MakeDependencyPrinterConsumer { - public: - P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule, - const CompileCommand &Command) - : Filename(Command.Filename), Rule(Rule) { - Rule.PrimaryOutput = Command.Output; - } - - void handleProvidedAndRequiredStdCXXModules( - std::optional<P1689ModuleInfo> Provided, - std::vector<P1689ModuleInfo> Requires) override { - Rule.Provides = Provided; - if (Rule.Provides) - Rule.Provides->SourcePath = Filename.str(); - Rule.Requires = Requires; - } - - StringRef getMakeFormatDependencyOutputPath() { - if (Opts->OutputFormat != DependencyOutputFormat::Make) - return {}; - return Opts->OutputFile; - } - - private: - StringRef Filename; - P1689Rule &Rule; - }; - - class P1689ActionController : public DependencyActionController { - public: - // The lookupModuleOutput is for clang modules. P1689 format don't need it. - std::string lookupModuleOutput(const ModuleDeps &, - ModuleOutputKind Kind) override { - return ""; - } - }; - - P1689Rule Rule; - P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command); - P1689ActionController Controller; - auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer, - Controller); - if (Result) - return std::move(Result); - - MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath(); - if (!MakeformatOutputPath.empty()) - Consumer.printDependencies(MakeformatOutput); - return Rule; -} - -llvm::Expected<TranslationUnitDeps> -DependencyScanningTool::getTranslationUnitDependencies( - const std::vector<std::string> &CommandLine, StringRef CWD, - const llvm::DenseSet<ModuleID> &AlreadySeen, - LookupModuleOutputCallback LookupModuleOutput, - std::optional<llvm::MemoryBufferRef> TUBuffer) { - FullDependencyConsumer Consumer(AlreadySeen); - CallbackActionController Controller(LookupModuleOutput); - llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, - Controller, TUBuffer); - - if (Result) - return std::move(Result); - return Consumer.takeTranslationUnitDeps(); -} - -llvm::Expected<TranslationUnitDeps> -DependencyScanningTool::getModuleDependencies( - StringRef ModuleName, const std::vector<std::string> &CommandLine, - StringRef CWD, const llvm::DenseSet<ModuleID> &AlreadySeen, - LookupModuleOutputCallback LookupModuleOutput) { - FullDependencyConsumer Consumer(AlreadySeen); - CallbackActionController Controller(LookupModuleOutput); - llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, - Controller, ModuleName); - if (Result) - return std::move(Result); - return Consumer.takeTranslationUnitDeps(); -} - -TranslationUnitDeps FullDependencyConsumer::takeTranslationUnitDeps() { - TranslationUnitDeps TU; - - TU.ID.ContextHash = std::move(ContextHash); - TU.ID.ModuleName = std::move(ModuleName); - TU.NamedModuleDeps = std::move(NamedModuleDeps); - TU.FileDeps = std::move(Dependencies); - TU.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); - TU.VisibleModules = std::move(VisibleModules); - TU.Commands = std::move(Commands); - - for (auto &&M : ClangModuleDeps) { - auto &MD = M.second; - // TODO: Avoid handleModuleDependency even being called for modules - // we've already seen. - if (AlreadySeen.count(M.first)) - continue; - TU.ModuleGraph.push_back(std::move(MD)); - } - TU.ClangModuleDeps = std::move(DirectModuleDeps); - - return TU; -} - -CallbackActionController::~CallbackActionController() {} diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp deleted file mode 100644 index 0a1cf6b..0000000 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ /dev/null @@ -1,195 +0,0 @@ -//===- DependencyScanningWorker.cpp - clang-scan-deps worker --------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" -#include "DependencyScannerImpl.h" -#include "clang/Basic/DiagnosticFrontend.h" -#include "clang/Driver/Driver.h" -#include "clang/Driver/Tool.h" - -using namespace clang; -using namespace tooling; -using namespace dependencies; - -DependencyScanningWorker::DependencyScanningWorker( - DependencyScanningService &Service, - llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) - : Service(Service) { - PCHContainerOps = std::make_shared<PCHContainerOperations>(); - // We need to read object files from PCH built outside the scanner. - PCHContainerOps->registerReader( - std::make_unique<ObjectFilePCHContainerReader>()); - // The scanner itself writes only raw ast files. - PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>()); - - if (Service.shouldTraceVFS()) - FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS)); - - switch (Service.getMode()) { - case ScanningMode::DependencyDirectivesScan: - DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>( - Service.getSharedCache(), FS); - BaseFS = DepFS; - break; - case ScanningMode::CanonicalPreprocessing: - DepFS = nullptr; - BaseFS = FS; - break; - } -} - -llvm::Error DependencyScanningWorker::computeDependencies( - StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, - DependencyConsumer &Consumer, DependencyActionController &Controller, - std::optional<llvm::MemoryBufferRef> TUBuffer) { - // Capture the emitted diagnostics and report them to the client - // in the case of a failure. - TextDiagnosticsPrinterWithOutput DiagPrinterWithOS(CommandLine); - - if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller, - DiagPrinterWithOS.DiagPrinter, TUBuffer)) - return llvm::Error::success(); - return llvm::make_error<llvm::StringError>( - DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode()); -} - -llvm::Error DependencyScanningWorker::computeDependencies( - StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, - DependencyConsumer &Consumer, DependencyActionController &Controller, - StringRef ModuleName) { - // Capture the emitted diagnostics and report them to the client - // in the case of a failure. - TextDiagnosticsPrinterWithOutput DiagPrinterWithOS(CommandLine); - - if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller, - DiagPrinterWithOS.DiagPrinter, ModuleName)) - return llvm::Error::success(); - return llvm::make_error<llvm::StringError>( - DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode()); -} - -static bool forEachDriverJob( - ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - llvm::function_ref<bool(const driver::Command &Cmd)> Callback) { - // Compilation holds a non-owning a reference to the Driver, hence we need to - // keep the Driver alive when we use Compilation. Arguments to commands may be - // owned by Alloc when expanded from response files. - llvm::BumpPtrAllocator Alloc; - auto [Driver, Compilation] = buildCompilation(ArgStrs, Diags, FS, Alloc); - if (!Compilation) - return false; - for (const driver::Command &Job : Compilation->getJobs()) { - if (!Callback(Job)) - return false; - } - return true; -} - -static bool createAndRunToolInvocation( - const std::vector<std::string> &CommandLine, - DependencyScanningAction &Action, - IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps, - DiagnosticsEngine &Diags, DependencyConsumer &Consumer) { - auto Invocation = createCompilerInvocation(CommandLine, Diags); - if (!Invocation) - return false; - - if (!Action.runInvocation(std::move(Invocation), std::move(FS), - PCHContainerOps, Diags.getClient())) - return false; - - std::vector<std::string> Args = Action.takeLastCC1Arguments(); - Consumer.handleBuildCommand({CommandLine[0], std::move(Args)}); - return true; -} - -bool DependencyScanningWorker::scanDependencies( - StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, - DependencyConsumer &Consumer, DependencyActionController &Controller, - DiagnosticConsumer &DC, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, - std::optional<StringRef> ModuleName) { - DignosticsEngineWithDiagOpts DiagEngineWithCmdAndOpts(CommandLine, FS, DC); - DependencyScanningAction Action(Service, WorkingDirectory, Consumer, - Controller, DepFS, ModuleName); - - bool Success = false; - if (CommandLine[1] == "-cc1") { - Success = createAndRunToolInvocation( - CommandLine, Action, FS, PCHContainerOps, - *DiagEngineWithCmdAndOpts.DiagEngine, Consumer); - } else { - Success = forEachDriverJob( - CommandLine, *DiagEngineWithCmdAndOpts.DiagEngine, FS, - [&](const driver::Command &Cmd) { - if (StringRef(Cmd.getCreator().getName()) != "clang") { - // Non-clang command. Just pass through to the dependency - // consumer. - Consumer.handleBuildCommand( - {Cmd.getExecutable(), - {Cmd.getArguments().begin(), Cmd.getArguments().end()}}); - return true; - } - - // Insert -cc1 comand line options into Argv - std::vector<std::string> Argv; - Argv.push_back(Cmd.getExecutable()); - llvm::append_range(Argv, Cmd.getArguments()); - - // Create an invocation that uses the underlying file - // system to ensure that any file system requests that - // are made by the driver do not go through the - // dependency scanning filesystem. - return createAndRunToolInvocation( - std::move(Argv), Action, FS, PCHContainerOps, - *DiagEngineWithCmdAndOpts.DiagEngine, Consumer); - }); - } - - if (Success && !Action.hasScanned()) - DiagEngineWithCmdAndOpts.DiagEngine->Report( - diag::err_fe_expected_compiler_job) - << llvm::join(CommandLine, " "); - - // Ensure finish() is called even if we never reached ExecuteAction(). - if (!Action.hasDiagConsumerFinished()) - DC.finish(); - - return Success && Action.hasScanned(); -} - -bool DependencyScanningWorker::computeDependencies( - StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, - DependencyConsumer &Consumer, DependencyActionController &Controller, - DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) { - if (TUBuffer) { - auto [FinalFS, FinalCommandLine] = initVFSForTUBuferScanning( - BaseFS, CommandLine, WorkingDirectory, *TUBuffer); - return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer, - Controller, DC, FinalFS, - /*ModuleName=*/std::nullopt); - } else { - BaseFS->setCurrentWorkingDirectory(WorkingDirectory); - return scanDependencies(WorkingDirectory, CommandLine, Consumer, Controller, - DC, BaseFS, /*ModuleName=*/std::nullopt); - } -} - -bool DependencyScanningWorker::computeDependencies( - StringRef WorkingDirectory, const std::vector<std::string> &CommandLine, - DependencyConsumer &Consumer, DependencyActionController &Controller, - DiagnosticConsumer &DC, StringRef ModuleName) { - auto [OverlayFS, ModifiedCommandLine] = initVFSForByNameScanning( - BaseFS, CommandLine, WorkingDirectory, ModuleName); - - return scanDependencies(WorkingDirectory, ModifiedCommandLine, Consumer, - Controller, DC, OverlayFS, ModuleName); -} - -DependencyActionController::~DependencyActionController() {} diff --git a/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp b/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp deleted file mode 100644 index d1e543b..0000000 --- a/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp +++ /dev/null @@ -1,120 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/Tooling/DependencyScanning/InProcessModuleCache.h" - -#include "clang/Serialization/InMemoryModuleCache.h" -#include "llvm/Support/AdvisoryLock.h" -#include "llvm/Support/Chrono.h" - -#include <mutex> - -using namespace clang; -using namespace tooling; -using namespace dependencies; - -namespace { -class ReaderWriterLock : public llvm::AdvisoryLock { - // TODO: Consider using std::atomic::{wait,notify_all} when we move to C++20. - std::unique_lock<std::shared_mutex> OwningLock; - -public: - ReaderWriterLock(std::shared_mutex &Mutex) - : OwningLock(Mutex, std::defer_lock) {} - - Expected<bool> tryLock() override { return OwningLock.try_lock(); } - - llvm::WaitForUnlockResult - waitForUnlockFor(std::chrono::seconds MaxSeconds) override { - assert(!OwningLock); - // We do not respect the timeout here. It's very generous for implicit - // modules, so we'd typically only reach it if the owner crashed (but so did - // we, since we run in the same process), or encountered deadlock. - (void)MaxSeconds; - std::shared_lock<std::shared_mutex> Lock(*OwningLock.mutex()); - return llvm::WaitForUnlockResult::Success; - } - - std::error_code unsafeMaybeUnlock() override { - // Unlocking the mutex here would trigger UB and we don't expect this to be - // actually called when compiling scanning modules due to the no-timeout - // guarantee above. - return {}; - } - - ~ReaderWriterLock() override = default; -}; - -class InProcessModuleCache : public ModuleCache { - ModuleCacheEntries &Entries; - - // TODO: If we changed the InMemoryModuleCache API and relied on strict - // context hash, we could probably create more efficient thread-safe - // implementation of the InMemoryModuleCache such that it doesn't need to be - // recreated for each translation unit. - InMemoryModuleCache InMemory; - -public: - InProcessModuleCache(ModuleCacheEntries &Entries) : Entries(Entries) {} - - void prepareForGetLock(StringRef Filename) override {} - - std::unique_ptr<llvm::AdvisoryLock> getLock(StringRef Filename) override { - auto &CompilationMutex = [&]() -> std::shared_mutex & { - std::lock_guard<std::mutex> Lock(Entries.Mutex); - auto &Entry = Entries.Map[Filename]; - if (!Entry) - Entry = std::make_unique<ModuleCacheEntry>(); - return Entry->CompilationMutex; - }(); - return std::make_unique<ReaderWriterLock>(CompilationMutex); - } - - std::time_t getModuleTimestamp(StringRef Filename) override { - auto &Timestamp = [&]() -> std::atomic<std::time_t> & { - std::lock_guard<std::mutex> Lock(Entries.Mutex); - auto &Entry = Entries.Map[Filename]; - if (!Entry) - Entry = std::make_unique<ModuleCacheEntry>(); - return Entry->Timestamp; - }(); - - return Timestamp.load(); - } - - void updateModuleTimestamp(StringRef Filename) override { - // Note: This essentially replaces FS contention with mutex contention. - auto &Timestamp = [&]() -> std::atomic<std::time_t> & { - std::lock_guard<std::mutex> Lock(Entries.Mutex); - auto &Entry = Entries.Map[Filename]; - if (!Entry) - Entry = std::make_unique<ModuleCacheEntry>(); - return Entry->Timestamp; - }(); - - Timestamp.store(llvm::sys::toTimeT(std::chrono::system_clock::now())); - } - - void maybePrune(StringRef Path, time_t PruneInterval, - time_t PruneAfter) override { - // FIXME: This only needs to be ran once per build, not in every - // compilation. Call it once per service. - maybePruneImpl(Path, PruneInterval, PruneAfter); - } - - InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; } - const InMemoryModuleCache &getInMemoryModuleCache() const override { - return InMemory; - } -}; -} // namespace - -IntrusiveRefCntPtr<ModuleCache> -dependencies::makeInProcessModuleCache(ModuleCacheEntries &Entries) { - return llvm::makeIntrusiveRefCnt<InProcessModuleCache>(Entries); -} diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp deleted file mode 100644 index a117bec..0000000 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ /dev/null @@ -1,1034 +0,0 @@ -//===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" - -#include "clang/Basic/MakeSupport.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/Support/BLAKE3.h" -#include <optional> - -using namespace clang; -using namespace tooling; -using namespace dependencies; - -void ModuleDeps::forEachFileDep(llvm::function_ref<void(StringRef)> Cb) const { - SmallString<0> PathBuf; - PathBuf.reserve(256); - for (StringRef FileDep : FileDeps) { - auto ResolvedFileDep = - ASTReader::ResolveImportedPath(PathBuf, FileDep, FileDepsBaseDir); - Cb(*ResolvedFileDep); - } -} - -const std::vector<std::string> &ModuleDeps::getBuildArguments() const { - // FIXME: this operation is not thread safe and is expected to be called - // on a single thread. Otherwise it should be protected with a lock. - assert(!std::holds_alternative<std::monostate>(BuildInfo) && - "Using uninitialized ModuleDeps"); - if (const auto *CI = std::get_if<CowCompilerInvocation>(&BuildInfo)) - BuildInfo = CI->getCC1CommandLine(); - return std::get<std::vector<std::string>>(BuildInfo); -} - -void PrebuiltModuleASTAttrs::updateDependentsNotInStableDirs( - PrebuiltModulesAttrsMap &PrebuiltModulesMap) { - setInStableDir(); - for (const auto Dep : ModuleFileDependents) { - if (!PrebuiltModulesMap[Dep].isInStableDir()) - return; - PrebuiltModulesMap[Dep].updateDependentsNotInStableDirs(PrebuiltModulesMap); - } -} - -static void -optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, ASTReader &Reader, - const serialization::ModuleFile &MF, - const PrebuiltModulesAttrsMap &PrebuiltModulesASTMap, - 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) { - Visited.insert(MF); - if (MF->Kind == serialization::MK_ImplicitModule) { - VFSUsage |= MF->VFSUsage; - // We only need to recurse into implicit modules. Other module types - // will have the correct set of VFSs for anything they depend on. - for (const serialization::ModuleFile *Import : MF->Imports) - if (!Visited.contains(Import)) - VisitMF(Import); - } else { - // This is not an implicitly built module, so it may have different - // VFS options. Fall back to a string comparison instead. - auto PrebuiltModulePropIt = - PrebuiltModulesASTMap.find(MF->FileName); - if (PrebuiltModulePropIt == PrebuiltModulesASTMap.end()) - return; - for (std::size_t I = 0, E = VFSOverlayFiles.size(); I != E; ++I) { - if (PrebuiltModulePropIt->second.getVFS().contains( - VFSOverlayFiles[I])) - VFSUsage[I] = true; - } - } - }; - 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, - bool IsSystemModule) { - // If this is not a system module or -Wsystem-headers was passed, don't - // optimize. - if (!IsSystemModule) - return; - bool Wsystem_headers = false; - for (StringRef Opt : Opts.Warnings) { - bool isPositive = !Opt.consume_front("no-"); - if (Opt == "system-headers") - Wsystem_headers = isPositive; - } - if (Wsystem_headers) - return; - - // Remove all warning flags. System modules suppress most, but not all, - // warnings. - Opts.Warnings.clear(); - Opts.UndefPrefixes.clear(); - Opts.Remarks.clear(); -} - -static void optimizeCWD(CowCompilerInvocation &BuildInvocation, StringRef CWD) { - BuildInvocation.getMutFileSystemOpts().WorkingDir.clear(); - BuildInvocation.getMutCodeGenOpts().DebugCompilationDir.clear(); - BuildInvocation.getMutCodeGenOpts().CoverageCompilationDir.clear(); -} - -static std::vector<std::string> splitString(std::string S, char Separator) { - SmallVector<StringRef> Segments; - StringRef(S).split(Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false); - std::vector<std::string> Result; - Result.reserve(Segments.size()); - for (StringRef Segment : Segments) - Result.push_back(Segment.str()); - return Result; -} - -void ModuleDepCollector::addOutputPaths(CowCompilerInvocation &CI, - ModuleDeps &Deps) { - CI.getMutFrontendOpts().OutputFile = - Controller.lookupModuleOutput(Deps, ModuleOutputKind::ModuleFile); - if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty()) - CI.getMutDiagnosticOpts().DiagnosticSerializationFile = - Controller.lookupModuleOutput( - Deps, ModuleOutputKind::DiagnosticSerializationFile); - if (!CI.getDependencyOutputOpts().OutputFile.empty()) { - CI.getMutDependencyOutputOpts().OutputFile = - Controller.lookupModuleOutput(Deps, ModuleOutputKind::DependencyFile); - CI.getMutDependencyOutputOpts().Targets = - splitString(Controller.lookupModuleOutput( - Deps, ModuleOutputKind::DependencyTargets), - '\0'); - if (!CI.getDependencyOutputOpts().OutputFile.empty() && - CI.getDependencyOutputOpts().Targets.empty()) { - // Fallback to -o as dependency target, as in the driver. - SmallString<128> Target; - quoteMakeTarget(CI.getFrontendOpts().OutputFile, Target); - CI.getMutDependencyOutputOpts().Targets.push_back(std::string(Target)); - } - } -} - -void dependencies::resetBenignCodeGenOptions(frontend::ActionKind ProgramAction, - const LangOptions &LangOpts, - CodeGenOptions &CGOpts) { - // TODO: Figure out better way to set options to their default value. - if (ProgramAction == frontend::GenerateModule) { - CGOpts.MainFileName.clear(); - CGOpts.DwarfDebugFlags.clear(); - } - if (ProgramAction == frontend::GeneratePCH || - (ProgramAction == frontend::GenerateModule && !LangOpts.ModulesCodegen)) { - CGOpts.DebugCompilationDir.clear(); - CGOpts.CoverageCompilationDir.clear(); - CGOpts.CoverageDataFile.clear(); - CGOpts.CoverageNotesFile.clear(); - CGOpts.ProfileInstrumentUsePath.clear(); - CGOpts.SampleProfileFile.clear(); - CGOpts.ProfileRemappingFile.clear(); - } -} - -bool dependencies::isPathInStableDir(const ArrayRef<StringRef> Directories, - const StringRef Input) { - using namespace llvm::sys; - - if (!path::is_absolute(Input)) - return false; - - auto PathStartsWith = [](StringRef Prefix, StringRef Path) { - auto PrefixIt = path::begin(Prefix), PrefixEnd = path::end(Prefix); - for (auto PathIt = path::begin(Path), PathEnd = path::end(Path); - PrefixIt != PrefixEnd && PathIt != PathEnd; ++PrefixIt, ++PathIt) { - if (*PrefixIt != *PathIt) - return false; - } - return PrefixIt == PrefixEnd; - }; - - return any_of(Directories, [&](StringRef Dir) { - return !Dir.empty() && PathStartsWith(Dir, Input); - }); -} - -bool dependencies::areOptionsInStableDir(const ArrayRef<StringRef> Directories, - const HeaderSearchOptions &HSOpts) { - assert(isPathInStableDir(Directories, HSOpts.Sysroot) && - "Sysroots differ between module dependencies and current TU"); - - assert(isPathInStableDir(Directories, HSOpts.ResourceDir) && - "ResourceDirs differ between module dependencies and current TU"); - - for (const auto &Entry : HSOpts.UserEntries) { - if (!Entry.IgnoreSysRoot) - continue; - if (!isPathInStableDir(Directories, Entry.Path)) - return false; - } - - for (const auto &SysPrefix : HSOpts.SystemHeaderPrefixes) { - if (!isPathInStableDir(Directories, SysPrefix.Prefix)) - return false; - } - - return true; -} - -static CowCompilerInvocation -makeCommonInvocationForModuleBuild(CompilerInvocation CI) { - CI.resetNonModularOptions(); - CI.clearImplicitModuleBuildOptions(); - - // The scanner takes care to avoid passing non-affecting module maps to the - // explicit compiles. No need to do extra work just to find out there are no - // module map files to prune. - CI.getHeaderSearchOpts().ModulesPruneNonAffectingModuleMaps = false; - - // Remove options incompatible with explicit module build or are likely to - // differ between identical modules discovered from different translation - // units. - CI.getFrontendOpts().Inputs.clear(); - CI.getFrontendOpts().OutputFile.clear(); - CI.getFrontendOpts().GenReducedBMI = false; - CI.getFrontendOpts().ModuleOutputPath.clear(); - CI.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = false; - CI.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = false; - // LLVM options are not going to affect the AST - CI.getFrontendOpts().LLVMArgs.clear(); - - resetBenignCodeGenOptions(frontend::GenerateModule, CI.getLangOpts(), - CI.getCodeGenOpts()); - - // Map output paths that affect behaviour to "-" so their existence is in the - // context hash. The final path will be computed in addOutputPaths. - if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty()) - CI.getDiagnosticOpts().DiagnosticSerializationFile = "-"; - if (!CI.getDependencyOutputOpts().OutputFile.empty()) - CI.getDependencyOutputOpts().OutputFile = "-"; - CI.getDependencyOutputOpts().Targets.clear(); - - CI.getFrontendOpts().ProgramAction = frontend::GenerateModule; - CI.getLangOpts().ModuleName.clear(); - - // Remove any macro definitions that are explicitly ignored. - if (!CI.getHeaderSearchOpts().ModulesIgnoreMacros.empty()) { - llvm::erase_if( - CI.getPreprocessorOpts().Macros, - [&CI](const std::pair<std::string, bool> &Def) { - StringRef MacroDef = Def.first; - return CI.getHeaderSearchOpts().ModulesIgnoreMacros.contains( - llvm::CachedHashString(MacroDef.split('=').first)); - }); - // Remove the now unused option. - CI.getHeaderSearchOpts().ModulesIgnoreMacros.clear(); - } - - return CI; -} - -CowCompilerInvocation -ModuleDepCollector::getInvocationAdjustedForModuleBuildWithoutOutputs( - const ModuleDeps &Deps, - llvm::function_ref<void(CowCompilerInvocation &)> Optimize) const { - CowCompilerInvocation CI = CommonInvocation; - - CI.getMutLangOpts().ModuleName = Deps.ID.ModuleName; - CI.getMutFrontendOpts().IsSystemModule = Deps.IsSystem; - - // Inputs - InputKind ModuleMapInputKind(CI.getFrontendOpts().DashX.getLanguage(), - InputKind::Format::ModuleMap); - CI.getMutFrontendOpts().Inputs.emplace_back(Deps.ClangModuleMapFile, - ModuleMapInputKind); - - auto CurrentModuleMapEntry = - ScanInstance.getFileManager().getOptionalFileRef(Deps.ClangModuleMapFile); - assert(CurrentModuleMapEntry && "module map file entry not found"); - - // Remove directly passed modulemap files. They will get added back if they - // were actually used. - CI.getMutFrontendOpts().ModuleMapFiles.clear(); - - auto DepModuleMapFiles = collectModuleMapFiles(Deps.ClangModuleDeps); - for (StringRef ModuleMapFile : Deps.ModuleMapFileDeps) { - // TODO: Track these as `FileEntryRef` to simplify the equality check below. - auto ModuleMapEntry = - ScanInstance.getFileManager().getOptionalFileRef(ModuleMapFile); - assert(ModuleMapEntry && "module map file entry not found"); - - // Don't report module maps describing eagerly-loaded dependency. This - // information will be deserialized from the PCM. - // TODO: Verify this works fine when modulemap for module A is eagerly - // loaded from A.pcm, and module map passed on the command line contains - // definition of a submodule: "explicit module A.Private { ... }". - if (Service.shouldEagerLoadModules() && - DepModuleMapFiles.contains(*ModuleMapEntry)) - continue; - - // Don't report module map file of the current module unless it also - // describes a dependency (for symmetry). - if (*ModuleMapEntry == *CurrentModuleMapEntry && - !DepModuleMapFiles.contains(*ModuleMapEntry)) - continue; - - CI.getMutFrontendOpts().ModuleMapFiles.emplace_back(ModuleMapFile); - } - - // Report the prebuilt modules this module uses. - for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) - CI.getMutFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile); - - // Add module file inputs from dependencies. - addModuleFiles(CI, Deps.ClangModuleDeps); - - if (!CI.getDiagnosticOpts().SystemHeaderWarningsModules.empty()) { - // Apply -Wsystem-headers-in-module for the current module. - if (llvm::is_contained(CI.getDiagnosticOpts().SystemHeaderWarningsModules, - Deps.ID.ModuleName)) - CI.getMutDiagnosticOpts().Warnings.push_back("system-headers"); - // Remove the now unused option(s). - CI.getMutDiagnosticOpts().SystemHeaderWarningsModules.clear(); - } - - Optimize(CI); - - return CI; -} - -llvm::DenseSet<const FileEntry *> ModuleDepCollector::collectModuleMapFiles( - ArrayRef<ModuleID> ClangModuleDeps) const { - llvm::DenseSet<const FileEntry *> ModuleMapFiles; - for (const ModuleID &MID : ClangModuleDeps) { - ModuleDeps *MD = ModuleDepsByID.lookup(MID); - assert(MD && "Inconsistent dependency info"); - // TODO: Track ClangModuleMapFile as `FileEntryRef`. - auto FE = ScanInstance.getFileManager().getOptionalFileRef( - MD->ClangModuleMapFile); - assert(FE && "Missing module map file that was previously found"); - ModuleMapFiles.insert(*FE); - } - return ModuleMapFiles; -} - -void ModuleDepCollector::addModuleMapFiles( - CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const { - if (Service.shouldEagerLoadModules()) - return; // Only pcm is needed for eager load. - - for (const ModuleID &MID : ClangModuleDeps) { - ModuleDeps *MD = ModuleDepsByID.lookup(MID); - assert(MD && "Inconsistent dependency info"); - CI.getFrontendOpts().ModuleMapFiles.push_back(MD->ClangModuleMapFile); - } -} - -void ModuleDepCollector::addModuleFiles( - CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const { - for (const ModuleID &MID : ClangModuleDeps) { - ModuleDeps *MD = ModuleDepsByID.lookup(MID); - std::string PCMPath = - Controller.lookupModuleOutput(*MD, ModuleOutputKind::ModuleFile); - - if (Service.shouldEagerLoadModules()) - CI.getFrontendOpts().ModuleFiles.push_back(std::move(PCMPath)); - else - CI.getHeaderSearchOpts().PrebuiltModuleFiles.insert( - {MID.ModuleName, std::move(PCMPath)}); - } -} - -void ModuleDepCollector::addModuleFiles( - CowCompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const { - for (const ModuleID &MID : ClangModuleDeps) { - ModuleDeps *MD = ModuleDepsByID.lookup(MID); - std::string PCMPath = - Controller.lookupModuleOutput(*MD, ModuleOutputKind::ModuleFile); - - if (Service.shouldEagerLoadModules()) - CI.getMutFrontendOpts().ModuleFiles.push_back(std::move(PCMPath)); - else - CI.getMutHeaderSearchOpts().PrebuiltModuleFiles.insert( - {MID.ModuleName, std::move(PCMPath)}); - } -} - -static bool needsModules(FrontendInputFile FIF) { - switch (FIF.getKind().getLanguage()) { - case Language::Unknown: - case Language::Asm: - case Language::LLVM_IR: - return false; - default: - return true; - } -} - -void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) { - CI.clearImplicitModuleBuildOptions(); - resetBenignCodeGenOptions(CI.getFrontendOpts().ProgramAction, - CI.getLangOpts(), CI.getCodeGenOpts()); - - if (llvm::any_of(CI.getFrontendOpts().Inputs, needsModules)) { - Preprocessor &PP = ScanInstance.getPreprocessor(); - if (Module *CurrentModule = PP.getCurrentModuleImplementation()) - if (OptionalFileEntryRef CurrentModuleMap = - PP.getHeaderSearchInfo() - .getModuleMap() - .getModuleMapFileForUniquing(CurrentModule)) - CI.getFrontendOpts().ModuleMapFiles.emplace_back( - CurrentModuleMap->getNameAsRequested()); - - SmallVector<ModuleID> DirectDeps; - for (const auto &KV : ModularDeps) - if (DirectModularDeps.contains(KV.first)) - DirectDeps.push_back(KV.second->ID); - - // TODO: Report module maps the same way it's done for modular dependencies. - addModuleMapFiles(CI, DirectDeps); - - addModuleFiles(CI, DirectDeps); - - for (const auto &KV : DirectPrebuiltModularDeps) - CI.getFrontendOpts().ModuleFiles.push_back(KV.second.PCMFile); - } -} - -static bool isSafeToIgnoreCWD(const CowCompilerInvocation &CI) { - // Check if the command line input uses relative paths. - // It is not safe to ignore the current working directory if any of the - // command line inputs use relative paths. -#define IF_RELATIVE_RETURN_FALSE(PATH) \ - do { \ - if (!PATH.empty() && !llvm::sys::path::is_absolute(PATH)) \ - return false; \ - } while (0) - -#define IF_ANY_RELATIVE_RETURN_FALSE(PATHS) \ - do { \ - if (llvm::any_of(PATHS, [](const auto &P) { \ - return !P.empty() && !llvm::sys::path::is_absolute(P); \ - })) \ - return false; \ - } while (0) - - // Header search paths. - const auto &HeaderSearchOpts = CI.getHeaderSearchOpts(); - IF_RELATIVE_RETURN_FALSE(HeaderSearchOpts.Sysroot); - for (auto &Entry : HeaderSearchOpts.UserEntries) - if (Entry.IgnoreSysRoot) - IF_RELATIVE_RETURN_FALSE(Entry.Path); - IF_RELATIVE_RETURN_FALSE(HeaderSearchOpts.ResourceDir); - IF_RELATIVE_RETURN_FALSE(HeaderSearchOpts.ModuleCachePath); - IF_RELATIVE_RETURN_FALSE(HeaderSearchOpts.ModuleUserBuildPath); - for (auto I = HeaderSearchOpts.PrebuiltModuleFiles.begin(), - E = HeaderSearchOpts.PrebuiltModuleFiles.end(); - I != E;) { - auto Current = I++; - IF_RELATIVE_RETURN_FALSE(Current->second); - } - IF_ANY_RELATIVE_RETURN_FALSE(HeaderSearchOpts.PrebuiltModulePaths); - IF_ANY_RELATIVE_RETURN_FALSE(HeaderSearchOpts.VFSOverlayFiles); - - // Preprocessor options. - const auto &PPOpts = CI.getPreprocessorOpts(); - IF_ANY_RELATIVE_RETURN_FALSE(PPOpts.MacroIncludes); - IF_ANY_RELATIVE_RETURN_FALSE(PPOpts.Includes); - IF_RELATIVE_RETURN_FALSE(PPOpts.ImplicitPCHInclude); - - // Frontend options. - const auto &FrontendOpts = CI.getFrontendOpts(); - for (const FrontendInputFile &Input : FrontendOpts.Inputs) { - if (Input.isBuffer()) - continue; // FIXME: Can this happen when parsing command-line? - - IF_RELATIVE_RETURN_FALSE(Input.getFile()); - } - IF_RELATIVE_RETURN_FALSE(FrontendOpts.CodeCompletionAt.FileName); - IF_ANY_RELATIVE_RETURN_FALSE(FrontendOpts.ModuleMapFiles); - IF_ANY_RELATIVE_RETURN_FALSE(FrontendOpts.ModuleFiles); - IF_ANY_RELATIVE_RETURN_FALSE(FrontendOpts.ModulesEmbedFiles); - IF_ANY_RELATIVE_RETURN_FALSE(FrontendOpts.ASTMergeFiles); - IF_RELATIVE_RETURN_FALSE(FrontendOpts.OverrideRecordLayoutsFile); - IF_RELATIVE_RETURN_FALSE(FrontendOpts.StatsFile); - - // Filesystem options. - const auto &FileSystemOpts = CI.getFileSystemOpts(); - IF_RELATIVE_RETURN_FALSE(FileSystemOpts.WorkingDir); - - // Codegen options. - const auto &CodeGenOpts = CI.getCodeGenOpts(); - IF_RELATIVE_RETURN_FALSE(CodeGenOpts.DebugCompilationDir); - IF_RELATIVE_RETURN_FALSE(CodeGenOpts.CoverageCompilationDir); - - // Sanitizer options. - IF_ANY_RELATIVE_RETURN_FALSE(CI.getLangOpts().NoSanitizeFiles); - - // Coverage mappings. - IF_RELATIVE_RETURN_FALSE(CodeGenOpts.ProfileInstrumentUsePath); - IF_RELATIVE_RETURN_FALSE(CodeGenOpts.SampleProfileFile); - IF_RELATIVE_RETURN_FALSE(CodeGenOpts.ProfileRemappingFile); - - // Dependency output options. - for (auto &ExtraDep : CI.getDependencyOutputOpts().ExtraDeps) - IF_RELATIVE_RETURN_FALSE(ExtraDep.first); - - return true; -} - -static std::string getModuleContextHash(const ModuleDeps &MD, - const CowCompilerInvocation &CI, - bool EagerLoadModules, bool IgnoreCWD, - llvm::vfs::FileSystem &VFS) { - llvm::HashBuilder<llvm::TruncatedBLAKE3<16>, llvm::endianness::native> - HashBuilder; - - // Hash the compiler version and serialization version to ensure the module - // will be readable. - HashBuilder.add(getClangFullRepositoryVersion()); - HashBuilder.add(serialization::VERSION_MAJOR, serialization::VERSION_MINOR); - llvm::ErrorOr<std::string> CWD = VFS.getCurrentWorkingDirectory(); - if (CWD && !IgnoreCWD) - HashBuilder.add(*CWD); - - // Hash the BuildInvocation without any input files. - SmallString<0> ArgVec; - ArgVec.reserve(4096); - CI.generateCC1CommandLine([&](const Twine &Arg) { - Arg.toVector(ArgVec); - ArgVec.push_back('\0'); - }); - HashBuilder.add(ArgVec); - - // Hash the module dependencies. These paths may differ even if the invocation - // is identical if they depend on the contents of the files in the TU -- for - // example, case-insensitive paths to modulemap files. Usually such a case - // would indicate a missed optimization to canonicalize, but it may be - // difficult to canonicalize all cases when there is a VFS. - for (const auto &ID : MD.ClangModuleDeps) { - HashBuilder.add(ID.ModuleName); - HashBuilder.add(ID.ContextHash); - } - - HashBuilder.add(EagerLoadModules); - - llvm::BLAKE3Result<16> Hash = HashBuilder.final(); - std::array<uint64_t, 2> Words; - static_assert(sizeof(Hash) == sizeof(Words), "Hash must match Words"); - std::memcpy(Words.data(), Hash.data(), sizeof(Hash)); - return toString(llvm::APInt(sizeof(Words) * 8, Words), 36, /*Signed=*/false); -} - -void ModuleDepCollector::associateWithContextHash( - const CowCompilerInvocation &CI, bool IgnoreCWD, ModuleDeps &Deps) { - Deps.ID.ContextHash = - getModuleContextHash(Deps, CI, Service.shouldEagerLoadModules(), - IgnoreCWD, ScanInstance.getVirtualFileSystem()); - bool Inserted = ModuleDepsByID.insert({Deps.ID, &Deps}).second; - (void)Inserted; - assert(Inserted && "duplicate module mapping"); -} - -void ModuleDepCollectorPP::LexedFileChanged(FileID FID, - LexedFileChangeReason Reason, - SrcMgr::CharacteristicKind FileType, - FileID PrevFID, - SourceLocation Loc) { - if (Reason != LexedFileChangeReason::EnterFile) - return; - - SourceManager &SM = MDC.ScanInstance.getSourceManager(); - - // Dependency generation really does want to go all the way to the - // file entry for a source location to find out what is depended on. - // We do not want #line markers to affect dependency generation! - if (std::optional<StringRef> Filename = SM.getNonBuiltinFilenameForID(FID)) - MDC.addFileDep(llvm::sys::path::remove_leading_dotslash(*Filename)); -} - -void ModuleDepCollectorPP::InclusionDirective( - SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, - bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File, - StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule, - bool ModuleImported, SrcMgr::CharacteristicKind FileType) { - if (!File && !ModuleImported) { - // This is a non-modular include that HeaderSearch failed to find. Add it - // here as `FileChanged` will never see it. - MDC.addFileDep(FileName); - } - handleImport(SuggestedModule); -} - -void ModuleDepCollectorPP::moduleImport(SourceLocation ImportLoc, - ModuleIdPath Path, - const Module *Imported) { - if (MDC.ScanInstance.getPreprocessor().isInImportingCXXNamedModules()) { - P1689ModuleInfo RequiredModule; - RequiredModule.ModuleName = Path[0].getIdentifierInfo()->getName().str(); - RequiredModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule; - MDC.RequiredStdCXXModules.push_back(std::move(RequiredModule)); - return; - } - - handleImport(Imported); -} - -void ModuleDepCollectorPP::handleImport(const Module *Imported) { - if (!Imported) - return; - - const Module *TopLevelModule = Imported->getTopLevelModule(); - - if (MDC.isPrebuiltModule(TopLevelModule)) - MDC.DirectPrebuiltModularDeps.insert( - {TopLevelModule, PrebuiltModuleDep{TopLevelModule}}); - else { - MDC.DirectModularDeps.insert(TopLevelModule); - MDC.DirectImports.insert(Imported); - } -} - -void ModuleDepCollectorPP::EndOfMainFile() { - FileID MainFileID = MDC.ScanInstance.getSourceManager().getMainFileID(); - MDC.MainFile = std::string(MDC.ScanInstance.getSourceManager() - .getFileEntryRefForID(MainFileID) - ->getName()); - - auto &PP = MDC.ScanInstance.getPreprocessor(); - if (PP.isInNamedModule()) { - P1689ModuleInfo ProvidedModule; - ProvidedModule.ModuleName = PP.getNamedModuleName(); - ProvidedModule.Type = P1689ModuleInfo::ModuleType::NamedCXXModule; - ProvidedModule.IsStdCXXModuleInterface = PP.isInNamedInterfaceUnit(); - // Don't put implementation (non partition) unit as Provide. - // Put the module as required instead. Since the implementation - // unit will import the primary module implicitly. - if (PP.isInImplementationUnit()) - MDC.RequiredStdCXXModules.push_back(ProvidedModule); - else - MDC.ProvidedStdCXXModule = ProvidedModule; - } - - if (!MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty()) - MDC.addFileDep(MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude); - - for (const Module *M : - MDC.ScanInstance.getPreprocessor().getAffectingClangModules()) - if (!MDC.isPrebuiltModule(M)) - MDC.DirectModularDeps.insert(M); - - MDC.addVisibleModules(); - - for (const Module *M : MDC.DirectModularDeps) - handleTopLevelModule(M); - - MDC.Consumer.handleContextHash( - MDC.ScanInstance.getInvocation().getModuleHash()); - - MDC.Consumer.handleDependencyOutputOpts(*MDC.Opts); - - MDC.Consumer.handleProvidedAndRequiredStdCXXModules( - MDC.ProvidedStdCXXModule, MDC.RequiredStdCXXModules); - - for (auto &&I : MDC.ModularDeps) - MDC.Consumer.handleModuleDependency(*I.second); - - for (const Module *M : MDC.DirectModularDeps) { - auto It = MDC.ModularDeps.find(M); - // Only report direct dependencies that were successfully handled. - if (It != MDC.ModularDeps.end()) - MDC.Consumer.handleDirectModuleDependency(It->second->ID); - } - - for (auto &&I : MDC.VisibleModules) - MDC.Consumer.handleVisibleModule(std::string(I.getKey())); - - for (auto &&I : MDC.FileDeps) - MDC.Consumer.handleFileDependency(I); - - for (auto &&I : MDC.DirectPrebuiltModularDeps) - MDC.Consumer.handlePrebuiltModuleDependency(I.second); -} - -std::optional<ModuleID> -ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { - assert(M == M->getTopLevelModule() && "Expected top level module!"); - - // A top-level module might not be actually imported as a module when - // -fmodule-name is used to compile a translation unit that imports this - // module. In that case it can be skipped. The appropriate header - // dependencies will still be reported as expected. - if (!M->getASTFile()) - return {}; - - // If this module has been handled already, just return its ID. - if (auto ModI = MDC.ModularDeps.find(M); ModI != MDC.ModularDeps.end()) - return ModI->second->ID; - - auto OwnedMD = std::make_unique<ModuleDeps>(); - ModuleDeps &MD = *OwnedMD; - - MD.ID.ModuleName = M->getFullModuleName(); - MD.IsSystem = M->IsSystem; - - // Start off with the assumption that this module is shareable when there - // are stable directories. As more dependencies are discovered, check if those - // come from the provided directories. - MD.IsInStableDirectories = !MDC.StableDirs.empty(); - - // For modules which use export_as link name, the linked product that of the - // corresponding export_as-named module. - if (!M->UseExportAsModuleLinkName) - MD.LinkLibraries = M->LinkLibraries; - - ModuleMap &ModMapInfo = - MDC.ScanInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap(); - - if (auto ModuleMap = ModMapInfo.getModuleMapFileForUniquing(M)) { - SmallString<128> Path = ModuleMap->getNameAsRequested(); - ModMapInfo.canonicalizeModuleMapPath(Path); - MD.ClangModuleMapFile = std::string(Path); - } - - serialization::ModuleFile *MF = - MDC.ScanInstance.getASTReader()->getModuleManager().lookup( - *M->getASTFile()); - MD.FileDepsBaseDir = MF->BaseDirectory; - MDC.ScanInstance.getASTReader()->visitInputFileInfos( - *MF, /*IncludeSystem=*/true, - [&](const serialization::InputFileInfo &IFI, bool IsSystem) { - // The __inferred_module.map file is an insignificant implementation - // detail of implicitly-built modules. The PCM will also report the - // actual on-disk module map file that allowed inferring the module, - // which is what we need for building the module explicitly - // Let's ignore this file. - if (IFI.UnresolvedImportedFilename.ends_with("__inferred_module.map")) - return; - MDC.addFileDep(MD, IFI.UnresolvedImportedFilename); - }); - - llvm::DenseSet<const Module *> SeenDeps; - addAllSubmodulePrebuiltDeps(M, MD, SeenDeps); - addAllSubmoduleDeps(M, MD, SeenDeps); - addAllAffectingClangModules(M, MD, SeenDeps); - - SmallString<0> PathBuf; - PathBuf.reserve(256); - MDC.ScanInstance.getASTReader()->visitInputFileInfos( - *MF, /*IncludeSystem=*/true, - [&](const serialization::InputFileInfo &IFI, bool IsSystem) { - if (MD.IsInStableDirectories) { - auto FullFilePath = ASTReader::ResolveImportedPath( - PathBuf, IFI.UnresolvedImportedFilename, MF->BaseDirectory); - MD.IsInStableDirectories = - isPathInStableDir(MDC.StableDirs, *FullFilePath); - } - if (!(IFI.TopLevel && IFI.ModuleMap)) - return; - if (IFI.UnresolvedImportedFilenameAsRequested.ends_with( - "__inferred_module.map")) - return; - auto ResolvedFilenameAsRequested = ASTReader::ResolveImportedPath( - PathBuf, IFI.UnresolvedImportedFilenameAsRequested, - MF->BaseDirectory); - MD.ModuleMapFileDeps.emplace_back(*ResolvedFilenameAsRequested); - }); - - bool IgnoreCWD = false; - CowCompilerInvocation CI = - MDC.getInvocationAdjustedForModuleBuildWithoutOutputs( - MD, [&](CowCompilerInvocation &BuildInvocation) { - if (any(MDC.Service.getOptimizeArgs() & - (ScanningOptimizations::HeaderSearch | - ScanningOptimizations::VFS))) - optimizeHeaderSearchOpts(BuildInvocation.getMutHeaderSearchOpts(), - *MDC.ScanInstance.getASTReader(), *MF, - MDC.PrebuiltModulesASTMap, - MDC.Service.getOptimizeArgs()); - - if (any(MDC.Service.getOptimizeArgs() & - ScanningOptimizations::SystemWarnings)) - optimizeDiagnosticOpts( - BuildInvocation.getMutDiagnosticOpts(), - BuildInvocation.getFrontendOpts().IsSystemModule); - - IgnoreCWD = any(MDC.Service.getOptimizeArgs() & - ScanningOptimizations::IgnoreCWD) && - isSafeToIgnoreCWD(BuildInvocation); - if (IgnoreCWD) { - llvm::ErrorOr<std::string> CWD = - MDC.ScanInstance.getVirtualFileSystem() - .getCurrentWorkingDirectory(); - if (CWD) - optimizeCWD(BuildInvocation, *CWD); - } - }); - - // Check provided input paths from the invocation for determining - // IsInStableDirectories. - if (MD.IsInStableDirectories) - MD.IsInStableDirectories = - areOptionsInStableDir(MDC.StableDirs, CI.getHeaderSearchOpts()); - - MDC.associateWithContextHash(CI, IgnoreCWD, MD); - - // Finish the compiler invocation. Requires dependencies and the context hash. - MDC.addOutputPaths(CI, MD); - - MD.BuildInfo = std::move(CI); - - MDC.ModularDeps.insert({M, std::move(OwnedMD)}); - - return MD.ID; -} - -static void forEachSubmoduleSorted(const Module *M, - llvm::function_ref<void(const Module *)> F) { - // Submodule order depends on order of header includes for inferred submodules - // we don't care about the exact order, so sort so that it's consistent across - // TUs to improve sharing. - SmallVector<const Module *> Submodules(M->submodules()); - llvm::stable_sort(Submodules, [](const Module *A, const Module *B) { - return A->Name < B->Name; - }); - for (const Module *SubM : Submodules) - F(SubM); -} - -void ModuleDepCollectorPP::addAllSubmodulePrebuiltDeps( - const Module *M, ModuleDeps &MD, - llvm::DenseSet<const Module *> &SeenSubmodules) { - addModulePrebuiltDeps(M, MD, SeenSubmodules); - - forEachSubmoduleSorted(M, [&](const Module *SubM) { - addAllSubmodulePrebuiltDeps(SubM, MD, SeenSubmodules); - }); -} - -void ModuleDepCollectorPP::addModulePrebuiltDeps( - const Module *M, ModuleDeps &MD, - llvm::DenseSet<const Module *> &SeenSubmodules) { - for (const Module *Import : M->Imports) - if (Import->getTopLevelModule() != M->getTopLevelModule()) - if (MDC.isPrebuiltModule(Import->getTopLevelModule())) - if (SeenSubmodules.insert(Import->getTopLevelModule()).second) { - MD.PrebuiltModuleDeps.emplace_back(Import->getTopLevelModule()); - if (MD.IsInStableDirectories) { - auto PrebuiltModulePropIt = MDC.PrebuiltModulesASTMap.find( - MD.PrebuiltModuleDeps.back().PCMFile); - MD.IsInStableDirectories = - (PrebuiltModulePropIt != MDC.PrebuiltModulesASTMap.end()) && - PrebuiltModulePropIt->second.isInStableDir(); - } - } -} - -void ModuleDepCollectorPP::addAllSubmoduleDeps( - const Module *M, ModuleDeps &MD, - llvm::DenseSet<const Module *> &AddedModules) { - addModuleDep(M, MD, AddedModules); - - forEachSubmoduleSorted(M, [&](const Module *SubM) { - addAllSubmoduleDeps(SubM, MD, AddedModules); - }); -} - -void ModuleDepCollectorPP::addOneModuleDep(const Module *M, const ModuleID ID, - ModuleDeps &MD) { - MD.ClangModuleDeps.push_back(std::move(ID)); - if (MD.IsInStableDirectories) - MD.IsInStableDirectories = MDC.ModularDeps[M]->IsInStableDirectories; -} - -void ModuleDepCollectorPP::addModuleDep( - const Module *M, ModuleDeps &MD, - llvm::DenseSet<const Module *> &AddedModules) { - for (const Module *Import : M->Imports) { - if (Import->getTopLevelModule() != M->getTopLevelModule() && - !MDC.isPrebuiltModule(Import)) { - if (auto ImportID = handleTopLevelModule(Import->getTopLevelModule())) - if (AddedModules.insert(Import->getTopLevelModule()).second) - addOneModuleDep(Import->getTopLevelModule(), *ImportID, MD); - } - } -} - -void ModuleDepCollectorPP::addAllAffectingClangModules( - const Module *M, ModuleDeps &MD, - llvm::DenseSet<const Module *> &AddedModules) { - addAffectingClangModule(M, MD, AddedModules); - - for (const Module *SubM : M->submodules()) - addAllAffectingClangModules(SubM, MD, AddedModules); -} - -void ModuleDepCollectorPP::addAffectingClangModule( - const Module *M, ModuleDeps &MD, - llvm::DenseSet<const Module *> &AddedModules) { - for (const Module *Affecting : M->AffectingClangModules) { - assert(Affecting == Affecting->getTopLevelModule() && - "Not quite import not top-level module"); - if (Affecting != M->getTopLevelModule() && - !MDC.isPrebuiltModule(Affecting)) { - if (auto ImportID = handleTopLevelModule(Affecting)) - if (AddedModules.insert(Affecting).second) - addOneModuleDep(Affecting, *ImportID, MD); - } - } -} - -ModuleDepCollector::ModuleDepCollector( - DependencyScanningService &Service, - std::unique_ptr<DependencyOutputOptions> Opts, - CompilerInstance &ScanInstance, DependencyConsumer &C, - DependencyActionController &Controller, CompilerInvocation OriginalCI, - const PrebuiltModulesAttrsMap PrebuiltModulesASTMap, - const ArrayRef<StringRef> StableDirs) - : Service(Service), ScanInstance(ScanInstance), Consumer(C), - Controller(Controller), - PrebuiltModulesASTMap(std::move(PrebuiltModulesASTMap)), - StableDirs(StableDirs), Opts(std::move(Opts)), - CommonInvocation( - makeCommonInvocationForModuleBuild(std::move(OriginalCI))) {} - -void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) { - PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(*this)); -} - -void ModuleDepCollector::attachToASTReader(ASTReader &R) {} - -bool ModuleDepCollector::isPrebuiltModule(const Module *M) { - std::string Name(M->getTopLevelModuleName()); - const auto &PrebuiltModuleFiles = - ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles; - auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(Name); - if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end()) - return false; - assert("Prebuilt module came from the expected AST file" && - PrebuiltModuleFileIt->second == M->getASTFile()->getName()); - return true; -} - -void ModuleDepCollector::addVisibleModules() { - llvm::DenseSet<const Module *> ImportedModules; - auto InsertVisibleModules = [&](const Module *M) { - if (ImportedModules.contains(M)) - return; - - VisibleModules.insert(M->getTopLevelModuleName()); - SmallVector<Module *> Stack; - M->getExportedModules(Stack); - while (!Stack.empty()) { - const Module *CurrModule = Stack.pop_back_val(); - if (ImportedModules.contains(CurrModule)) - continue; - ImportedModules.insert(CurrModule); - VisibleModules.insert(CurrModule->getTopLevelModuleName()); - CurrModule->getExportedModules(Stack); - } - }; - - for (const Module *Import : DirectImports) - InsertVisibleModules(Import); -} - -static StringRef makeAbsoluteAndPreferred(CompilerInstance &CI, StringRef Path, - SmallVectorImpl<char> &Storage) { - if (llvm::sys::path::is_absolute(Path) && - !llvm::sys::path::is_style_windows(llvm::sys::path::Style::native)) - return Path; - Storage.assign(Path.begin(), Path.end()); - CI.getFileManager().makeAbsolutePath(Storage); - llvm::sys::path::make_preferred(Storage); - return StringRef(Storage.data(), Storage.size()); -} - -void ModuleDepCollector::addFileDep(StringRef Path) { - if (Service.getFormat() == ScanningOutputFormat::P1689) { - // Within P1689 format, we don't want all the paths to be absolute path - // since it may violate the traditional make style dependencies info. - FileDeps.emplace_back(Path); - return; - } - - llvm::SmallString<256> Storage; - Path = makeAbsoluteAndPreferred(ScanInstance, Path, Storage); - FileDeps.emplace_back(Path); -} - -void ModuleDepCollector::addFileDep(ModuleDeps &MD, StringRef Path) { - MD.FileDeps.emplace_back(Path); -} |
