[clang] 0e35f56 - [clang][Dependency Scanning][NFC] Move `DependencyScanningAction` to its own header and source files (#160795)

via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 26 10:16:27 PDT 2025


Author: Qiongsi Wu
Date: 2025-09-26T10:16:23-07:00
New Revision: 0e35f56d40d3b4fe44fd69066ba94f54cb9b844a

URL: https://github.com/llvm/llvm-project/commit/0e35f56d40d3b4fe44fd69066ba94f54cb9b844a
DIFF: https://github.com/llvm/llvm-project/commit/0e35f56d40d3b4fe44fd69066ba94f54cb9b844a.diff

LOG: [clang][Dependency Scanning][NFC] Move `DependencyScanningAction` to its own header and source files (#160795)

This is the first of three PRs to land
https://github.com/llvm/llvm-project/pull/160207 in smaller pieces.

This PR is an NFC. It moves `DependencyScanningAction` to its own source
file, so we can later implement a `CompilerInstanceWithContext` in the
new file.

Part of work for rdar://136303612.

Added: 
    clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp
    clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.h

Modified: 
    clang/lib/Tooling/DependencyScanning/CMakeLists.txt
    clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt
index 42a63faa26d3e..53a2728bd5786 100644
--- a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt
+++ b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt
@@ -10,6 +10,7 @@ add_clang_library(clangDependencyScanning
   DependencyScanningService.cpp
   DependencyScanningWorker.cpp
   DependencyScanningTool.cpp
+  DependencyScannerImpl.cpp
   InProcessModuleCache.cpp
   ModuleDepCollector.cpp
 

diff  --git a/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp
new file mode 100644
index 0000000000000..d370bfd0dd10f
--- /dev/null
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.cpp
@@ -0,0 +1,525 @@
+//===- 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/Frontend/FrontendActions.h"
+#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.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::fs::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());
+  }
+};
+} // namespace
+
+/// Sanitize diagnostic options for dependency scan.
+void clang::tooling::dependencies::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);
+  });
+}
+
+bool DependencyScanningAction::runInvocation(
+    std::shared_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;
+  ScanInstance.setBuildingModule(false);
+
+  ScanInstance.createVirtualFileSystem(FS, DiagConsumer);
+
+  // Create the compiler's actual diagnostics engine.
+  sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
+  assert(!DiagConsumerFinished && "attempt to reuse finished consumer");
+  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;
+  // 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.
+  auto *FileMgr = ScanInstance.createFileManager();
+
+  // Use the dependency scanning optimized file system if requested to do so.
+  if (DepFS) {
+    DepFS->resetBypassedPathPrefix();
+    if (!ScanInstance.getHeaderSearchOpts().ModuleCachePath.empty()) {
+      SmallString<256> ModulesCachePath;
+      normalizeModuleCachePath(
+          *FileMgr, ScanInstance.getHeaderSearchOpts().ModuleCachePath,
+          ModulesCachePath);
+      DepFS->setBypassedPathPrefix(ModulesCachePath);
+    }
+
+    ScanInstance.setDependencyDirectivesGetter(
+        std::make_unique<ScanningDependencyDirectivesGetter>(*FileMgr));
+  }
+
+  ScanInstance.createSourceManager(*FileMgr);
+
+  // 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};
+
+  // 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 false;
+
+  // Create the dependency collector that will collect the produced
+  // dependencies.
+  //
+  // This also 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;
+
+  switch (Service.getFormat()) {
+  case ScanningOutputFormat::Make:
+    ScanInstance.addDependencyCollector(
+        std::make_shared<DependencyConsumerForwarder>(
+            std::move(Opts), WorkingDirectory, Consumer));
+    break;
+  case ScanningOutputFormat::P1689:
+  case ScanningOutputFormat::Full:
+    MDC = std::make_shared<ModuleDepCollector>(
+        Service, std::move(Opts), ScanInstance, Consumer, Controller,
+        OriginalInvocation, std::move(PrebuiltModulesASTMap), StableDirs);
+    ScanInstance.addDependencyCollector(MDC);
+    break;
+  }
+
+  // Consider 
diff erent header search and diagnostic options to create
+  // 
diff erent 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;
+
+  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
new file mode 100644
index 0000000000000..32fbcfffde53c
--- /dev/null
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScannerImpl.h
@@ -0,0 +1,82 @@
+//===- 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/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.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,
+      llvm::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::shared_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;
+  llvm::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
+void sanitizeDiagOpts(DiagnosticOptions &DiagOpts);
+
+} // namespace dependencies
+} // namespace tooling
+} // namespace clang
+
+#endif

