================
@@ -0,0 +1,1507 @@
+//===--- ModulesDriver.cpp - Driver managed module builds 
-----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines functionality to support driver managed builds for
+/// compilations which use Clang modules or standard C++20 named modules.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Driver/ModulesDriver.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/DependencyScanning/DependencyScanningUtils.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Driver/Job.h"
+#include "clang/Driver/Tool.h"
+#include "clang/Driver/ToolChain.h"
+#include "clang/Frontend/StandaloneDiagnostic.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/DepthFirstIterator.h"
+#include "llvm/ADT/DirectedGraph.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVectorExtras.h"
+#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/GraphWriter.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/ThreadPool.h"
+#include <algorithm>
+#include <utility>
+
+namespace deps = clang::dependencies;
+
+using namespace llvm::opt;
+using namespace clang;
+using namespace driver;
+using namespace modules;
+
+namespace clang::driver::modules {
+static bool fromJSON(const llvm::json::Value &Params,
+                     StdModuleManifest::Module::LocalArguments &LocalArgs,
+                     llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O.mapOptional("system-include-directories",
+                       LocalArgs.SystemIncludeDirs);
+}
+
+static bool fromJSON(const llvm::json::Value &Params,
+                     StdModuleManifest::Module &ModuleEntry,
+                     llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O.map("is-std-library", ModuleEntry.IsStdlib) &&
+         O.map("logical-name", ModuleEntry.LogicalName) &&
+         O.map("source-path", ModuleEntry.SourcePath) &&
+         O.mapOptional("local-arguments", ModuleEntry.LocalArgs);
+}
+
+static bool fromJSON(const llvm::json::Value &Params,
+                     StdModuleManifest &Manifest, llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O.map("modules", Manifest.Modules);
+}
+} // namespace clang::driver::modules
+
+/// Parses the Standard library module manifest from \p Buffer.
+static Expected<StdModuleManifest> parseManifest(StringRef Buffer) {
+  auto ParsedOrErr = llvm::json::parse(Buffer);
+  if (!ParsedOrErr)
+    return ParsedOrErr.takeError();
+
+  StdModuleManifest Manifest;
+  llvm::json::Path::Root Root;
+  if (!fromJSON(*ParsedOrErr, Manifest, Root))
+    return Root.getError();
+
+  return Manifest;
+}
+
+/// Converts each file path in manifest from relative to absolute.
+///
+/// Each file path in the manifest is expected to be relative the manifest's
+/// location \p ManifestPath itself.
+static void makeManifestPathsAbsolute(
+    MutableArrayRef<StdModuleManifest::Module> ManifestEntries,
+    StringRef ManifestPath) {
+  StringRef ManifestDir = llvm::sys::path::parent_path(ManifestPath);
+  SmallString<256> TempPath;
+
+  auto PrependManifestDir = [&](StringRef Path) {
+    TempPath = ManifestDir;
+    llvm::sys::path::append(TempPath, Path);
+    return std::string(TempPath);
+  };
+
+  for (auto &Entry : ManifestEntries) {
+    Entry.SourcePath = PrependManifestDir(Entry.SourcePath);
+    if (!Entry.LocalArgs)
+      continue;
+
+    for (auto &IncludeDir : Entry.LocalArgs->SystemIncludeDirs)
+      IncludeDir = PrependManifestDir(IncludeDir);
+  }
+}
+
+Expected<StdModuleManifest>
+driver::modules::readStdModuleManifest(StringRef ManifestPath,
+                                       llvm::vfs::FileSystem &VFS) {
+  auto MemBufOrErr = VFS.getBufferForFile(ManifestPath);
+  if (!MemBufOrErr)
+    return llvm::createFileError(ManifestPath, MemBufOrErr.getError());
+
+  auto ManifestOrErr = parseManifest((*MemBufOrErr)->getBuffer());
+  if (!ManifestOrErr)
+    return ManifestOrErr.takeError();
+  auto Manifest = std::move(*ManifestOrErr);
+
+  makeManifestPathsAbsolute(Manifest.Modules, ManifestPath);
+  return Manifest;
+}
+
+void driver::modules::buildStdModuleManifestInputs(
+    ArrayRef<StdModuleManifest::Module> ManifestEntries, Compilation &C,
+    InputList &Inputs) {
+  DerivedArgList &Args = C.getArgs();
+  const OptTable &Opts = C.getDriver().getOpts();
+  for (const auto &Entry : ManifestEntries) {
+    auto *InputArg =
+        makeInputArg(Args, Opts, Args.MakeArgString(Entry.SourcePath));
+    Inputs.emplace_back(types::TY_CXXModule, InputArg);
+  }
+}
+
+/// Computes the -fmodule-cache-path for this compilation.
+static std::optional<std::string>
+getModuleCachePath(llvm::opt::DerivedArgList &Args) {
+  if (const Arg *A = Args.getLastArg(options::OPT_fmodules_cache_path))
+    return A->getValue();
+
+  if (SmallString<128> Path; Driver::getDefaultModuleCachePath(Path))
+    return std::string(Path);
+
+  return std::nullopt;
+}
+
+using ManifestEntryLookup =
+    llvm::DenseMap<StringRef, const StdModuleManifest::Module *>;
+
+/// Builds a mapping from a module's source path to its entry in the manifest.
+static ManifestEntryLookup
+buildManifestLookupMap(ArrayRef<StdModuleManifest::Module> ManifestEntries) {
+  ManifestEntryLookup ManifestEntryBySource;
+  for (auto &Entry : ManifestEntries) {
+    const bool Inserted =
+        ManifestEntryBySource.try_emplace(Entry.SourcePath, &Entry).second;
+    assert(Inserted &&
+           "Manifest defines multiple modules with the same source path.");
+  }
+  return ManifestEntryBySource;
+}
+
+/// Returns the manifest entry corresponding to \p Job, or \c nullptr if none
+/// exists.
+static const StdModuleManifest::Module *
+getManifestEntryForCommand(const Command &Job,
+                           const ManifestEntryLookup &ManifestLookup) {
+  for (const auto &II : Job.getInputInfos()) {
+    if (const auto It = ManifestLookup.find(II.getFilename());
+        It != ManifestLookup.end())
+      return It->second;
+  }
+  return nullptr;
+}
+
+/// Adds all \p SystemIncludeDirs to \p Job's arguments.
+static void
+addSystemIncludeDirsFromManifest(Compilation &C, Command &Job,
+                                 ArrayRef<std::string> SystemIncludeDirs) {
+  const ToolChain &TC = Job.getCreator().getToolChain();
+  const DerivedArgList &TCArgs =
+      C.getArgsForToolChain(&TC, Job.getSource().getOffloadingArch(),
+                            Job.getSource().getOffloadingDeviceKind());
+
+  ArgStringList NewArgs = Job.getArguments();
+  for (const auto &IncludeDir : SystemIncludeDirs)
+    TC.addSystemInclude(TCArgs, NewArgs, IncludeDir);
+  Job.replaceArguments(NewArgs);
+}
+
+static bool isCC1Job(const Command &Job) {
+  return StringRef(Job.getCreator().getName()) == "clang";
+}
+
+/// For each job that generates a Standard library module, applies any
+/// local arguments specified in the corresponding module manifest entry.
+static void
+applyLocalArgsFromManifest(Compilation &C,
+                           const ManifestEntryLookup &ManifestLookup,
+                           MutableArrayRef<std::unique_ptr<Command>> Jobs) {
+  for (auto &Job : Jobs) {
+    if (!isCC1Job(*Job))
+      continue;
+
+    const auto *Entry = getManifestEntryForCommand(*Job, ManifestLookup);
+    if (!Entry || !Entry->LocalArgs)
+      continue;
+
+    const auto &IncludeDirs = Entry->LocalArgs->SystemIncludeDirs;
+    addSystemIncludeDirsFromManifest(C, *Job, IncludeDirs);
+  }
+}
+
+/// Returns true if a dependency scan can be performed using \p Job.
+static bool isDependencyScannableJob(const Command &Job) {
+  if (!isCC1Job(Job))
+    return false;
+  const auto &InputInfos = Job.getInputInfos();
+  return !InputInfos.empty() && types::isSrcFile(InputInfos.front().getType());
+}
+
+namespace {
+/// Pool of reusable dependency scanning workers and their contexts with
+/// RAII-based acquire/release.
+class ScanningWorkerPool {
+public:
+  ScanningWorkerPool(size_t NumWorkers,
+                     deps::DependencyScanningService &ScanningService) {
+    for (size_t I = 0; I < NumWorkers; ++I)
+      Slots.emplace_back(ScanningService);
+
+    AvailableSlots.resize(NumWorkers);
+    std::iota(AvailableSlots.begin(), AvailableSlots.end(), 0);
+  }
+
+  /// Acquires a unique pointer to a dependency scanning worker and its
+  /// context.
+  ///
+  /// The worker bundle automatically released back to the pool when the
+  /// pointer is destroyed. The pool has to outlive the leased worker bundle.
+  [[nodiscard]] auto scopedAcquire() {
+    std::unique_lock<std::mutex> UL(Lock);
+    CV.wait(UL, [&] { return !AvailableSlots.empty(); });
+    const size_t Index = AvailableSlots.pop_back_val();
+    auto ReleaseHandle = [this, Index](WorkerBundle *) { release(Index); };
+    return std::unique_ptr<WorkerBundle, decltype(ReleaseHandle)>(
+        &Slots[Index], ReleaseHandle);
+  }
+
+private:
+  /// Releases the worker bundle at \c Index back into the pool.
+  void release(size_t Index) {
+    {
+      std::scoped_lock<std::mutex> SL(Lock);
+      AvailableSlots.push_back(Index);
+    }
+    CV.notify_one();
+  }
+
+  /// A scanning worker with its associated context.
+  struct WorkerBundle {
+    WorkerBundle(deps::DependencyScanningService &ScanningService)
+        : Worker(std::make_unique<deps::DependencyScanningWorker>(
+              ScanningService)) {}
+
+    std::unique_ptr<deps::DependencyScanningWorker> Worker;
+    llvm::DenseSet<deps::ModuleID> SeenModules;
+  };
+
+  std::mutex Lock;
+  std::condition_variable CV;
+  SmallVector<size_t> AvailableSlots;
+  SmallVector<WorkerBundle, 0> Slots;
+};
+} // anonymous namespace
+
+// Creates a ThreadPool and a corresponding ScanningWorkerPool optimized for
+// the configuration of dependency scan inputs.
+static std::pair<std::unique_ptr<llvm::ThreadPoolInterface>,
+                 std::unique_ptr<ScanningWorkerPool>>
+createOptimalThreadAndWorkerPool(
+    size_t NumScanInputs, bool HasStdlibModuleInputs,
+    deps::DependencyScanningService &ScanningService) {
+  // TODO: Benchmark: Determine the optimal number of worker threads for a
+  // given number of inputs. How many inputs are required for multi-threading
+  // to be beneficial? How many inputs should each thread scan at least?
+#if LLVM_ENABLE_THREADS
+  std::unique_ptr<llvm::ThreadPoolInterface> ThreadPool;
+  size_t WorkerCount;
+
+  if (NumScanInputs == 1 || (HasStdlibModuleInputs && NumScanInputs <= 2)) {
+    auto S = llvm::optimal_concurrency(1);
+    ThreadPool = std::make_unique<llvm::SingleThreadExecutor>(std::move(S));
+    WorkerCount = 1;
+  } else {
+    auto ThreadPoolStrategy = llvm::optimal_concurrency(
+        NumScanInputs - static_cast<size_t>(HasStdlibModuleInputs));
+    ThreadPool = std::make_unique<llvm::DefaultThreadPool>(
+        std::move(ThreadPoolStrategy));
+    const size_t MaxConcurrency = ThreadPool->getMaxConcurrency();
+    const size_t MaxConcurrentlyScannedInputs =
+        NumScanInputs -
+        (HasStdlibModuleInputs && NumScanInputs < MaxConcurrency ? 1 : 0);
+    WorkerCount = std::min(MaxConcurrency, MaxConcurrentlyScannedInputs);
+  }
+#else
+  auto ThreadPool = std::make_unique<llvm::SingleThreadExecutor>();
+  size_t WorkerCount = 1;
+#endif
+
+  return {std::move(ThreadPool),
+          std::make_unique<ScanningWorkerPool>(WorkerCount, ScanningService)};
+}
+
+static StringRef getTriple(const Command &Job) {
+  return Job.getCreator().getToolChain().getTriple().getTriple();
+}
+
+using ModuleNameAndTriple = std::pair<StringRef, StringRef>;
+
+namespace {
+/// Thread-safe registry of Standard library scan inputs.
+struct StdlibModuleScanScheduler {
+  StdlibModuleScanScheduler(const llvm::DenseMap<ModuleNameAndTriple, size_t>
+                                &StdlibModuleScanIndexByID)
+      : StdlibModuleScanIndexByID(StdlibModuleScanIndexByID) {
+    ScheduledScanInputs.reserve(StdlibModuleScanIndexByID.size());
+  }
+
+  /// Returns the indices of scan inputs corresponding to newly imported
+  /// Standard library modules.
+  ///
+  /// Thread-safe.
+  SmallVector<size_t, 2> getNewScanInputs(ArrayRef<std::string> 
NamedModuleDeps,
+                                          StringRef Triple) {
+    SmallVector<size_t, 2> NewScanInputs;
+    std::scoped_lock<std::mutex> Guard(Lock);
+    for (const auto &ModuleName : NamedModuleDeps) {
+      const auto It = StdlibModuleScanIndexByID.find({ModuleName, Triple});
+      if (It == StdlibModuleScanIndexByID.end())
+        continue;
+      const size_t ScanIndex = It->second;
+      const bool AlreadyScheduled =
+          !ScheduledScanInputs.insert(ScanIndex).second;
+      if (AlreadyScheduled)
+        continue;
+      NewScanInputs.push_back(ScanIndex);
+    }
+    return NewScanInputs;
+  }
+
+private:
+  const llvm::DenseMap<ModuleNameAndTriple, size_t> &StdlibModuleScanIndexByID;
+  llvm::SmallDenseSet<size_t> ScheduledScanInputs;
+  std::mutex Lock;
+};
+
+/// Collects diagnostics in a form that can be retained until after their
+/// associated SourceManager is destroyed.
+class StandaloneDiagCollector : public DiagnosticConsumer {
+public:
+  void BeginSourceFile(const LangOptions &LangOpts,
+                       const Preprocessor *PP = nullptr) override {
+    this->LangOpts = &LangOpts;
+  }
+
+  void HandleDiagnostic(DiagnosticsEngine::Level Level,
+                        const Diagnostic &Info) override {
+    StoredDiagnostic StoredDiag(Level, Info);
+    StandaloneDiags.emplace_back(*LangOpts, StoredDiag);
+    DiagnosticConsumer::HandleDiagnostic(Level, Info);
+  }
+
+  SmallVector<StandaloneDiagnostic, 0> takeDiagnostics() {
+    return std::move(StandaloneDiags);
+  }
+
+private:
+  const LangOptions *LangOpts = nullptr;
+  SmallVector<StandaloneDiagnostic, 0> StandaloneDiags;
+};
+
+/// RAII utility to report collected StandaloneDiagnostic through a
+/// DiagnosticsEngine.
+///
+/// The driver's DiagnosticsEngine usually does not have a SourceManager at
+/// this point of building the compilation, in which case the
+/// StandaloneDiagReporter supplies its own.
+class StandaloneDiagReporter {
+public:
+  explicit StandaloneDiagReporter(DiagnosticsEngine &Diags) : Diags(Diags) {
+    if (!Diags.hasSourceManager()) {
+      FileSystemOptions Opts;
+      Opts.WorkingDir = ".";
+      OwnedFileMgr = llvm::makeIntrusiveRefCnt<FileManager>(std::move(Opts));
+      OwnedSrcMgr =
+          llvm::makeIntrusiveRefCnt<SourceManager>(Diags, *OwnedFileMgr);
+    }
+  }
+
+  /// Emits all diagnostics in \c StandaloneDiags using the associated
+  /// DiagnosticsEngine.
+  void Report(ArrayRef<StandaloneDiagnostic> StandaloneDiags) const {
+    llvm::StringMap<SourceLocation> SrcLocCache;
+    Diags.getClient()->BeginSourceFile(LangOptions(), nullptr);
+    for (auto &StandaloneDiag : StandaloneDiags) {
+      const auto StoredDiag = translateStandaloneDiag(
+          getFileManager(), getSourceManager(), StandaloneDiag, SrcLocCache);
+      Diags.Report(StoredDiag);
+    }
+    Diags.getClient()->EndSourceFile();
+  }
+
+private:
+  DiagnosticsEngine &Diags;
+  IntrusiveRefCntPtr<FileManager> OwnedFileMgr;
+  IntrusiveRefCntPtr<SourceManager> OwnedSrcMgr;
+
+  FileManager &getFileManager() const {
+    if (OwnedFileMgr)
+      return *OwnedFileMgr;
+    return Diags.getSourceManager().getFileManager();
+  }
+
+  SourceManager &getSourceManager() const {
+    if (OwnedSrcMgr)
+      return *OwnedSrcMgr;
+    return Diags.getSourceManager();
+  }
+};
+} // anonymous namespace
+
+/// Report the diagnostics collected during each dependency scan.
+static void reportAllScanDiagnostics(
+    SmallVectorImpl<SmallVector<StandaloneDiagnostic, 0>> &&AllScanDiags,
+    DiagnosticsEngine &Diags) {
+  StandaloneDiagReporter Reporter(Diags);
+  for (auto &SingleScanDiags : AllScanDiags)
+    Reporter.Report(SingleScanDiags);
+}
+
+/// Construct a path for the explicitly built PCM.
+static std::string constructPCMPath(const deps::ModuleID &ID,
+                                    StringRef OutputDir) {
+  assert(!ID.ModuleName.empty() && !ID.ContextHash.empty() &&
+         "Invalid ModuleID!");
+  SmallString<256> ExplicitPCMPath(OutputDir);
+  llvm::sys::path::append(ExplicitPCMPath, ID.ContextHash,
+                          ID.ModuleName + "-" + ID.ContextHash + ".pcm");
+  return std::string(ExplicitPCMPath);
+}
+
+namespace {
+/// A simple dependency action controller that only provides module lookup for
+/// Clang modules.
+class ModuleLookupController : public deps::DependencyActionController {
+public:
+  ModuleLookupController(StringRef OutputDir) : OutputDir(OutputDir) {}
+
+  std::string lookupModuleOutput(const deps::ModuleDeps &MD,
+                                 deps::ModuleOutputKind Kind) override {
+    if (Kind == deps::ModuleOutputKind::ModuleFile)
+      return constructPCMPath(MD.ID, OutputDir);
+
+    // Driver command lines that trigger lookups for unsupported
+    // ModuleOutputKinds are not supported by the modules driver. Those
+    // command lines should probably be adjusted or rejected in
+    // Driver::handleArguments or Driver::HandleImmediateArgs.
+    llvm::reportFatalInternalError(
+        "call to lookupModuleOutput with unexpected ModuleOutputKind");
+  }
+
+private:
+  StringRef OutputDir;
+};
+
+/// The full dependencies for a specific command-line input.
+struct InputDependencies {
+  /// The name of the C++20 module provided by this translation unit.
+  std::string ModuleName;
+
+  /// A list of modules this translation unit directly depends on, not 
including
+  /// transitive dependencies.
+  ///
+  /// This may include modules with a different context hash when it can be
+  /// determined that the differences are benign for this compilation.
+  std::vector<deps::ModuleID> ClangModuleDeps;
+
+  /// A list of the C++20 named modules this translation unit depends on.
+  ///
+  /// All C++20 named module dependencies are expected to target the same 
triple
+  /// as this translation unit.
+  std::vector<std::string> NamedModuleDeps;
+
+  /// A collection of absolute paths to files that this translation unit
+  /// directly depends on, not including transitive dependencies.
+  std::vector<std::string> FileDeps;
+
+  /// The compiler invocation with modifications to properly import all Clang
+  /// module dependencies. Does not include argv[0].
+  std::vector<std::string> BuildArgs;
+};
+} // anonymous namespace
+
+static InputDependencies makeInputDeps(deps::TranslationUnitDeps &&TUDeps) {
+  InputDependencies InputDeps;
+  InputDeps.ModuleName = std::move(TUDeps.ID.ModuleName);
+  InputDeps.NamedModuleDeps = std::move(TUDeps.NamedModuleDeps);
+  InputDeps.ClangModuleDeps = std::move(TUDeps.ClangModuleDeps);
+  InputDeps.FileDeps = std::move(TUDeps.FileDeps);
+  assert(TUDeps.Commands.size() == 1 && "Expected exactly one command");
+  InputDeps.BuildArgs = std::move(TUDeps.Commands.front().Arguments);
+  return InputDeps;
+}
+
+/// Constructs the full command line, including the executable, for \p Job.
+static SmallVector<std::string, 0> buildCommandLine(const Command &Job) {
+  const auto &JobArgs = Job.getArguments();
+  SmallVector<std::string, 0> CommandLine;
+  CommandLine.reserve(JobArgs.size() + 1);
+  CommandLine.emplace_back(Job.getExecutable());
+  for (const char *Arg : JobArgs)
+    CommandLine.emplace_back(Arg);
+  return CommandLine;
+}
+
+/// Performs a dependency scan for a single job.
+///
+/// \returns a pair containing TranslationUnitDeps on success, or std::nullopt
+/// on failure, along with any diagnostics produced.
+static std::pair<std::optional<deps::TranslationUnitDeps>,
+                 SmallVector<StandaloneDiagnostic, 0>>
+scanDependenciesForJob(const Command &Job, ScanningWorkerPool &WorkerPool,
+                       ModuleLookupController &LookupController) {
+  StandaloneDiagCollector DiagConsumer;
+  std::optional<deps::TranslationUnitDeps> MaybeTUDeps;
+
+  {
+    const auto CC1CommandLine = buildCommandLine(Job);
+    auto WorkerBundleHandle = WorkerPool.scopedAcquire();
+    deps::FullDependencyConsumer DepConsumer(WorkerBundleHandle->SeenModules);
+
+    if (WorkerBundleHandle->Worker->computeDependencies(
+            /*WorkingDirectory*/ ".", CC1CommandLine, DepConsumer,
+            LookupController, DiagConsumer))
+      MaybeTUDeps = DepConsumer.takeTranslationUnitDeps();
+  }
+
+  return {std::move(MaybeTUDeps), DiagConsumer.takeDiagnostics()};
+}
+
+namespace {
+struct DependencyScanResult {
+  /// Indices of jobs that were successfully scanned.
+  SmallVector<size_t> ScannedJobIndices;
+
+  /// Input dependencies for scanned jobs. Parallel to \c ScannedJobIndices.
+  SmallVector<InputDependencies, 0> InputDepsForScannedJobs;
+
+  /// Module dependency graphs for scanned jobs. Parallel to \c
+  /// ScannedJobIndices.
+  SmallVector<deps::ModuleDepsGraph, 0> ModuleDepGraphsForScannedJobs;
+
+  /// Indices of Standard library module jobs not discovered as dependencies.
+  SmallVector<size_t> UnusedStdlibModuleJobIndices;
+
+  /// Indices of jobs that could not be scanned (e.g. image jobs, ...).
+  SmallVector<size_t> NonScannableJobIndices;
+};
+} // anonymous namespace
+
+/// Scans the compilations job list \p Jobs for module dependencies.
+///
+/// Standard library module jobs are scanned on demand if imported by any
+/// user-provided input.
+///
+/// \returns DependencyScanResult on success, or std::nullopt on failure, with
+/// diagnostics reported via \p Diags in both cases.
+static std::optional<DependencyScanResult> scanDependencies(
+    ArrayRef<std::unique_ptr<Command>> Jobs,
+    llvm::DenseMap<StringRef, const StdModuleManifest::Module *> 
ManifestLookup,
+    StringRef ModuleCachePath, IntrusiveRefCntPtr<llvm::vfs::FileSystem> 
BaseFS,
+    DiagnosticsEngine &Diags) {
+  llvm::PrettyStackTraceString CrashInfo("Performing module dependency scan.");
+
+  // Classify the jobs based on scan eligibility.
+  SmallVector<size_t> ScannableJobIndices;
+  SmallVector<size_t> NonScannableJobIndices;
+  for (auto &&[Index, Job] : llvm::enumerate(Jobs)) {
+    if (isDependencyScannableJob(*Job))
+      ScannableJobIndices.push_back(Index);
+    else
+      NonScannableJobIndices.push_back(Index);
+  }
+
+  // Classify scannable jobs by origin. User-provided inputs will be scanned
+  // immediately, while Standard library modules are indexed for on-demand
+  // scanning when discovered as dependencies.
+  SmallVector<size_t> UserInputScanIndices;
+  llvm::DenseMap<ModuleNameAndTriple, size_t> StdlibModuleScanIndexByID;
+  for (auto &&[ScanIndex, JobIndex] : llvm::enumerate(ScannableJobIndices)) {
+    const Command &ScanJob = *Jobs[JobIndex];
+    if (const auto *Entry =
+            getManifestEntryForCommand(ScanJob, ManifestLookup)) {
+      ModuleNameAndTriple ID{Entry->LogicalName, getTriple(ScanJob)};
+      const bool Inserted =
+          StdlibModuleScanIndexByID.try_emplace(ID, ScanIndex).second;
+      assert(Inserted &&
+             "Multiple jobs build the same module for the same triple.");
+    } else {
+      UserInputScanIndices.push_back(ScanIndex);
+    }
+  }
+
+  // Initialize the scan context.
+  const size_t NumScanInputs = ScannableJobIndices.size();
+  const bool HasStdlibModuleInputs = !StdlibModuleScanIndexByID.empty();
+
+  deps::DependencyScanningServiceOptions Opts;
+  Opts.MakeVFS = [&] { return BaseFS; };
----------------
naveen-seth wrote:

Addressed in 
[65cf582](https://github.com/llvm/llvm-project/pull/152770/commits/65cf58254ece12180db4b5b4fd4dd85f8678c34d).

https://github.com/llvm/llvm-project/pull/152770
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to