aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Tooling/DependencyScanning
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Tooling/DependencyScanning')
-rw-r--r--clang/lib/Tooling/DependencyScanning/CMakeLists.txt28
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp706
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.h157
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp504
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp21
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp196
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp195
-rw-r--r--clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp120
-rw-r--r--clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp1034
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);
-}