1//===--- ModulesDriver.cpp - Driver managed module builds -----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file defines functionality to support driver managed builds for
11/// compilations which use Clang modules or standard C++20 named modules.
12///
13//===----------------------------------------------------------------------===//
14
15#include "clang/Driver/ModulesDriver.h"
16#include "clang/Basic/Diagnostic.h"
17#include "clang/Basic/LLVM.h"
18#include "clang/DependencyScanning/DependencyScanningUtils.h"
19#include "clang/Driver/Compilation.h"
20#include "clang/Driver/Driver.h"
21#include "clang/Driver/Job.h"
22#include "clang/Driver/Tool.h"
23#include "clang/Driver/ToolChain.h"
24#include "clang/Driver/Types.h"
25#include "clang/Frontend/StandaloneDiagnostic.h"
26#include "llvm/ADT/DenseSet.h"
27#include "llvm/ADT/DepthFirstIterator.h"
28#include "llvm/ADT/DirectedGraph.h"
29#include "llvm/ADT/PostOrderIterator.h"
30#include "llvm/ADT/STLExtras.h"
31#include "llvm/ADT/SmallVectorExtras.h"
32#include "llvm/ADT/TypeSwitch.h"
33#include "llvm/ADT/iterator_range.h"
34#include "llvm/Option/ArgList.h"
35#include "llvm/Support/Casting.h"
36#include "llvm/Support/GraphWriter.h"
37#include "llvm/Support/JSON.h"
38#include "llvm/Support/Path.h"
39#include "llvm/Support/PrettyStackTrace.h"
40#include "llvm/Support/ThreadPool.h"
41#include "llvm/Support/VirtualFileSystem.h"
42#include <utility>
43
44namespace deps = clang::dependencies;
45
46using namespace llvm::opt;
47using namespace clang;
48using namespace driver;
49using namespace modules;
50
51void driver::modules::diagnoseModulesDriverArgs(llvm::opt::DerivedArgList &DAL,
52 DiagnosticsEngine &Diags) {
53 if (!DAL.hasFlag(Pos: options::OPT_fmodules_reduced_bmi,
54 Neg: options::OPT_fno_modules_reduced_bmi, Default: true)) {
55 Diags.Report(DiagID: diag::err_drv_modules_driver_requires_reduced_bmi);
56 }
57}
58
59namespace clang::driver::modules {
60static bool fromJSON(const llvm::json::Value &Params,
61 StdModuleManifest::Module::LocalArguments &LocalArgs,
62 llvm::json::Path P) {
63 llvm::json::ObjectMapper O(Params, P);
64 return O.mapOptional(Prop: "system-include-directories",
65 Out&: LocalArgs.SystemIncludeDirs);
66}
67
68static bool fromJSON(const llvm::json::Value &Params,
69 StdModuleManifest::Module &ModuleEntry,
70 llvm::json::Path P) {
71 llvm::json::ObjectMapper O(Params, P);
72 return O.map(Prop: "is-std-library", Out&: ModuleEntry.IsStdlib) &&
73 O.map(Prop: "logical-name", Out&: ModuleEntry.LogicalName) &&
74 O.map(Prop: "source-path", Out&: ModuleEntry.SourcePath) &&
75 O.mapOptional(Prop: "local-arguments", Out&: ModuleEntry.LocalArgs);
76}
77
78static bool fromJSON(const llvm::json::Value &Params,
79 StdModuleManifest &Manifest, llvm::json::Path P) {
80 llvm::json::ObjectMapper O(Params, P);
81 return O.map(Prop: "modules", Out&: Manifest.Modules);
82}
83} // namespace clang::driver::modules
84
85/// Parses the Standard library module manifest from \p Buffer.
86static Expected<StdModuleManifest> parseManifest(StringRef Buffer) {
87 auto ParsedOrErr = llvm::json::parse(JSON: Buffer);
88 if (!ParsedOrErr)
89 return ParsedOrErr.takeError();
90
91 StdModuleManifest Manifest;
92 llvm::json::Path::Root Root;
93 if (!fromJSON(Params: *ParsedOrErr, Manifest, P: Root))
94 return Root.getError();
95
96 return Manifest;
97}
98
99/// Converts each file path in manifest from relative to absolute.
100///
101/// Each file path in the manifest is expected to be relative the manifest's
102/// location \p ManifestPath itself.
103static void makeManifestPathsAbsolute(
104 MutableArrayRef<StdModuleManifest::Module> ManifestEntries,
105 StringRef ManifestPath) {
106 StringRef ManifestDir = llvm::sys::path::parent_path(path: ManifestPath);
107 SmallString<256> TempPath;
108
109 auto PrependManifestDir = [&](StringRef Path) {
110 TempPath = ManifestDir;
111 llvm::sys::path::append(path&: TempPath, a: Path);
112 return std::string(TempPath);
113 };
114
115 for (auto &Entry : ManifestEntries) {
116 Entry.SourcePath = PrependManifestDir(Entry.SourcePath);
117 if (!Entry.LocalArgs)
118 continue;
119
120 for (auto &IncludeDir : Entry.LocalArgs->SystemIncludeDirs)
121 IncludeDir = PrependManifestDir(IncludeDir);
122 }
123}
124
125Expected<StdModuleManifest>
126driver::modules::readStdModuleManifest(StringRef ManifestPath,
127 llvm::vfs::FileSystem &VFS) {
128 auto MemBufOrErr = VFS.getBufferForFile(Name: ManifestPath);
129 if (!MemBufOrErr)
130 return llvm::createFileError(F: ManifestPath, EC: MemBufOrErr.getError());
131
132 auto ManifestOrErr = parseManifest(Buffer: (*MemBufOrErr)->getBuffer());
133 if (!ManifestOrErr)
134 return ManifestOrErr.takeError();
135 auto Manifest = std::move(*ManifestOrErr);
136
137 makeManifestPathsAbsolute(ManifestEntries: Manifest.Modules, ManifestPath);
138 return Manifest;
139}
140
141void driver::modules::buildStdModuleManifestInputs(
142 ArrayRef<StdModuleManifest::Module> ManifestEntries, Compilation &C,
143 InputList &Inputs) {
144 DerivedArgList &Args = C.getArgs();
145 const OptTable &Opts = C.getDriver().getOpts();
146 for (const auto &Entry : ManifestEntries) {
147 auto *InputArg =
148 makeInputArg(Args, Opts, Value: Args.MakeArgString(Str: Entry.SourcePath));
149 Inputs.emplace_back(Args: types::TY_CXXStdModule, Args&: InputArg);
150 }
151}
152
153using ManifestEntryLookup =
154 llvm::DenseMap<StringRef, const StdModuleManifest::Module *>;
155
156/// Builds a mapping from a module's source path to its entry in the manifest.
157static ManifestEntryLookup
158buildManifestLookupMap(ArrayRef<StdModuleManifest::Module> ManifestEntries) {
159 ManifestEntryLookup ManifestEntryBySource;
160 for (auto &Entry : ManifestEntries) {
161 [[maybe_unused]] const bool Inserted =
162 ManifestEntryBySource.try_emplace(Key: Entry.SourcePath, Args: &Entry).second;
163 assert(Inserted &&
164 "Manifest defines multiple modules with the same source path.");
165 }
166 return ManifestEntryBySource;
167}
168
169/// Returns the manifest entry corresponding to \p Job, or \c nullptr if none
170/// exists.
171static const StdModuleManifest::Module *
172getManifestEntryForCommand(const Command &Job,
173 const ManifestEntryLookup &ManifestEntryBySource) {
174 for (const auto &II : Job.getInputInfos()) {
175 if (const auto It = ManifestEntryBySource.find(Val: II.getFilename());
176 It != ManifestEntryBySource.end())
177 return It->second;
178 }
179 return nullptr;
180}
181
182/// Adds all \p SystemIncludeDirs to the \p CC1Args of \p Job.
183static void
184addSystemIncludeDirsFromManifest(Compilation &C, Command &Job,
185 ArgStringList &CC1Args,
186 ArrayRef<std::string> SystemIncludeDirs) {
187 const ToolChain &TC = Job.getCreator().getToolChain();
188 const DerivedArgList &TCArgs =
189 C.getArgsForToolChain(TC: &TC, BA: Job.getSource().getOffloadingArch(),
190 DeviceOffloadKind: Job.getSource().getOffloadingDeviceKind());
191
192 for (const auto &IncludeDir : SystemIncludeDirs)
193 TC.addSystemInclude(DriverArgs: TCArgs, CC1Args, Path: IncludeDir);
194}
195
196static bool isCC1Job(const Command &Job) {
197 return StringRef(Job.getCreator().getName()) == "clang";
198}
199
200/// Apply command-line modifications specific for inputs originating from the
201/// Standard library module manifest.
202static void applyArgsForStdModuleManifestInputs(
203 Compilation &C, const ManifestEntryLookup &ManifestEntryBySource,
204 MutableArrayRef<std::unique_ptr<Command>> Jobs) {
205 for (auto &Job : Jobs) {
206 if (!isCC1Job(Job: *Job))
207 continue;
208
209 const auto *Entry = getManifestEntryForCommand(Job: *Job, ManifestEntryBySource);
210 if (!Entry)
211 continue;
212
213 auto CC1Args = Job->getArguments();
214 if (Entry->IsStdlib)
215 CC1Args.push_back(Elt: "-Wno-reserved-module-identifier");
216 if (Entry->LocalArgs)
217 addSystemIncludeDirsFromManifest(C, Job&: *Job, CC1Args,
218 SystemIncludeDirs: Entry->LocalArgs->SystemIncludeDirs);
219 Job->replaceArguments(List: CC1Args);
220 }
221}
222
223/// Computes the -fmodule-cache-path for this compilation.
224static std::optional<std::string>
225getModuleCachePath(llvm::opt::DerivedArgList &Args) {
226 if (const Arg *A = Args.getLastArg(Ids: options::OPT_fmodules_cache_path))
227 return A->getValue();
228
229 if (SmallString<128> Path; Driver::getDefaultModuleCachePath(Result&: Path))
230 return std::string(Path);
231
232 return std::nullopt;
233}
234
235/// Returns true if a dependency scan can be performed using \p Job.
236static bool isDependencyScannableJob(const Command &Job) {
237 if (!isCC1Job(Job))
238 return false;
239 const auto &InputInfos = Job.getInputInfos();
240 return !InputInfos.empty() && types::isSrcFile(Id: InputInfos.front().getType());
241}
242
243namespace {
244/// Pool of reusable dependency scanning workers and their contexts with
245/// RAII-based acquire/release.
246class ScanningWorkerPool {
247public:
248 ScanningWorkerPool(size_t NumWorkers,
249 deps::DependencyScanningService &ScanningService) {
250 for (size_t I = 0; I < NumWorkers; ++I)
251 Slots.emplace_back(Args&: ScanningService);
252
253 AvailableSlots.resize(N: NumWorkers);
254 std::iota(first: AvailableSlots.begin(), last: AvailableSlots.end(), value: 0);
255 }
256
257 /// Acquires a unique pointer to a dependency scanning worker and its
258 /// context.
259 ///
260 /// The worker bundle automatically released back to the pool when the
261 /// pointer is destroyed. The pool has to outlive the leased worker bundle.
262 [[nodiscard]] auto scopedAcquire() {
263 std::unique_lock<std::mutex> UL(Lock);
264 CV.wait(lock&: UL, p: [&] { return !AvailableSlots.empty(); });
265 const size_t Index = AvailableSlots.pop_back_val();
266 auto ReleaseHandle = [this, Index](WorkerBundle *) { release(Index); };
267 return std::unique_ptr<WorkerBundle, decltype(ReleaseHandle)>(
268 &Slots[Index], ReleaseHandle);
269 }
270
271private:
272 /// Releases the worker bundle at \c Index back into the pool.
273 void release(size_t Index) {
274 {
275 std::scoped_lock<std::mutex> SL(Lock);
276 AvailableSlots.push_back(Elt: Index);
277 }
278 CV.notify_one();
279 }
280
281 /// A scanning worker with its associated context.
282 struct WorkerBundle {
283 WorkerBundle(deps::DependencyScanningService &ScanningService)
284 : Worker(std::make_unique<deps::DependencyScanningWorker>(
285 args&: ScanningService)) {}
286
287 std::unique_ptr<deps::DependencyScanningWorker> Worker;
288 llvm::DenseSet<deps::ModuleID> SeenModules;
289 };
290
291 std::mutex Lock;
292 std::condition_variable CV;
293 SmallVector<size_t> AvailableSlots;
294 SmallVector<WorkerBundle, 0> Slots;
295};
296} // anonymous namespace
297
298// Creates a ThreadPool and a corresponding ScanningWorkerPool optimized for
299// the configuration of dependency scan inputs.
300static std::pair<std::unique_ptr<llvm::ThreadPoolInterface>,
301 std::unique_ptr<ScanningWorkerPool>>
302createOptimalThreadAndWorkerPool(
303 size_t NumScanInputs, bool HasStdlibModuleInputs,
304 deps::DependencyScanningService &ScanningService) {
305 // TODO: Benchmark: Determine the optimal number of worker threads for a
306 // given number of inputs. How many inputs are required for multi-threading
307 // to be beneficial? How many inputs should each thread scan at least?
308#if LLVM_ENABLE_THREADS
309 std::unique_ptr<llvm::ThreadPoolInterface> ThreadPool;
310 size_t WorkerCount;
311
312 if (NumScanInputs == 1 || (HasStdlibModuleInputs && NumScanInputs <= 2)) {
313 auto S = llvm::optimal_concurrency(TaskCount: 1);
314 ThreadPool = std::make_unique<llvm::SingleThreadExecutor>(args: std::move(S));
315 WorkerCount = 1;
316 } else {
317 auto ThreadPoolStrategy = llvm::optimal_concurrency(
318 TaskCount: NumScanInputs - static_cast<size_t>(HasStdlibModuleInputs));
319 ThreadPool = std::make_unique<llvm::DefaultThreadPool>(
320 args: std::move(ThreadPoolStrategy));
321 const size_t MaxConcurrency = ThreadPool->getMaxConcurrency();
322 const size_t MaxConcurrentlyScannedInputs =
323 NumScanInputs -
324 (HasStdlibModuleInputs && NumScanInputs < MaxConcurrency ? 1 : 0);
325 WorkerCount = std::min(a: MaxConcurrency, b: MaxConcurrentlyScannedInputs);
326 }
327#else
328 auto ThreadPool = std::make_unique<llvm::SingleThreadExecutor>();
329 size_t WorkerCount = 1;
330#endif
331
332 return {std::move(ThreadPool),
333 std::make_unique<ScanningWorkerPool>(args&: WorkerCount, args&: ScanningService)};
334}
335
336static StringRef getTriple(const Command &Job) {
337 return Job.getCreator().getToolChain().getTriple().getTriple();
338}
339
340using ModuleNameAndTriple = std::pair<StringRef, StringRef>;
341
342namespace {
343/// Helper to schedule on-demand dependency scans for modules originating from
344/// the Standard library module manifest.
345struct StdlibModuleScanScheduler {
346 StdlibModuleScanScheduler(const llvm::DenseMap<ModuleNameAndTriple, size_t>
347 &StdlibModuleScanIndexByID)
348 : StdlibModuleScanIndexByID(StdlibModuleScanIndexByID) {
349 ScheduledScanInputs.reserve(Size: StdlibModuleScanIndexByID.size());
350 }
351
352 /// Returns the indices of scan inputs corresponding to newly imported
353 /// Standard library modules.
354 ///
355 /// Thread-safe.
356 SmallVector<size_t, 2> getNewScanInputs(ArrayRef<std::string> NamedModuleDeps,
357 StringRef Triple) {
358 SmallVector<size_t, 2> NewScanInputs;
359 std::scoped_lock<std::mutex> Guard(Lock);
360 for (const auto &ModuleName : NamedModuleDeps) {
361 const auto It = StdlibModuleScanIndexByID.find(Val: {ModuleName, Triple});
362 if (It == StdlibModuleScanIndexByID.end())
363 continue;
364 const size_t ScanIndex = It->second;
365 const bool AlreadyScheduled =
366 !ScheduledScanInputs.insert(V: ScanIndex).second;
367 if (AlreadyScheduled)
368 continue;
369 NewScanInputs.push_back(Elt: ScanIndex);
370 }
371 return NewScanInputs;
372 }
373
374private:
375 const llvm::DenseMap<ModuleNameAndTriple, size_t> &StdlibModuleScanIndexByID;
376 llvm::SmallDenseSet<size_t> ScheduledScanInputs;
377 std::mutex Lock;
378};
379
380/// Collects diagnostics in a form that can be retained until after their
381/// associated SourceManager is destroyed.
382class StandaloneDiagCollector : public DiagnosticConsumer {
383public:
384 void BeginSourceFile(const LangOptions &LangOpts,
385 const Preprocessor *PP = nullptr) override {
386 this->LangOpts = &LangOpts;
387 }
388
389 void HandleDiagnostic(DiagnosticsEngine::Level Level,
390 const Diagnostic &Info) override {
391 StoredDiagnostic StoredDiag(Level, Info);
392 StandaloneDiags.emplace_back(Args: *LangOpts, Args&: StoredDiag);
393 DiagnosticConsumer::HandleDiagnostic(DiagLevel: Level, Info);
394 }
395
396 SmallVector<StandaloneDiagnostic, 0> takeDiagnostics() {
397 return std::move(StandaloneDiags);
398 }
399
400private:
401 const LangOptions *LangOpts = nullptr;
402 SmallVector<StandaloneDiagnostic, 0> StandaloneDiags;
403};
404
405/// RAII utility to report collected StandaloneDiagnostic through a
406/// DiagnosticsEngine.
407///
408/// The driver's DiagnosticsEngine usually does not have a SourceManager at
409/// this point of building the compilation, in which case the
410/// StandaloneDiagReporter supplies its own.
411class StandaloneDiagReporter {
412public:
413 explicit StandaloneDiagReporter(DiagnosticsEngine &Diags) : Diags(Diags) {
414 if (!Diags.hasSourceManager()) {
415 FileSystemOptions Opts;
416 Opts.WorkingDir = ".";
417 OwnedFileMgr = llvm::makeIntrusiveRefCnt<FileManager>(A: std::move(Opts));
418 OwnedSrcMgr =
419 llvm::makeIntrusiveRefCnt<SourceManager>(A&: Diags, A&: *OwnedFileMgr);
420 }
421 }
422
423 /// Emits all diagnostics in \c StandaloneDiags using the associated
424 /// DiagnosticsEngine.
425 void Report(ArrayRef<StandaloneDiagnostic> StandaloneDiags) const {
426 llvm::StringMap<SourceLocation> SrcLocCache;
427 Diags.getClient()->BeginSourceFile(LangOpts: LangOptions(), PP: nullptr);
428 for (const auto &StandaloneDiag : StandaloneDiags) {
429 const auto StoredDiag = translateStandaloneDiag(
430 FileMgr&: getFileManager(), SrcMgr&: getSourceManager(), StandaloneDiag, SrcLocCache);
431 Diags.Report(storedDiag: StoredDiag);
432 }
433 Diags.getClient()->EndSourceFile();
434 }
435
436private:
437 DiagnosticsEngine &Diags;
438 IntrusiveRefCntPtr<FileManager> OwnedFileMgr;
439 IntrusiveRefCntPtr<SourceManager> OwnedSrcMgr;
440
441 FileManager &getFileManager() const {
442 if (OwnedFileMgr)
443 return *OwnedFileMgr;
444 return Diags.getSourceManager().getFileManager();
445 }
446
447 SourceManager &getSourceManager() const {
448 if (OwnedSrcMgr)
449 return *OwnedSrcMgr;
450 return Diags.getSourceManager();
451 }
452};
453} // anonymous namespace
454
455/// Report the diagnostics collected during each dependency scan.
456static void reportAllScanDiagnostics(
457 SmallVectorImpl<SmallVector<StandaloneDiagnostic, 0>> &&AllScanDiags,
458 DiagnosticsEngine &Diags) {
459 StandaloneDiagReporter Reporter(Diags);
460 for (auto &SingleScanDiags : AllScanDiags)
461 Reporter.Report(StandaloneDiags: SingleScanDiags);
462}
463
464/// Construct a path for the explicitly built PCM.
465static std::string constructPCMPath(const deps::ModuleID &ID,
466 StringRef OutputDir) {
467 assert(!ID.ModuleName.empty() && !ID.ContextHash.empty() &&
468 "Invalid ModuleID!");
469 SmallString<256> ExplicitPCMPath(OutputDir);
470 llvm::sys::path::append(path&: ExplicitPCMPath, a: ID.ContextHash,
471 b: ID.ModuleName + "-" + ID.ContextHash + ".pcm");
472 return std::string(ExplicitPCMPath);
473}
474
475namespace {
476/// A simple dependency action controller that only provides module lookup for
477/// Clang modules.
478class ModuleLookupController : public deps::DependencyActionController {
479public:
480 ModuleLookupController(StringRef OutputDir) : OutputDir(OutputDir) {}
481
482 std::string lookupModuleOutput(const deps::ModuleDeps &MD,
483 deps::ModuleOutputKind Kind) override {
484 if (Kind == deps::ModuleOutputKind::ModuleFile)
485 return constructPCMPath(ID: MD.ID, OutputDir);
486
487 // Driver command lines that trigger lookups for unsupported
488 // ModuleOutputKinds are not supported by the modules driver. Those
489 // command lines should probably be adjusted or rejected in
490 // Driver::handleArguments or Driver::HandleImmediateArgs.
491 llvm::reportFatalInternalError(
492 reason: "call to lookupModuleOutput with unexpected ModuleOutputKind");
493 }
494
495 std::unique_ptr<DependencyActionController> clone() const override {
496 return std::make_unique<ModuleLookupController>(args: OutputDir);
497 }
498
499private:
500 StringRef OutputDir;
501};
502
503/// The full dependencies for a specific command-line input.
504struct InputDependencies {
505 /// The name of the C++20 module provided by this translation unit.
506 std::string ModuleName;
507
508 /// A list of modules this translation unit directly depends on, not including
509 /// transitive dependencies.
510 ///
511 /// This may include modules with a different context hash when it can be
512 /// determined that the differences are benign for this compilation.
513 std::vector<deps::ModuleID> ClangModuleDeps;
514
515 /// A list of the C++20 named modules this translation unit depends on.
516 ///
517 /// These correspond only to modules built with compatible compiler
518 /// invocations.
519 std::vector<std::string> NamedModuleDeps;
520
521 /// A collection of absolute paths to files that this translation unit
522 /// directly depends on, not including transitive dependencies.
523 std::vector<std::string> FileDeps;
524
525 /// The compiler invocation with modifications to properly import all Clang
526 /// module dependencies. Does not include argv[0].
527 std::vector<std::string> BuildArgs;
528};
529} // anonymous namespace
530
531static InputDependencies makeInputDeps(deps::TranslationUnitDeps &&TUDeps) {
532 InputDependencies InputDeps;
533 InputDeps.ModuleName = std::move(TUDeps.ID.ModuleName);
534 InputDeps.NamedModuleDeps = std::move(TUDeps.NamedModuleDeps);
535 InputDeps.ClangModuleDeps = std::move(TUDeps.ClangModuleDeps);
536 InputDeps.FileDeps = std::move(TUDeps.FileDeps);
537 assert(TUDeps.Commands.size() == 1 && "Expected exactly one command");
538 InputDeps.BuildArgs = std::move(TUDeps.Commands.front().Arguments);
539 return InputDeps;
540}
541
542/// Constructs the full command line, including the executable, for \p Job.
543static SmallVector<std::string, 0> buildCommandLine(const Command &Job) {
544 const auto &JobArgs = Job.getArguments();
545 SmallVector<std::string, 0> CommandLine;
546 CommandLine.reserve(N: JobArgs.size() + 1);
547 CommandLine.emplace_back(Args: Job.getExecutable());
548 for (const char *Arg : JobArgs)
549 CommandLine.emplace_back(Args&: Arg);
550 return CommandLine;
551}
552
553/// Performs a dependency scan for a single job.
554///
555/// \returns a pair containing TranslationUnitDeps on success, or std::nullopt
556/// on failure, along with any diagnostics produced.
557static std::pair<std::optional<deps::TranslationUnitDeps>,
558 SmallVector<StandaloneDiagnostic, 0>>
559scanDependenciesForJob(const Command &Job, ScanningWorkerPool &WorkerPool,
560 StringRef WorkingDirectory,
561 ModuleLookupController &LookupController) {
562 StandaloneDiagCollector DiagConsumer;
563 std::optional<deps::TranslationUnitDeps> MaybeTUDeps;
564
565 {
566 const auto CC1CommandLine = buildCommandLine(Job);
567 auto WorkerBundleHandle = WorkerPool.scopedAcquire();
568 deps::FullDependencyConsumer DepConsumer(WorkerBundleHandle->SeenModules);
569
570 if (WorkerBundleHandle->Worker->computeDependencies(
571 WorkingDirectory, CommandLines: {CC1CommandLine}, DepConsumer, Controller&: LookupController,
572 DiagConsumer))
573 MaybeTUDeps = DepConsumer.takeTranslationUnitDeps();
574 }
575
576 return {std::move(MaybeTUDeps), DiagConsumer.takeDiagnostics()};
577}
578
579namespace {
580struct DependencyScanResult {
581 /// Indices of jobs that were successfully scanned.
582 SmallVector<size_t> ScannedJobIndices;
583
584 /// Input dependencies for scanned jobs. Parallel to \c ScannedJobIndices.
585 SmallVector<InputDependencies, 0> InputDepsForScannedJobs;
586
587 /// Module dependency graphs for scanned jobs. Parallel to \c
588 /// ScannedJobIndices.
589 SmallVector<deps::ModuleDepsGraph, 0> ModuleDepGraphsForScannedJobs;
590
591 /// Indices of Standard library module jobs not discovered as dependencies.
592 SmallVector<size_t> UnusedStdlibModuleJobIndices;
593
594 /// Indices of jobs that could not be scanned (e.g. image jobs, ...).
595 SmallVector<size_t> NonScannableJobIndices;
596};
597} // anonymous namespace
598
599/// Scans the compilations job list \p Jobs for module dependencies.
600///
601/// Standard library module jobs are scanned on demand if imported by any
602/// user-provided input.
603///
604/// \returns DependencyScanResult on success, or std::nullopt on failure, with
605/// diagnostics reported via \p Diags in both cases.
606static std::optional<DependencyScanResult> scanDependencies(
607 ArrayRef<std::unique_ptr<Command>> Jobs,
608 llvm::DenseMap<StringRef, const StdModuleManifest::Module *> ManifestLookup,
609 StringRef ModuleCachePath, StringRef WorkingDirectory,
610 StringRef DepScanLogPath, DiagnosticsEngine &Diags) {
611 llvm::PrettyStackTraceString CrashInfo("Performing module dependency scan.");
612
613 // Classify the jobs based on scan eligibility.
614 SmallVector<size_t> ScannableJobIndices;
615 SmallVector<size_t> NonScannableJobIndices;
616 for (const auto &&[Index, Job] : llvm::enumerate(First&: Jobs)) {
617 if (isDependencyScannableJob(Job: *Job))
618 ScannableJobIndices.push_back(Elt: Index);
619 else
620 NonScannableJobIndices.push_back(Elt: Index);
621 }
622
623 // Classify scannable jobs by origin. User-provided inputs will be scanned
624 // immediately, while Standard library modules are indexed for on-demand
625 // scanning when discovered as dependencies.
626 SmallVector<size_t> UserInputScanIndices;
627 llvm::DenseMap<ModuleNameAndTriple, size_t> StdlibModuleScanIndexByID;
628 for (const auto &&[ScanIndex, JobIndex] :
629 llvm::enumerate(First&: ScannableJobIndices)) {
630 const Command &ScanJob = *Jobs[JobIndex];
631 if (const auto *Entry =
632 getManifestEntryForCommand(Job: ScanJob, ManifestEntryBySource: ManifestLookup)) {
633 ModuleNameAndTriple ID{Entry->LogicalName, getTriple(Job: ScanJob)};
634 [[maybe_unused]] const bool Inserted =
635 StdlibModuleScanIndexByID.try_emplace(Key: ID, Args&: ScanIndex).second;
636 assert(Inserted &&
637 "Multiple jobs build the same module for the same triple.");
638 } else {
639 UserInputScanIndices.push_back(Elt: ScanIndex);
640 }
641 }
642
643 // Initialize the scan context.
644 const size_t NumScanInputs = ScannableJobIndices.size();
645 const bool HasStdlibModuleInputs = !StdlibModuleScanIndexByID.empty();
646
647 deps::DependencyScanningServiceOptions Opts;
648 Opts.LogPath = DepScanLogPath.str();
649 deps::DependencyScanningService ScanningService(std::move(Opts));
650
651 std::unique_ptr<llvm::ThreadPoolInterface> ThreadPool;
652 std::unique_ptr<ScanningWorkerPool> WorkerPool;
653 std::tie(args&: ThreadPool, args&: WorkerPool) = createOptimalThreadAndWorkerPool(
654 NumScanInputs, HasStdlibModuleInputs, ScanningService);
655
656 StdlibModuleScanScheduler StdlibModuleRegistry(StdlibModuleScanIndexByID);
657 ModuleLookupController LookupController(ModuleCachePath);
658
659 // Scan results are indexed by ScanIndex into ScannableJobIndices, not by
660 // JobIndex into Jobs. This allows one result slot per scannable job.
661 SmallVector<std::optional<deps::TranslationUnitDeps>, 0> AllScanResults(
662 NumScanInputs);
663 SmallVector<SmallVector<StandaloneDiagnostic, 0>, 0> AllScanDiags(
664 NumScanInputs);
665 std::atomic<bool> HasError{false};
666
667 // Scans the job at the given scan index and schedules scans for any newly
668 // discovered Standard library module dependencies.
669 std::function<void(size_t)> ScanOneAndScheduleNew;
670 ScanOneAndScheduleNew = [&](size_t ScanIndex) {
671 const size_t JobIndex = ScannableJobIndices[ScanIndex];
672 const Command &Job = *Jobs[JobIndex];
673 auto [MaybeTUDeps, ScanDiags] = scanDependenciesForJob(
674 Job, WorkerPool&: *WorkerPool, WorkingDirectory, LookupController);
675
676 // Store diagnostics even for successful scans to also capture any warnings
677 // or notes.
678 assert(AllScanDiags[ScanIndex].empty() &&
679 "Each slot should be written to at most once.");
680 AllScanDiags[ScanIndex] = std::move(ScanDiags);
681
682 if (!MaybeTUDeps) {
683 HasError.store(i: true, m: std::memory_order_relaxed);
684 return;
685 }
686
687 // Schedule scans for newly discovered Standard library module dependencies.
688 const auto NewScanInputs = StdlibModuleRegistry.getNewScanInputs(
689 NamedModuleDeps: MaybeTUDeps->NamedModuleDeps, Triple: getTriple(Job));
690 for (const size_t NewScanIndex : NewScanInputs)
691 ThreadPool->async(
692 F: [&, NewScanIndex]() { ScanOneAndScheduleNew(NewScanIndex); });
693
694 assert(!AllScanResults[ScanIndex].has_value() &&
695 "Each slot should be written to at most once.");
696 AllScanResults[ScanIndex] = std::move(MaybeTUDeps);
697 };
698
699 // Initiate the scan with all jobs for user-provided inputs.
700 for (const size_t ScanIndex : UserInputScanIndices)
701 ThreadPool->async(F: [&ScanOneAndScheduleNew, ScanIndex]() {
702 ScanOneAndScheduleNew(ScanIndex);
703 });
704 ThreadPool->wait();
705
706 reportAllScanDiagnostics(AllScanDiags: std::move(AllScanDiags), Diags);
707 if (HasError.load(m: std::memory_order_relaxed))
708 return std::nullopt;
709
710 // Collect results, mapping scan indices back to job indices.
711 DependencyScanResult Result;
712 for (auto &&[JobIndex, MaybeTUDeps] :
713 llvm::zip_equal(t&: ScannableJobIndices, u&: AllScanResults)) {
714 if (MaybeTUDeps) {
715 Result.ScannedJobIndices.push_back(Elt: JobIndex);
716 Result.ModuleDepGraphsForScannedJobs.push_back(
717 Elt: std::move(MaybeTUDeps->ModuleGraph));
718 Result.InputDepsForScannedJobs.push_back(
719 Elt: makeInputDeps(TUDeps: std::move(*MaybeTUDeps)));
720 } else
721 Result.UnusedStdlibModuleJobIndices.push_back(Elt: JobIndex);
722 }
723 Result.NonScannableJobIndices = std::move(NonScannableJobIndices);
724
725#ifndef NDEBUG
726 llvm::SmallDenseSet<size_t> SeenJobIndices;
727 SeenJobIndices.insert_range(Result.ScannedJobIndices);
728 SeenJobIndices.insert_range(Result.UnusedStdlibModuleJobIndices);
729 SeenJobIndices.insert_range(Result.NonScannableJobIndices);
730 assert(llvm::all_of(llvm::index_range(0, Jobs.size()),
731 [&](size_t JobIndex) {
732 return SeenJobIndices.contains(JobIndex);
733 }) &&
734 "Scan result must partition all jobs");
735#endif
736
737 return Result;
738}
739
740namespace {
741class CGNode;
742class CGEdge;
743using CGNodeBase = llvm::DGNode<CGNode, CGEdge>;
744using CGEdgeBase = llvm::DGEdge<CGNode, CGEdge>;
745using CGBase = llvm::DirectedGraph<CGNode, CGEdge>;
746
747/// Compilation Graph Node
748class CGNode : public CGNodeBase {
749public:
750 enum class NodeKind {
751 ClangModuleCC1Job,
752 NamedModuleCC1Job,
753 NonModuleCC1Job,
754 MiscJob,
755 ImageJob,
756 Root,
757 };
758
759 CGNode(const NodeKind K) : Kind(K) {}
760 CGNode(const CGNode &) = delete;
761 CGNode(CGNode &&) = delete;
762 CGNode &operator=(const CGNode &) = delete;
763 CGNode &operator=(CGNode &&) = delete;
764 virtual ~CGNode() = 0;
765
766 NodeKind getKind() const { return Kind; }
767
768private:
769 NodeKind Kind;
770};
771CGNode::~CGNode() = default;
772
773/// Subclass of CGNode representing the root node of the graph.
774///
775/// The root node is a special node that connects to all other nodes with
776/// no incoming edges, so that there is always a path from it to any node
777/// in the graph.
778///
779/// There should only be one such node in a given graph.
780class RootNode : public CGNode {
781public:
782 RootNode() : CGNode(NodeKind::Root) {}
783 ~RootNode() override = default;
784
785 static bool classof(const CGNode *N) {
786 return N->getKind() == NodeKind::Root;
787 }
788};
789
790/// Base class for any CGNode type that represents a job.
791class JobNode : public CGNode {
792public:
793 JobNode(std::unique_ptr<Command> &&Job, NodeKind Kind)
794 : CGNode(Kind), Job(std::move(Job)) {
795 assert(this->Job && "Expected valid job!");
796 }
797 virtual ~JobNode() override = 0;
798
799 std::unique_ptr<Command> Job;
800
801 static bool classof(const CGNode *N) {
802 return N->getKind() != NodeKind::Root;
803 }
804};
805JobNode::~JobNode() = default;
806
807/// Subclass of CGNode representing a -cc1 job which produces a Clang module.
808class ClangModuleJobNode : public JobNode {
809public:
810 ClangModuleJobNode(std::unique_ptr<Command> &&Job, deps::ModuleDeps &&MD)
811 : JobNode(std::move(Job), NodeKind::ClangModuleCC1Job),
812 MD(std::move(MD)) {}
813 ~ClangModuleJobNode() override = default;
814
815 deps::ModuleDeps MD;
816
817 static bool classof(const CGNode *N) {
818 return N->getKind() == NodeKind::ClangModuleCC1Job;
819 }
820};
821
822/// Base class for any CGNode type that represents any scanned -cc1 job.
823class ScannedJobNode : public JobNode {
824public:
825 ScannedJobNode(std::unique_ptr<Command> &&Job, InputDependencies &&InputDeps,
826 NodeKind Kind)
827 : JobNode(std::move(Job), Kind), InputDeps(std::move(InputDeps)) {}
828 ~ScannedJobNode() override = default;
829
830 InputDependencies InputDeps;
831
832 static bool classof(const CGNode *N) {
833 return N->getKind() == NodeKind::NamedModuleCC1Job ||
834 N->getKind() == NodeKind::NonModuleCC1Job;
835 }
836};
837
838/// Subclass of CGNode representing a -cc1 job which produces a C++20 named
839/// module.
840class NamedModuleJobNode : public ScannedJobNode {
841public:
842 NamedModuleJobNode(std::unique_ptr<Command> &&Job,
843 InputDependencies &&InputDeps)
844 : ScannedJobNode(std::move(Job), std::move(InputDeps),
845 NodeKind::NamedModuleCC1Job) {}
846 ~NamedModuleJobNode() override = default;
847
848 static bool classof(const CGNode *N) {
849 return N->getKind() == NodeKind::NamedModuleCC1Job;
850 }
851};
852
853/// Subclass of CGNode representing a -cc1 job which does not produce any
854/// module, but might still have module imports.
855class NonModuleTUJobNode : public ScannedJobNode {
856public:
857 NonModuleTUJobNode(std::unique_ptr<Command> &&Job,
858 InputDependencies &&InputDeps)
859 : ScannedJobNode(std::move(Job), std::move(InputDeps),
860 NodeKind::NonModuleCC1Job) {}
861 ~NonModuleTUJobNode() override = default;
862
863 static bool classof(const CGNode *N) {
864 return N->getKind() == NodeKind::NonModuleCC1Job;
865 }
866};
867
868/// Subclass of CGNode representing a job which produces an image file, such as
869/// a linker or interface stub merge job.
870class ImageJobNode : public JobNode {
871public:
872 ImageJobNode(std::unique_ptr<Command> &&Job)
873 : JobNode(std::move(Job), NodeKind::ImageJob) {}
874 ~ImageJobNode() override = default;
875
876 static bool classof(const CGNode *N) {
877 return N->getKind() == NodeKind::ImageJob;
878 }
879};
880
881/// Subclass of CGNode representing any job not covered by the other node types.
882///
883/// Jobs represented by this node type are not modified by the modules driver.
884class MiscJobNode : public JobNode {
885public:
886 MiscJobNode(std::unique_ptr<Command> &&Job)
887 : JobNode(std::move(Job), NodeKind::MiscJob) {}
888 ~MiscJobNode() override = default;
889
890 static bool classof(const CGNode *N) {
891 return N->getKind() == NodeKind::MiscJob;
892 }
893};
894
895/// Compilation Graph Edge
896///
897/// Edges connect the producer of an output to its consumer, except for edges
898/// stemming from the root node.
899class CGEdge : public CGEdgeBase {
900public:
901 enum class EdgeKind {
902 Regular,
903 ModuleDependency,
904 Rooted,
905 };
906
907 CGEdge(CGNode &N, EdgeKind K) : CGEdgeBase(N), Kind(K) {}
908 CGEdge(const CGEdge &) = delete;
909 CGEdge &operator=(const CGEdge &) = delete;
910 CGEdge(CGEdge &&) = delete;
911 CGEdge &operator=(CGEdge &&) = delete;
912
913 EdgeKind getKind() const { return Kind; }
914
915private:
916 EdgeKind Kind;
917};
918
919/// Compilation Graph
920///
921/// The graph owns all of its components.
922/// All nodes and edges created by the graph have the same livetime as the
923/// graph, even if removed from the graph's node list.
924class CompilationGraph : public CGBase {
925public:
926 CompilationGraph() = default;
927 CompilationGraph(const CompilationGraph &) = delete;
928 CompilationGraph &operator=(const CompilationGraph &) = delete;
929 CompilationGraph(CompilationGraph &&G) = default;
930 CompilationGraph &operator=(CompilationGraph &&) = default;
931 ~CompilationGraph() = default;
932
933 CGNode &getRoot() const {
934 assert(Root && "Root node has not yet been created!");
935 return *Root;
936 }
937
938 RootNode &createRoot() {
939 assert(!Root && "Root node has already been created!");
940 auto &RootRef = createNodeImpl<RootNode>();
941 Root = &RootRef;
942 return RootRef;
943 }
944
945 template <typename T, typename... Args> T &createJobNode(Args &&...Arg) {
946 static_assert(std::is_base_of<JobNode, T>::value,
947 "T must be derived from JobNode");
948 return createNodeImpl<T>(std::forward<Args>(Arg)...);
949 }
950
951 CGEdge &createEdge(CGEdge::EdgeKind Kind, CGNode &Src, CGNode &Dst) {
952 auto Edge = std::make_unique<CGEdge>(args&: Dst, args&: Kind);
953 CGEdge &EdgeRef = *Edge;
954 AllEdges.push_back(Elt: std::move(Edge));
955 connect(Src, Dst, E&: EdgeRef);
956 return EdgeRef;
957 }
958
959private:
960 using CGBase::addNode;
961 using CGBase::connect;
962
963 template <typename T, typename... Args> T &createNodeImpl(Args &&...Arg) {
964 auto Node = std::make_unique<T>(std::forward<Args>(Arg)...);
965 T &NodeRef = *Node;
966 AllNodes.push_back(std::move(Node));
967 addNode(N&: NodeRef);
968 return NodeRef;
969 }
970
971 CGNode *Root = nullptr;
972 SmallVector<std::unique_ptr<CGNode>> AllNodes;
973 SmallVector<std::unique_ptr<CGEdge>> AllEdges;
974};
975} // anonymous namespace
976
977static StringRef getFirstInputFilename(const Command &Job) {
978 return Job.getInputInfos().front().getFilename();
979}
980
981namespace llvm {
982/// Non-const versions of the GraphTraits specializations for CompilationGraph.
983template <> struct GraphTraits<CGNode *> {
984 using NodeRef = CGNode *;
985
986 static NodeRef CGGetTargetNode(CGEdge *E) { return &E->getTargetNode(); }
987
988 using ChildIteratorType =
989 mapped_iterator<CGNode::iterator, decltype(&CGGetTargetNode)>;
990 using ChildEdgeIteratorType = CGNode::iterator;
991
992 static NodeRef getEntryNode(NodeRef N) { return N; }
993
994 static ChildIteratorType child_begin(NodeRef N) {
995 return ChildIteratorType(N->begin(), &CGGetTargetNode);
996 }
997
998 static ChildIteratorType child_end(NodeRef N) {
999 return ChildIteratorType(N->end(), &CGGetTargetNode);
1000 }
1001
1002 static ChildEdgeIteratorType child_edge_begin(NodeRef N) {
1003 return N->begin();
1004 }
1005 static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); }
1006};
1007
1008template <> struct GraphTraits<CompilationGraph *> : GraphTraits<CGNode *> {
1009 using GraphRef = CompilationGraph *;
1010 using NodeRef = CGNode *;
1011
1012 using nodes_iterator = CompilationGraph::iterator;
1013
1014 static NodeRef getEntryNode(GraphRef G) { return &G->getRoot(); }
1015
1016 static nodes_iterator nodes_begin(GraphRef G) { return G->begin(); }
1017
1018 static nodes_iterator nodes_end(GraphRef G) { return G->end(); }
1019};
1020
1021/// Const versions of the GraphTraits specializations for CompilationGraph.
1022template <> struct GraphTraits<const CGNode *> {
1023 using NodeRef = const CGNode *;
1024
1025 static NodeRef CGGetTargetNode(const CGEdge *E) {
1026 return &E->getTargetNode();
1027 }
1028
1029 using ChildIteratorType =
1030 mapped_iterator<CGNode::const_iterator, decltype(&CGGetTargetNode)>;
1031 using ChildEdgeIteratorType = CGNode::const_iterator;
1032
1033 static NodeRef getEntryNode(NodeRef N) { return N; }
1034
1035 static ChildIteratorType child_begin(NodeRef N) {
1036 return ChildIteratorType(N->begin(), &CGGetTargetNode);
1037 }
1038
1039 static ChildIteratorType child_end(NodeRef N) {
1040 return ChildIteratorType(N->end(), &CGGetTargetNode);
1041 }
1042
1043 static ChildEdgeIteratorType child_edge_begin(NodeRef N) {
1044 return N->begin();
1045 }
1046
1047 static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); }
1048};
1049
1050template <>
1051struct GraphTraits<const CompilationGraph *> : GraphTraits<const CGNode *> {
1052 using GraphRef = const CompilationGraph *;
1053 using NodeRef = const CGNode *;
1054
1055 using nodes_iterator = CompilationGraph::const_iterator;
1056
1057 static NodeRef getEntryNode(GraphRef G) { return &G->getRoot(); }
1058
1059 static nodes_iterator nodes_begin(GraphRef G) { return G->begin(); }
1060
1061 static nodes_iterator nodes_end(GraphRef G) { return G->end(); }
1062};
1063
1064template <>
1065struct DOTGraphTraits<const CompilationGraph *> : DefaultDOTGraphTraits {
1066 explicit DOTGraphTraits(bool IsSimple = false)
1067 : DefaultDOTGraphTraits(IsSimple) {}
1068 using GraphRef = const CompilationGraph *;
1069 using NodeRef = const CGNode *;
1070
1071 static std::string getGraphName(GraphRef) {
1072 return "Module Dependency Graph";
1073 }
1074
1075 static std::string getGraphProperties(GraphRef) {
1076 return "\tnode [shape=Mrecord, colorscheme=set23, style=filled];\n";
1077 }
1078
1079 static bool renderGraphFromBottomUp() { return true; }
1080
1081 static bool isNodeHidden(NodeRef N, GraphRef) {
1082 // Only show nodes with module dependency relations.
1083 return !isa<ClangModuleJobNode, ScannedJobNode>(Val: N);
1084 }
1085
1086 static std::string getNodeIdentifier(NodeRef N, GraphRef) {
1087 return llvm::TypeSwitch<NodeRef, std::string>(N)
1088 .Case(caseFn: [](const ClangModuleJobNode *ClangModuleNode) {
1089 const auto &ID = ClangModuleNode->MD.ID;
1090 return llvm::formatv(Fmt: "{0}-{1}", Vals: ID.ModuleName, Vals: ID.ContextHash).str();
1091 })
1092 .Case(caseFn: [](const NamedModuleJobNode *NamedModuleNode) {
1093 return llvm::formatv(Fmt: "{0}-{1}", Vals: NamedModuleNode->InputDeps.ModuleName,
1094 Vals: getTriple(Job: *NamedModuleNode->Job))
1095 .str();
1096 })
1097 .Case(caseFn: [](const NonModuleTUJobNode *NonModuleTUNode) {
1098 const auto &Job = *NonModuleTUNode->Job;
1099 return llvm::formatv(Fmt: "{0}-{1}", Vals: getFirstInputFilename(Job),
1100 Vals: getTriple(Job))
1101 .str();
1102 })
1103 .DefaultUnreachable(message: "Unexpected node kind! Is this node hidden?");
1104 }
1105
1106 static std::string getNodeLabel(NodeRef N, GraphRef) {
1107 return llvm::TypeSwitch<NodeRef, std::string>(N)
1108 .Case(caseFn: [](const ClangModuleJobNode *ClangModuleNode) {
1109 const auto &ID = ClangModuleNode->MD.ID;
1110 return llvm::formatv(Fmt: "Module type: Clang module \\| Module name: {0} "
1111 "\\| Hash: {1}",
1112 Vals: ID.ModuleName, Vals: ID.ContextHash)
1113 .str();
1114 })
1115 .Case(caseFn: [](const NamedModuleJobNode *NamedModuleNode) {
1116 const auto &Job = *NamedModuleNode->Job;
1117 return llvm::formatv(
1118 Fmt: "Filename: {0} \\| Module type: Named module \\| "
1119 "Module name: {1} \\| Triple: {2}",
1120 Vals: getFirstInputFilename(Job),
1121 Vals: NamedModuleNode->InputDeps.ModuleName, Vals: getTriple(Job))
1122 .str();
1123 })
1124 .Case(caseFn: [](const NonModuleTUJobNode *NonModuleTUNode) {
1125 const auto &Job = *NonModuleTUNode->Job;
1126 return llvm::formatv(Fmt: "Filename: {0} \\| Triple: {1}",
1127 Vals: getFirstInputFilename(Job), Vals: getTriple(Job))
1128 .str();
1129 })
1130 .DefaultUnreachable(message: "Unexpected node kind! Is this node hidden?");
1131 }
1132
1133 static std::string getNodeAttributes(NodeRef N, GraphRef) {
1134 switch (N->getKind()) {
1135 case CGNode::NodeKind::ClangModuleCC1Job:
1136 return "fillcolor=1";
1137 case CGNode::NodeKind::NamedModuleCC1Job:
1138 return "fillcolor=2";
1139 case CGNode::NodeKind::NonModuleCC1Job:
1140 return "fillcolor=3";
1141 default:
1142 llvm_unreachable("Unexpected node kind! Is this node hidden?");
1143 }
1144 }
1145};
1146
1147/// GraphWriter specialization for CompilationGraph that emits a more
1148/// human-readable DOT graph.
1149template <>
1150class GraphWriter<const CompilationGraph *>
1151 : public GraphWriterBase<const CompilationGraph *,
1152 GraphWriter<const CompilationGraph *>> {
1153public:
1154 using GraphType = const CompilationGraph *;
1155 using Base = GraphWriterBase<GraphType, GraphWriter<GraphType>>;
1156
1157 GraphWriter(llvm::raw_ostream &O, const GraphType &G, bool IsSimple)
1158 : Base(O, G, IsSimple), EscapedIDByNodeRef(G->size()) {}
1159
1160 void writeNodes() {
1161 auto IsNodeVisible = [&](NodeRef N) { return !DTraits.isNodeHidden(N, G); };
1162 auto VisibleNodes = llvm::filter_to_vector(C: nodes(G), Pred&: IsNodeVisible);
1163
1164 writeNodeDefinitions(VisibleNodes);
1165 O << "\n";
1166 writeNodeRelations(VisibleNodes);
1167 }
1168
1169private:
1170 using Base::DOTTraits;
1171 using Base::GTraits;
1172 using Base::NodeRef;
1173
1174 void writeNodeDefinitions(ArrayRef<NodeRef> VisibleNodes) {
1175 for (NodeRef Node : VisibleNodes) {
1176 std::string EscapedNodeID =
1177 DOT::EscapeString(Label: DTraits.getNodeIdentifier(N: Node, G));
1178 const std::string NodeLabel = DTraits.getNodeLabel(N: Node, G);
1179 const std::string NodeAttrs = DTraits.getNodeAttributes(N: Node, G);
1180 O << '\t' << '"' << EscapedNodeID << "\" [" << NodeAttrs << ", label=\"{ "
1181 << DOT::EscapeString(Label: NodeLabel) << " }\"];\n";
1182 EscapedIDByNodeRef.try_emplace(Key: Node, Args: std::move(EscapedNodeID));
1183 }
1184 }
1185
1186 void writeNodeRelations(ArrayRef<NodeRef> VisibleNodes) {
1187 auto IsNodeVisible = [&](NodeRef N) { return !DTraits.isNodeHidden(N, G); };
1188 for (NodeRef Node : VisibleNodes) {
1189 auto DstNodes = llvm::make_range(x: GTraits::child_begin(N: Node),
1190 y: GTraits::child_end(N: Node));
1191 auto VisibleDstNodes = llvm::make_filter_range(Range&: DstNodes, Pred: IsNodeVisible);
1192 StringRef EscapedSrcNodeID = EscapedIDByNodeRef.at(Val: Node);
1193 for (NodeRef DstNode : VisibleDstNodes) {
1194 StringRef EscapedTgtNodeID = EscapedIDByNodeRef.at(Val: DstNode);
1195 O << '\t' << '"' << EscapedSrcNodeID << "\" -> \"" << EscapedTgtNodeID
1196 << "\";\n";
1197 }
1198 }
1199 }
1200
1201 DenseMap<NodeRef, std::string> EscapedIDByNodeRef;
1202};
1203} // namespace llvm
1204
1205/// Validates that each module-defining source is of type \c TY_CXXModule.
1206///
1207/// \returns false on error, with diagnostics emitted via \p Diags.
1208static bool validateScannedJobInputKinds(
1209 ArrayRef<std::unique_ptr<Command>> ScannedJobs,
1210 ArrayRef<InputDependencies> InputDepsForScannedJobs,
1211 DiagnosticsEngine &Diags) {
1212 for (const auto &&[Job, InputDeps] : llvm::zip_equal(
1213 t: llvm::make_pointee_range(Range&: ScannedJobs), u&: InputDepsForScannedJobs)) {
1214 const auto &MainInput = Job.getInputInfos().front();
1215 const bool DefinesNamedModule = !InputDeps.ModuleName.empty();
1216
1217 if (DefinesNamedModule && MainInput.getType() != types::TY_CXXModule &&
1218 MainInput.getType() != types::TY_CXXStdModule) {
1219 Diags.Report(DiagID: diag::err_module_defined_outside_of_module_source)
1220 << InputDeps.ModuleName << MainInput.getFilename();
1221 return false;
1222 }
1223 }
1224 return true;
1225}
1226
1227static SmallVector<std::unique_ptr<Command>>
1228takeJobsAtIndices(SmallVectorImpl<std::unique_ptr<Command>> &Jobs,
1229 ArrayRef<size_t> Indices) {
1230 SmallVector<std::unique_ptr<Command>> Out;
1231 for (const auto JobIndex : Indices) {
1232 assert(Jobs[JobIndex] && "Expected valid job!");
1233 Out.push_back(Elt: std::move(Jobs[JobIndex]));
1234 }
1235 return Out;
1236}
1237
1238/// Creates nodes for all jobs that could not be scanned (e.g. image jobs, ...).
1239static void createNodesForNonScannableJobs(
1240 CompilationGraph &Graph,
1241 SmallVectorImpl<std::unique_ptr<Command>> &&NonScannableJobs) {
1242 for (auto &Job : NonScannableJobs) {
1243 if (Job->getCreator().isLinkJob())
1244 Graph.createJobNode<ImageJobNode>(Arg: std::move(Job));
1245 else
1246 Graph.createJobNode<MiscJobNode>(Arg: std::move(Job));
1247 }
1248}
1249
1250/// Creates nodes for the Standard library module jobs not discovered as
1251/// dependencies.
1252///
1253/// These and any dependent (non-image) job nodes should be pruned from the
1254/// graph later.
1255static SmallVector<JobNode *> createNodesForUnusedStdlibModuleJobs(
1256 CompilationGraph &Graph,
1257 SmallVectorImpl<std::unique_ptr<Command>> &&UnusedStdlibModuleJobs) {
1258 SmallVector<JobNode *> StdlibModuleNodesToPrune;
1259 for (auto &Job : UnusedStdlibModuleJobs) {
1260 auto &NewNode = Graph.createJobNode<MiscJobNode>(Arg: std::move(Job));
1261 StdlibModuleNodesToPrune.push_back(Elt: &NewNode);
1262 }
1263 return StdlibModuleNodesToPrune;
1264}
1265
1266// Returns the derived argument list for the tool chain responsible
1267// for creating \p Job.
1268static const DerivedArgList &getToolChainArgs(Compilation &C,
1269 const Command &Job) {
1270 const auto &TC = Job.getCreator().getToolChain();
1271 const auto &SourceAction = Job.getSource();
1272 return C.getArgsForToolChain(TC: &TC, BA: SourceAction.getOffloadingArch(),
1273 DeviceOffloadKind: SourceAction.getOffloadingDeviceKind());
1274}
1275
1276/// Creates a job for the Clang module described by \p MD.
1277static std::unique_ptr<Command>
1278createClangModulePrecompileJob(Compilation &C, const Command &ImportingJob,
1279 const deps::ModuleDeps &MD) {
1280 DerivedArgList &Args = C.getArgs();
1281 const OptTable &Opts = C.getDriver().getOpts();
1282 Arg *InputArg = makeInputArg(Args, Opts, Value: "<discovered clang module>");
1283 Action *IA = C.MakeAction<InputAction>(Arg&: *InputArg, Arg: types::ID::TY_ModuleFile);
1284 Action *PA = C.MakeAction<PrecompileJobAction>(Arg&: IA, Arg: types::ID::TY_ModuleFile);
1285 PA->propagateOffloadInfo(A: &ImportingJob.getSource());
1286
1287 const auto &TCArgs = getToolChainArgs(C, Job: ImportingJob);
1288
1289 const auto &BuildArgs = MD.getBuildArguments();
1290 ArgStringList JobArgs;
1291 JobArgs.reserve(N: BuildArgs.size());
1292 for (const auto &Arg : BuildArgs)
1293 JobArgs.push_back(Elt: TCArgs.MakeArgString(Str: Arg));
1294
1295 const auto &D = C.getDriver();
1296 return std::make_unique<Command>(
1297 args&: *PA, args: ImportingJob.getCreator(), args: ResponseFileSupport::AtFileUTF8(),
1298 args: D.getDriverProgramPath(), args&: JobArgs,
1299 /*Inputs=*/args: ArrayRef<InputInfo>{},
1300 /*Outputs=*/args: ArrayRef<InputInfo>{}, args: D.getPrependArg());
1301}
1302
1303/// Creates a \c ClangModuleJobNode with associated job for each unique Clang
1304/// module in \p ModuleDepGraphsForScannedJobs.
1305///
1306/// \param ImportingJobs Jobs whose module dependencies were scanned.
1307/// \param ModuleDepGraphsForScannedJobs Full Clang module dependency graphs
1308/// corresponding to \p ImportingJobs, in order.
1309static void createClangModuleJobsAndNodes(
1310 CompilationGraph &Graph, Compilation &C,
1311 ArrayRef<std::unique_ptr<Command>> ImportingJobs,
1312 SmallVectorImpl<deps::ModuleDepsGraph> &&ModuleDepGraphsForScannedJobs) {
1313 llvm::DenseSet<deps::ModuleID> AlreadySeen;
1314 for (auto &&[ImportingJob, ModuleDepsGraph] :
1315 llvm::zip_equal(t: llvm::make_pointee_range(Range&: ImportingJobs),
1316 u&: ModuleDepGraphsForScannedJobs)) {
1317 for (auto &MD : ModuleDepsGraph) {
1318 const auto Inserted = AlreadySeen.insert(V: MD.ID).second;
1319 if (!Inserted)
1320 continue;
1321
1322 auto ClangModuleJob = createClangModulePrecompileJob(C, ImportingJob, MD);
1323 Graph.createJobNode<ClangModuleJobNode>(Arg: std::move(ClangModuleJob),
1324 Arg: std::move(MD));
1325 }
1326 }
1327}
1328
1329/// Installs the command lines produced by the dependency scan into
1330/// \p ScannedJobs.
1331static void
1332installScanCommandLines(Compilation &C,
1333 MutableArrayRef<std::unique_ptr<Command>> ScannedJobs,
1334 ArrayRef<InputDependencies> InputDepsForScannedJobs) {
1335 for (auto &&[Job, InputDeps] : llvm::zip_equal(
1336 t: llvm::make_pointee_range(Range&: ScannedJobs), u&: InputDepsForScannedJobs)) {
1337 const auto &BuildArgs = InputDeps.BuildArgs;
1338 ArgStringList JobArgs;
1339 JobArgs.reserve(N: BuildArgs.size());
1340
1341 auto &TCArgs = getToolChainArgs(C, Job);
1342 for (const auto &Arg : BuildArgs)
1343 JobArgs.push_back(Elt: TCArgs.MakeArgString(Str: Arg));
1344
1345 Job.replaceArguments(List: std::move(JobArgs));
1346 }
1347}
1348
1349/// Creates nodes for all jobs which were scanned for dependencies.
1350///
1351/// The updated command lines produced by the dependency scan are installed at a
1352/// later point.
1353static void createNodesForScannedJobs(
1354 CompilationGraph &Graph,
1355 SmallVectorImpl<std::unique_ptr<Command>> &&ScannedJobs,
1356 SmallVectorImpl<InputDependencies> &&InputDepsForScannedJobs) {
1357 for (auto &&[Job, InputDeps] :
1358 llvm::zip_equal(t&: ScannedJobs, u&: InputDepsForScannedJobs)) {
1359 if (InputDeps.ModuleName.empty())
1360 Graph.createJobNode<NonModuleTUJobNode>(Arg: std::move(Job),
1361 Arg: std::move(InputDeps));
1362 else
1363 Graph.createJobNode<NamedModuleJobNode>(Arg: std::move(Job),
1364 Arg: std::move(InputDeps));
1365 }
1366}
1367
1368template <typename LookupT, typename KeyRangeT>
1369static void connectEdgesViaLookup(CompilationGraph &Graph, CGNode &TgtNode,
1370 const LookupT &SrcNodeLookup,
1371 const KeyRangeT &SrcNodeLookupKeys,
1372 CGEdge::EdgeKind Kind) {
1373 for (const auto &Key : SrcNodeLookupKeys) {
1374 const auto It = SrcNodeLookup.find(Key);
1375 if (It == SrcNodeLookup.end())
1376 continue;
1377
1378 auto &SrcNode = *It->second;
1379 Graph.createEdge(Kind, Src&: SrcNode, Dst&: TgtNode);
1380 }
1381}
1382
1383/// Create edges for regular (non-module) dependencies in \p Graph.
1384static void createRegularEdges(CompilationGraph &Graph) {
1385 llvm::DenseMap<StringRef, CGNode *> NodeByOutputFiles;
1386 for (auto *Node : Graph) {
1387 for (const auto &Output : cast<JobNode>(Val: Node)->Job->getOutputFilenames()) {
1388 [[maybe_unused]] const bool Inserted =
1389 NodeByOutputFiles.try_emplace(Key: Output, Args&: Node).second;
1390 assert(Inserted &&
1391 "Driver should not produce multiple jobs with identical outputs!");
1392 }
1393 }
1394
1395 for (auto *Node : Graph) {
1396 const auto &InputInfos = cast<JobNode>(Val: Node)->Job->getInputInfos();
1397 auto InputFilenames = llvm::map_range(
1398 C: InputInfos, F: [](const auto &II) { return II.getFilename(); });
1399
1400 connectEdgesViaLookup(Graph, TgtNode&: *Node, SrcNodeLookup: NodeByOutputFiles, SrcNodeLookupKeys: InputFilenames,
1401 Kind: CGEdge::EdgeKind::Regular);
1402 }
1403}
1404
1405/// Create edges for module dependencies in \p Graph.
1406///
1407/// \returns false if there are multiple definitions for a named module, with
1408/// diagnostics reported to \p Diags; otherwise returns true.
1409static bool createModuleDependencyEdges(CompilationGraph &Graph,
1410 DiagnosticsEngine &Diags) {
1411 llvm::DenseMap<deps::ModuleID, CGNode *> ClangModuleNodeByID;
1412 llvm::DenseMap<ModuleNameAndTriple, CGNode *> NamedModuleNodeByID;
1413
1414 // Map each module to the job that produces it.
1415 bool HasDuplicateModuleError = false;
1416 for (auto *Node : Graph) {
1417 llvm::TypeSwitch<CGNode *>(Node)
1418 .Case(caseFn: [&](ClangModuleJobNode *ClangModuleNode) {
1419 [[maybe_unused]] const bool Inserted =
1420 ClangModuleNodeByID.try_emplace(Key: ClangModuleNode->MD.ID, Args&: Node)
1421 .second;
1422 assert(Inserted &&
1423 "Multiple Clang module nodes with the same module ID!");
1424 })
1425 .Case(caseFn: [&](NamedModuleJobNode *NamedModuleNode) {
1426 StringRef ModuleName = NamedModuleNode->InputDeps.ModuleName;
1427 ModuleNameAndTriple ID{ModuleName, getTriple(Job: *NamedModuleNode->Job)};
1428 const auto [It, Inserted] = NamedModuleNodeByID.try_emplace(Key: ID, Args&: Node);
1429 if (!Inserted) {
1430 // For scan input jobs, their first input is always a filename and
1431 // the scanned source.
1432 // We don't use InputDeps.FileDeps here because diagnostics should
1433 // refer to the filename as specified on the command line, not the
1434 // canonical absolute path.
1435 StringRef PrevFile =
1436 getFirstInputFilename(Job: *cast<JobNode>(Val: It->second)->Job);
1437 StringRef CurFile = getFirstInputFilename(Job: *NamedModuleNode->Job);
1438 Diags.Report(DiagID: diag::err_modules_driver_named_module_redefinition)
1439 << ModuleName << PrevFile << CurFile;
1440 HasDuplicateModuleError = true;
1441 }
1442 });
1443 }
1444 if (HasDuplicateModuleError)
1445 return false;
1446
1447 // Create edges from the module nodes to their importers.
1448 for (auto *Node : Graph) {
1449 llvm::TypeSwitch<CGNode *>(Node)
1450 .Case(caseFn: [&](ClangModuleJobNode *ClangModuleNode) {
1451 connectEdgesViaLookup(Graph, TgtNode&: *ClangModuleNode, SrcNodeLookup: ClangModuleNodeByID,
1452 SrcNodeLookupKeys: ClangModuleNode->MD.ClangModuleDeps,
1453 Kind: CGEdge::EdgeKind::ModuleDependency);
1454 })
1455 .Case(caseFn: [&](ScannedJobNode *NodeWithInputDeps) {
1456 connectEdgesViaLookup(Graph, TgtNode&: *NodeWithInputDeps, SrcNodeLookup: ClangModuleNodeByID,
1457 SrcNodeLookupKeys: NodeWithInputDeps->InputDeps.ClangModuleDeps,
1458 Kind: CGEdge::EdgeKind::ModuleDependency);
1459
1460 StringRef Triple = getTriple(Job: *NodeWithInputDeps->Job);
1461 const auto NamedModuleDepIDs =
1462 llvm::map_range(C&: NodeWithInputDeps->InputDeps.NamedModuleDeps,
1463 F: [&](StringRef ModuleName) {
1464 return ModuleNameAndTriple{ModuleName, Triple};
1465 });
1466 connectEdgesViaLookup(Graph, TgtNode&: *NodeWithInputDeps, SrcNodeLookup: NamedModuleNodeByID,
1467 SrcNodeLookupKeys: NamedModuleDepIDs,
1468 Kind: CGEdge::EdgeKind::ModuleDependency);
1469 });
1470 }
1471
1472 return true;
1473}
1474
1475/// Prunes the compilation graph of any jobs which build Standard library
1476/// modules not required in this compilation.
1477static void
1478pruneUnusedStdlibModuleJobs(CompilationGraph &Graph,
1479 ArrayRef<JobNode *> UnusedStdlibModuleJobNodes) {
1480 // Collect all reachable non-image job nodes.
1481 llvm::SmallPtrSet<JobNode *, 16> PrunableJobNodes;
1482 for (auto *PrunableJobNodeRoot : UnusedStdlibModuleJobNodes) {
1483 auto ReachableJobNodes =
1484 llvm::map_range(C: llvm::depth_first(G: cast<CGNode>(Val: PrunableJobNodeRoot)),
1485 F: llvm::CastTo<JobNode>);
1486 auto ReachableNonImageNodes = llvm::make_filter_range(
1487 Range&: ReachableJobNodes, Pred: [](auto *N) { return !llvm::isa<ImageJobNode>(N); });
1488 PrunableJobNodes.insert_range(R&: ReachableNonImageNodes);
1489 }
1490
1491 // Map image job nodes to the prunable job nodes that feed into them.
1492 llvm::DenseMap<ImageJobNode *, llvm::SmallPtrSet<JobNode *, 4>>
1493 PrunableJobNodesByImageNode;
1494 for (auto *PrunableJobNode : PrunableJobNodes) {
1495 auto ReachableJobNodes = llvm::depth_first(G: cast<CGNode>(Val: PrunableJobNode));
1496 auto ReachableImageJobNodes = llvm::map_range(
1497 C: llvm::make_filter_range(Range&: ReachableJobNodes, Pred: llvm::IsaPred<ImageJobNode>),
1498 F: llvm::CastTo<ImageJobNode>);
1499
1500 for (auto *ImageNode : ReachableImageJobNodes)
1501 PrunableJobNodesByImageNode[ImageNode].insert(Ptr: PrunableJobNode);
1502 }
1503
1504 // Remove from each affected image job node any arguments corresponding to
1505 // outputs of the connected prunable job nodes.
1506 for (auto &[ImageNode, PrunableJobNodeInputs] : PrunableJobNodesByImageNode) {
1507 SmallVector<StringRef, 4> OutputsToRemove;
1508 for (auto *JN : PrunableJobNodeInputs)
1509 llvm::append_range(C&: OutputsToRemove, R: JN->Job->getOutputFilenames());
1510
1511 auto NewArgs = ImageNode->Job->getArguments();
1512 llvm::erase_if(C&: NewArgs, P: [&](StringRef Arg) {
1513 return llvm::is_contained(Range&: OutputsToRemove, Element: Arg);
1514 });
1515 ImageNode->Job->replaceArguments(List: NewArgs);
1516 }
1517
1518 // Erase all prunable job nodes from the graph.
1519 for (auto *JN : PrunableJobNodes) {
1520 // Nodes are owned by the graph, but we can release the associated job.
1521 JN->Job.reset();
1522 Graph.removeNode(N&: *JN);
1523 }
1524}
1525
1526/// Creates the root node and connects it to all nodes with no incoming edges
1527/// ensuring that every node in the graph is reachable from the root.
1528static void createAndConnectRoot(CompilationGraph &Graph) {
1529 llvm::SmallPtrSet<CGNode *, 16> HasIncomingEdge;
1530 for (auto *Node : Graph)
1531 for (auto *Edge : Node->getEdges())
1532 HasIncomingEdge.insert(Ptr: &Edge->getTargetNode());
1533
1534 auto AllNonRootNodes = llvm::iterator_range(Graph);
1535 auto &Root = Graph.createRoot();
1536
1537 for (auto *Node : AllNonRootNodes) {
1538 if (HasIncomingEdge.contains(Ptr: Node))
1539 continue;
1540 Graph.createEdge(Kind: CGEdge::EdgeKind::Rooted, Src&: Root, Dst&: *Node);
1541 }
1542}
1543
1544/// Creates a temporary output path for \p ModuleName.
1545static std::string createModuleOutputPath(const Compilation &C,
1546 StringRef ModuleName) {
1547 // Sanitize the ':' included in parition names. It is illegal for filenames on
1548 // Windows.
1549 SmallString<32> SanitizedModuleName(ModuleName);
1550 llvm::replace(Range&: SanitizedModuleName, OldValue: ':', NewValue: '-');
1551 auto ModuleOutputPath = C.getDriver().GetTemporaryPath(
1552 Prefix: SanitizedModuleName, Suffix: types::getTypeTempSuffix(Id: types::TY_ModuleFile));
1553 return ModuleOutputPath;
1554}
1555
1556/// Adds the '-fmodule-output=' argument for the module produced by \p Node.
1557static void configureNamedModuleOutputArg(Compilation &C,
1558 NamedModuleJobNode &Node,
1559 StringRef ModuleOutputPath) {
1560 auto &Job = *Node.Job;
1561 const auto &TCArgs = getToolChainArgs(C, Job);
1562 auto JobArgs = Job.getArguments();
1563 JobArgs.push_back(
1564 Elt: TCArgs.MakeArgString(Str: "-fmodule-output=" + ModuleOutputPath));
1565 Job.replaceArguments(List: std::move(JobArgs));
1566}
1567
1568/// Propagates the '-fmodule-file=' mapping for the named module described by
1569/// \p Node to each dependent job.
1570static void propagateModuleFileMappingArg(Compilation &C,
1571 NamedModuleJobNode &Node,
1572 StringRef ModuleOutputPath) {
1573 const StringRef ModuleName = Node.InputDeps.ModuleName;
1574
1575 auto DependentNodes = llvm::drop_begin(RangeOrContainer: llvm::depth_first<CGNode *>(G: &Node));
1576 auto DependentScannedNodes = llvm::map_range(
1577 C: llvm::make_filter_range(Range&: DependentNodes, Pred: llvm::IsaPred<ScannedJobNode>),
1578 F: llvm::CastTo<ScannedJobNode>);
1579
1580 for (ScannedJobNode *DependentNode : DependentScannedNodes) {
1581 auto &DependentJob = *DependentNode->Job;
1582 const auto &TCArgs = getToolChainArgs(C, Job: DependentJob);
1583 auto JobArgs = DependentJob.getArguments();
1584 JobArgs.push_back(Elt: TCArgs.MakeArgString(Str: "-fmodule-file=" + ModuleName + "=" +
1585 ModuleOutputPath));
1586 DependentJob.replaceArguments(List: std::move(JobArgs));
1587 }
1588}
1589
1590/// Finalizes command lines for C++20 named module dependencies.
1591///
1592/// The command lines produced by dependency scanning are only adjusted to
1593/// handle discovered Clang modules. For C++20 named modules, we update the
1594/// command-lines here.
1595static void fixupNamedModuleCommandLines(Compilation &C,
1596 CompilationGraph &Graph) {
1597 const auto NamedModuleNodes = llvm::map_range(
1598 C: llvm::make_filter_range(Range&: Graph, Pred: llvm::IsaPred<NamedModuleJobNode>),
1599 F: llvm::CastTo<NamedModuleJobNode>);
1600
1601 for (NamedModuleJobNode *Node : NamedModuleNodes) {
1602 const auto &Job = *Node->Job;
1603
1604 // For Standard library modules, the driver already creates the module
1605 // output as a temp file, so we can use that path directly.
1606 const bool IsStdModule =
1607 Job.getInputInfos().front().getType() == types::TY_CXXStdModule;
1608 if (IsStdModule) {
1609 StringRef ModuleOutputPath = Job.getOutputFilenames().front();
1610 propagateModuleFileMappingArg(C, Node&: *Node, ModuleOutputPath);
1611 continue;
1612 }
1613
1614 const StringRef ModuleName = Node->InputDeps.ModuleName;
1615 const auto ModuleOutputPath = createModuleOutputPath(C, ModuleName);
1616 C.addTempFile(Name: C.getArgs().MakeArgString(Str: ModuleOutputPath));
1617
1618 configureNamedModuleOutputArg(C, Node&: *Node, ModuleOutputPath);
1619 propagateModuleFileMappingArg(C, Node&: *Node, ModuleOutputPath);
1620 }
1621}
1622
1623/// Moves jobs from \p Graph into \p C in the graph's topological order.
1624static void feedJobsBackIntoCompilation(Compilation &C,
1625 CompilationGraph &&Graph) {
1626 llvm::ReversePostOrderTraversal<CompilationGraph *> TopologicallySortedNodes(
1627 &Graph);
1628 assert(isa<RootNode>(*TopologicallySortedNodes.begin()) &&
1629 "First node in topological order must be the root!");
1630 auto TopologicallySortedJobNodes = llvm::map_range(
1631 C: llvm::drop_begin(RangeOrContainer&: TopologicallySortedNodes), F: llvm::CastTo<JobNode>);
1632 for (auto *JN : TopologicallySortedJobNodes)
1633 C.addCommand(Cmd: std::move(JN->Job));
1634}
1635
1636void driver::modules::runModulesDriver(
1637 Compilation &C, ArrayRef<StdModuleManifest::Module> ManifestEntries) {
1638 llvm::PrettyStackTraceString CrashInfo("Running modules driver.");
1639
1640 auto Jobs = C.getJobs().takeJobs();
1641
1642 const auto ManifestEntryBySource = buildManifestLookupMap(ManifestEntries);
1643 // Apply manifest-entry specific command-line modifications before the scan as
1644 // they might affect it.
1645 applyArgsForStdModuleManifestInputs(C, ManifestEntryBySource, Jobs);
1646
1647 DiagnosticsEngine &Diags = C.getDriver().getDiags();
1648
1649 // Run the dependency scan.
1650 const auto MaybeModuleCachePath = getModuleCachePath(Args&: C.getArgs());
1651 if (!MaybeModuleCachePath) {
1652 Diags.Report(DiagID: diag::err_default_modules_cache_not_available);
1653 return;
1654 }
1655
1656 auto MaybeCWD = C.getDriver().getVFS().getCurrentWorkingDirectory();
1657 const auto CWD = MaybeCWD ? std::move(*MaybeCWD) : ".";
1658
1659 const llvm::opt::Arg *LogPathArg =
1660 C.getArgs().getLastArg(Ids: options::OPT_fdepscan_log_path);
1661 StringRef DepScanLogPath =
1662 LogPathArg ? StringRef(LogPathArg->getValue()).trim() : StringRef();
1663 if (LogPathArg && DepScanLogPath.empty()) {
1664 Diags.Report(DiagID: diag::err_drv_depscan_log_path_empty);
1665 return;
1666 }
1667
1668 auto MaybeScanResults =
1669 scanDependencies(Jobs, ManifestLookup: ManifestEntryBySource, ModuleCachePath: *MaybeModuleCachePath, WorkingDirectory: CWD,
1670 DepScanLogPath, Diags);
1671 if (!MaybeScanResults) {
1672 Diags.Report(DiagID: diag::err_dependency_scan_failed);
1673 return;
1674 }
1675 auto &ScanResult = *MaybeScanResults;
1676
1677 // Build the compilation graph.
1678 CompilationGraph Graph;
1679 createNodesForNonScannableJobs(
1680 Graph, NonScannableJobs: takeJobsAtIndices(Jobs, Indices: ScanResult.NonScannableJobIndices));
1681 auto UnusedStdlibModuleJobNodes = createNodesForUnusedStdlibModuleJobs(
1682 Graph, UnusedStdlibModuleJobs: takeJobsAtIndices(Jobs, Indices: ScanResult.UnusedStdlibModuleJobIndices));
1683
1684 auto ScannedJobs = takeJobsAtIndices(Jobs, Indices: ScanResult.ScannedJobIndices);
1685 if (!validateScannedJobInputKinds(ScannedJobs,
1686 InputDepsForScannedJobs: ScanResult.InputDepsForScannedJobs, Diags))
1687 return;
1688 installScanCommandLines(C, ScannedJobs, InputDepsForScannedJobs: ScanResult.InputDepsForScannedJobs);
1689
1690 createClangModuleJobsAndNodes(
1691 Graph, C, /*ImportingJobs*/ ScannedJobs,
1692 ModuleDepGraphsForScannedJobs: std::move(ScanResult.ModuleDepGraphsForScannedJobs));
1693 createNodesForScannedJobs(Graph, ScannedJobs: std::move(ScannedJobs),
1694 InputDepsForScannedJobs: std::move(ScanResult.InputDepsForScannedJobs));
1695
1696 createRegularEdges(Graph);
1697 pruneUnusedStdlibModuleJobs(Graph, UnusedStdlibModuleJobNodes);
1698 if (!createModuleDependencyEdges(Graph, Diags))
1699 return;
1700 createAndConnectRoot(Graph);
1701
1702 Diags.Report(DiagID: diag::remark_printing_module_graph);
1703 if (!Diags.isLastDiagnosticIgnored())
1704 llvm::WriteGraph<const CompilationGraph *>(O&: llvm::errs(), G: &Graph);
1705
1706 fixupNamedModuleCommandLines(C, Graph);
1707 feedJobsBackIntoCompilation(C, Graph: std::move(Graph));
1708}
1709