diff  --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
index 8375732e4aa33..796e587ba9147 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
+#include "DependencyScannerImpl.h"
 #include "clang/Basic/DiagnosticDriver.h"
 #include "clang/Basic/DiagnosticFrontend.h"
 #include "clang/Basic/DiagnosticSerialization.h"
@@ -35,563 +36,6 @@ 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::fs::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());
-}
-
-/// Sanitize diagnostic options for dependency scan.
-static 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);
-  });
-}
-
-// 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());
-  }
-};
-
-/// A clang tool that runs the preprocessor in a mode that's optimized for
-/// dependency scanning for the given compiler invocation.
-class DependencyScanningAction {
-public:
-  DependencyScanningAction(
-      DependencyScanningService &Service, StringRef WorkingDirectory,
-      DependencyConsumer &Consumer, DependencyActionController &Controller,
-      llvm::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::shared_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;
-    ScanInstance.setBuildingModule(false);
-
-    ScanInstance.createVirtualFileSystem(FS, DiagConsumer);
-
-    // Create the compiler's actual diagnostics engine.
-    sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
-    assert(!DiagConsumerFinished && "attempt to reuse finished consumer");
-    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;
-    // 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.
-    auto *FileMgr = ScanInstance.createFileManager();
-
-    // Use the dependency scanning optimized file system if requested to do so.
-    if (DepFS) {
-      DepFS->resetBypassedPathPrefix();
-      if (!ScanInstance.getHeaderSearchOpts().ModuleCachePath.empty()) {
-        SmallString<256> ModulesCachePath;
-        normalizeModuleCachePath(
-            *FileMgr, ScanInstance.getHeaderSearchOpts().ModuleCachePath,
-            ModulesCachePath);
-        DepFS->setBypassedPathPrefix(ModulesCachePath);
-      }
-
-      ScanInstance.setDependencyDirectivesGetter(
-          std::make_unique<ScanningDependencyDirectivesGetter>(*FileMgr));
-    }
-
-    ScanInstance.createSourceManager(*FileMgr);
-
-    // 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};
-
-    // 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 false;
-
-    // Create the dependency collector that will collect the produced
-    // dependencies.
-    //
-    // This also 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;
-
-    switch (Service.getFormat()) {
-    case ScanningOutputFormat::Make:
-      ScanInstance.addDependencyCollector(
-          std::make_shared<DependencyConsumerForwarder>(
-              std::move(Opts), WorkingDirectory, Consumer));
-      break;
-    case ScanningOutputFormat::P1689:
-    case ScanningOutputFormat::Full:
-      MDC = std::make_shared<ModuleDepCollector>(
-          Service, std::move(Opts), ScanInstance, Consumer, Controller,
-          OriginalInvocation, std::move(PrebuiltModulesASTMap), StableDirs);
-      ScanInstance.addDependencyCollector(MDC);
-      break;
-    }
-
-    // Consider 
diff erent header search and diagnostic options to create
-    // 
diff erent 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;
-
-    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;
-  }
-
-  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;
-  llvm::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;
-};
-
-} // end anonymous namespace
-
 DependencyScanningWorker::DependencyScanningWorker(
     DependencyScanningService &Service,
     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)


        


More information about the cfe-commits mailing list