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 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 deps::DependencyScanningService ScanningService(std::move(Opts));
649
650 std::unique_ptr<llvm::ThreadPoolInterface> ThreadPool;
651 std::unique_ptr<ScanningWorkerPool> WorkerPool;
652 std::tie(args&: ThreadPool, args&: WorkerPool) = createOptimalThreadAndWorkerPool(
653 NumScanInputs, HasStdlibModuleInputs, ScanningService);
654
655 StdlibModuleScanScheduler StdlibModuleRegistry(StdlibModuleScanIndexByID);
656 ModuleLookupController LookupController(ModuleCachePath);
657
658 // Scan results are indexed by ScanIndex into ScannableJobIndices, not by
659 // JobIndex into Jobs. This allows one result slot per scannable job.
660 SmallVector<std::optional<deps::TranslationUnitDeps>, 0> AllScanResults(
661 NumScanInputs);
662 SmallVector<SmallVector<StandaloneDiagnostic, 0>, 0> AllScanDiags(
663 NumScanInputs);
664 std::atomic<bool> HasError{false};
665
666 // Scans the job at the given scan index and schedules scans for any newly
667 // discovered Standard library module dependencies.
668 std::function<void(size_t)> ScanOneAndScheduleNew;
669 ScanOneAndScheduleNew = [&](size_t ScanIndex) {
670 const size_t JobIndex = ScannableJobIndices[ScanIndex];
671 const Command &Job = *Jobs[JobIndex];
672 auto [MaybeTUDeps, ScanDiags] = scanDependenciesForJob(
673 Job, WorkerPool&: *WorkerPool, WorkingDirectory, LookupController);
674
675 // Store diagnostics even for successful scans to also capture any warnings
676 // or notes.
677 assert(AllScanDiags[ScanIndex].empty() &&
678 "Each slot should be written to at most once.");
679 AllScanDiags[ScanIndex] = std::move(ScanDiags);
680
681 if (!MaybeTUDeps) {
682 HasError.store(i: true, m: std::memory_order_relaxed);
683 return;
684 }
685
686 // Schedule scans for newly discovered Standard library module dependencies.
687 const auto NewScanInputs = StdlibModuleRegistry.getNewScanInputs(
688 NamedModuleDeps: MaybeTUDeps->NamedModuleDeps, Triple: getTriple(Job));
689 for (const size_t NewScanIndex : NewScanInputs)
690 ThreadPool->async(
691 F: [&, NewScanIndex]() { ScanOneAndScheduleNew(NewScanIndex); });
692
693 assert(!AllScanResults[ScanIndex].has_value() &&
694 "Each slot should be written to at most once.");
695 AllScanResults[ScanIndex] = std::move(MaybeTUDeps);
696 };
697
698 // Initiate the scan with all jobs for user-provided inputs.
699 for (const size_t ScanIndex : UserInputScanIndices)
700 ThreadPool->async(F: [&ScanOneAndScheduleNew, ScanIndex]() {
701 ScanOneAndScheduleNew(ScanIndex);
702 });
703 ThreadPool->wait();
704
705 reportAllScanDiagnostics(AllScanDiags: std::move(AllScanDiags), Diags);
706 if (HasError.load(m: std::memory_order_relaxed))
707 return std::nullopt;
708
709 // Collect results, mapping scan indices back to job indices.
710 DependencyScanResult Result;
711 for (auto &&[JobIndex, MaybeTUDeps] :
712 llvm::zip_equal(t&: ScannableJobIndices, u&: AllScanResults)) {
713 if (MaybeTUDeps) {
714 Result.ScannedJobIndices.push_back(Elt: JobIndex);
715 Result.ModuleDepGraphsForScannedJobs.push_back(
716 Elt: std::move(MaybeTUDeps->ModuleGraph));
717 Result.InputDepsForScannedJobs.push_back(
718 Elt: makeInputDeps(TUDeps: std::move(*MaybeTUDeps)));
719 } else
720 Result.UnusedStdlibModuleJobIndices.push_back(Elt: JobIndex);
721 }
722 Result.NonScannableJobIndices = std::move(NonScannableJobIndices);
723
724#ifndef NDEBUG
725 llvm::SmallDenseSet<size_t> SeenJobIndices;
726 SeenJobIndices.insert_range(Result.ScannedJobIndices);
727 SeenJobIndices.insert_range(Result.UnusedStdlibModuleJobIndices);
728 SeenJobIndices.insert_range(Result.NonScannableJobIndices);
729 assert(llvm::all_of(llvm::index_range(0, Jobs.size()),
730 [&](size_t JobIndex) {
731 return SeenJobIndices.contains(JobIndex);
732 }) &&
733 "Scan result must partition all jobs");
734#endif
735
736 return Result;
737}
738
739namespace {
740class CGNode;
741class CGEdge;
742using CGNodeBase = llvm::DGNode<CGNode, CGEdge>;
743using CGEdgeBase = llvm::DGEdge<CGNode, CGEdge>;
744using CGBase = llvm::DirectedGraph<CGNode, CGEdge>;
745
746/// Compilation Graph Node
747class CGNode : public CGNodeBase {
748public:
749 enum class NodeKind {
750 ClangModuleCC1Job,
751 NamedModuleCC1Job,
752 NonModuleCC1Job,
753 MiscJob,
754 ImageJob,
755 Root,
756 };
757
758 CGNode(const NodeKind K) : Kind(K) {}
759 CGNode(const CGNode &) = delete;
760 CGNode(CGNode &&) = delete;
761 CGNode &operator=(const CGNode &) = delete;
762 CGNode &operator=(CGNode &&) = delete;
763 virtual ~CGNode() = 0;
764
765 NodeKind getKind() const { return Kind; }
766
767private:
768 NodeKind Kind;
769};
770CGNode::~CGNode() = default;
771
772/// Subclass of CGNode representing the root node of the graph.
773///
774/// The root node is a special node that connects to all other nodes with
775/// no incoming edges, so that there is always a path from it to any node
776/// in the graph.
777///
778/// There should only be one such node in a given graph.
779class RootNode : public CGNode {
780public:
781 RootNode() : CGNode(NodeKind::Root) {}
782 ~RootNode() override = default;
783
784 static bool classof(const CGNode *N) {
785 return N->getKind() == NodeKind::Root;
786 }
787};
788
789/// Base class for any CGNode type that represents a job.
790class JobNode : public CGNode {
791public:
792 JobNode(std::unique_ptr<Command> &&Job, NodeKind Kind)
793 : CGNode(Kind), Job(std::move(Job)) {
794 assert(this->Job && "Expected valid job!");
795 }
796 virtual ~JobNode() override = 0;
797
798 std::unique_ptr<Command> Job;
799
800 static bool classof(const CGNode *N) {
801 return N->getKind() != NodeKind::Root;
802 }
803};
804JobNode::~JobNode() = default;
805
806/// Subclass of CGNode representing a -cc1 job which produces a Clang module.
807class ClangModuleJobNode : public JobNode {
808public:
809 ClangModuleJobNode(std::unique_ptr<Command> &&Job, deps::ModuleDeps &&MD)
810 : JobNode(std::move(Job), NodeKind::ClangModuleCC1Job),
811 MD(std::move(MD)) {}
812 ~ClangModuleJobNode() override = default;
813
814 deps::ModuleDeps MD;
815
816 static bool classof(const CGNode *N) {
817 return N->getKind() == NodeKind::ClangModuleCC1Job;
818 }
819};
820
821/// Base class for any CGNode type that represents any scanned -cc1 job.
822class ScannedJobNode : public JobNode {
823public:
824 ScannedJobNode(std::unique_ptr<Command> &&Job, InputDependencies &&InputDeps,
825 NodeKind Kind)
826 : JobNode(std::move(Job), Kind), InputDeps(std::move(InputDeps)) {}
827 ~ScannedJobNode() override = default;
828
829 InputDependencies InputDeps;
830
831 static bool classof(const CGNode *N) {
832 return N->getKind() == NodeKind::NamedModuleCC1Job ||
833 N->getKind() == NodeKind::NonModuleCC1Job;
834 }
835};
836
837/// Subclass of CGNode representing a -cc1 job which produces a C++20 named
838/// module.
839class NamedModuleJobNode : public ScannedJobNode {
840public:
841 NamedModuleJobNode(std::unique_ptr<Command> &&Job,
842 InputDependencies &&InputDeps)
843 : ScannedJobNode(std::move(Job), std::move(InputDeps),
844 NodeKind::NamedModuleCC1Job) {}
845 ~NamedModuleJobNode() override = default;
846
847 static bool classof(const CGNode *N) {
848 return N->getKind() == NodeKind::NamedModuleCC1Job;
849 }
850};
851
852/// Subclass of CGNode representing a -cc1 job which does not produce any
853/// module, but might still have module imports.
854class NonModuleTUJobNode : public ScannedJobNode {
855public:
856 NonModuleTUJobNode(std::unique_ptr<Command> &&Job,
857 InputDependencies &&InputDeps)
858 : ScannedJobNode(std::move(Job), std::move(InputDeps),
859 NodeKind::NonModuleCC1Job) {}
860 ~NonModuleTUJobNode() override = default;
861
862 static bool classof(const CGNode *N) {
863 return N->getKind() == NodeKind::NonModuleCC1Job;
864 }
865};
866
867/// Subclass of CGNode representing a job which produces an image file, such as
868/// a linker or interface stub merge job.
869class ImageJobNode : public JobNode {
870public:
871 ImageJobNode(std::unique_ptr<Command> &&Job)
872 : JobNode(std::move(Job), NodeKind::ImageJob) {}
873 ~ImageJobNode() override = default;
874
875 static bool classof(const CGNode *N) {
876 return N->getKind() == NodeKind::ImageJob;
877 }
878};
879
880/// Subclass of CGNode representing any job not covered by the other node types.
881///
882/// Jobs represented by this node type are not modified by the modules driver.
883class MiscJobNode : public JobNode {
884public:
885 MiscJobNode(std::unique_ptr<Command> &&Job)
886 : JobNode(std::move(Job), NodeKind::MiscJob) {}
887 ~MiscJobNode() override = default;
888
889 static bool classof(const CGNode *N) {
890 return N->getKind() == NodeKind::MiscJob;
891 }
892};
893
894/// Compilation Graph Edge
895///
896/// Edges connect the producer of an output to its consumer, except for edges
897/// stemming from the root node.
898class CGEdge : public CGEdgeBase {
899public:
900 enum class EdgeKind {
901 Regular,
902 ModuleDependency,
903 Rooted,
904 };
905
906 CGEdge(CGNode &N, EdgeKind K) : CGEdgeBase(N), Kind(K) {}
907 CGEdge(const CGEdge &) = delete;
908 CGEdge &operator=(const CGEdge &) = delete;
909 CGEdge(CGEdge &&) = delete;
910 CGEdge &operator=(CGEdge &&) = delete;
911
912 EdgeKind getKind() const { return Kind; }
913
914private:
915 EdgeKind Kind;
916};
917
918/// Compilation Graph
919///
920/// The graph owns all of its components.
921/// All nodes and edges created by the graph have the same livetime as the
922/// graph, even if removed from the graph's node list.
923class CompilationGraph : public CGBase {
924public:
925 CompilationGraph() = default;
926 CompilationGraph(const CompilationGraph &) = delete;
927 CompilationGraph &operator=(const CompilationGraph &) = delete;
928 CompilationGraph(CompilationGraph &&G) = default;
929 CompilationGraph &operator=(CompilationGraph &&) = default;
930 ~CompilationGraph() = default;
931
932 CGNode &getRoot() const {
933 assert(Root && "Root node has not yet been created!");
934 return *Root;
935 }
936
937 RootNode &createRoot() {
938 assert(!Root && "Root node has already been created!");
939 auto &RootRef = createNodeImpl<RootNode>();
940 Root = &RootRef;
941 return RootRef;
942 }
943
944 template <typename T, typename... Args> T &createJobNode(Args &&...Arg) {
945 static_assert(std::is_base_of<JobNode, T>::value,
946 "T must be derived from JobNode");
947 return createNodeImpl<T>(std::forward<Args>(Arg)...);
948 }
949
950 CGEdge &createEdge(CGEdge::EdgeKind Kind, CGNode &Src, CGNode &Dst) {
951 auto Edge = std::make_unique<CGEdge>(args&: Dst, args&: Kind);
952 CGEdge &EdgeRef = *Edge;
953 AllEdges.push_back(Elt: std::move(Edge));
954 connect(Src, Dst, E&: EdgeRef);
955 return EdgeRef;
956 }
957
958private:
959 using CGBase::addNode;
960 using CGBase::connect;
961
962 template <typename T, typename... Args> T &createNodeImpl(Args &&...Arg) {
963 auto Node = std::make_unique<T>(std::forward<Args>(Arg)...);
964 T &NodeRef = *Node;
965 AllNodes.push_back(std::move(Node));
966 addNode(N&: NodeRef);
967 return NodeRef;
968 }
969
970 CGNode *Root = nullptr;
971 SmallVector<std::unique_ptr<CGNode>> AllNodes;
972 SmallVector<std::unique_ptr<CGEdge>> AllEdges;
973};
974} // anonymous namespace
975
976static StringRef getFirstInputFilename(const Command &Job) {
977 return Job.getInputInfos().front().getFilename();
978}
979
980namespace llvm {
981/// Non-const versions of the GraphTraits specializations for CompilationGraph.
982template <> struct GraphTraits<CGNode *> {
983 using NodeRef = CGNode *;
984
985 static NodeRef CGGetTargetNode(CGEdge *E) { return &E->getTargetNode(); }
986
987 using ChildIteratorType =
988 mapped_iterator<CGNode::iterator, decltype(&CGGetTargetNode)>;
989 using ChildEdgeIteratorType = CGNode::iterator;
990
991 static NodeRef getEntryNode(NodeRef N) { return N; }
992
993 static ChildIteratorType child_begin(NodeRef N) {
994 return ChildIteratorType(N->begin(), &CGGetTargetNode);
995 }
996
997 static ChildIteratorType child_end(NodeRef N) {
998 return ChildIteratorType(N->end(), &CGGetTargetNode);
999 }
1000
1001 static ChildEdgeIteratorType child_edge_begin(NodeRef N) {
1002 return N->begin();
1003 }
1004 static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); }
1005};
1006
1007template <> struct GraphTraits<CompilationGraph *> : GraphTraits<CGNode *> {
1008 using GraphRef = CompilationGraph *;
1009 using NodeRef = CGNode *;
1010
1011 using nodes_iterator = CompilationGraph::iterator;
1012
1013 static NodeRef getEntryNode(GraphRef G) { return &G->getRoot(); }
1014
1015 static nodes_iterator nodes_begin(GraphRef G) { return G->begin(); }
1016
1017 static nodes_iterator nodes_end(GraphRef G) { return G->end(); }
1018};
1019
1020/// Const versions of the GraphTraits specializations for CompilationGraph.
1021template <> struct GraphTraits<const CGNode *> {
1022 using NodeRef = const CGNode *;
1023
1024 static NodeRef CGGetTargetNode(const CGEdge *E) {
1025 return &E->getTargetNode();
1026 }
1027
1028 using ChildIteratorType =
1029 mapped_iterator<CGNode::const_iterator, decltype(&CGGetTargetNode)>;
1030 using ChildEdgeIteratorType = CGNode::const_iterator;
1031
1032 static NodeRef getEntryNode(NodeRef N) { return N; }
1033
1034 static ChildIteratorType child_begin(NodeRef N) {
1035 return ChildIteratorType(N->begin(), &CGGetTargetNode);
1036 }
1037
1038 static ChildIteratorType child_end(NodeRef N) {
1039 return ChildIteratorType(N->end(), &CGGetTargetNode);
1040 }
1041
1042 static ChildEdgeIteratorType child_edge_begin(NodeRef N) {
1043 return N->begin();
1044 }
1045
1046 static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); }
1047};
1048
1049template <>
1050struct GraphTraits<const CompilationGraph *> : GraphTraits<const CGNode *> {
1051 using GraphRef = const CompilationGraph *;
1052 using NodeRef = const CGNode *;
1053
1054 using nodes_iterator = CompilationGraph::const_iterator;
1055
1056 static NodeRef getEntryNode(GraphRef G) { return &G->getRoot(); }
1057
1058 static nodes_iterator nodes_begin(GraphRef G) { return G->begin(); }
1059
1060 static nodes_iterator nodes_end(GraphRef G) { return G->end(); }
1061};
1062
1063template <>
1064struct DOTGraphTraits<const CompilationGraph *> : DefaultDOTGraphTraits {
1065 explicit DOTGraphTraits(bool IsSimple = false)
1066 : DefaultDOTGraphTraits(IsSimple) {}
1067 using GraphRef = const CompilationGraph *;
1068 using NodeRef = const CGNode *;
1069
1070 static std::string getGraphName(GraphRef) {
1071 return "Module Dependency Graph";
1072 }
1073
1074 static std::string getGraphProperties(GraphRef) {
1075 return "\tnode [shape=Mrecord, colorscheme=set23, style=filled];\n";
1076 }
1077
1078 static bool renderGraphFromBottomUp() { return true; }
1079
1080 static bool isNodeHidden(NodeRef N, GraphRef) {
1081 // Only show nodes with module dependency relations.
1082 return !isa<ClangModuleJobNode, ScannedJobNode>(Val: N);
1083 }
1084
1085 static std::string getNodeIdentifier(NodeRef N, GraphRef) {
1086 return llvm::TypeSwitch<NodeRef, std::string>(N)
1087 .Case(caseFn: [](const ClangModuleJobNode *ClangModuleNode) {
1088 const auto &ID = ClangModuleNode->MD.ID;
1089 return llvm::formatv(Fmt: "{0}-{1}", Vals: ID.ModuleName, Vals: ID.ContextHash).str();
1090 })
1091 .Case(caseFn: [](const NamedModuleJobNode *NamedModuleNode) {
1092 return llvm::formatv(Fmt: "{0}-{1}", Vals: NamedModuleNode->InputDeps.ModuleName,
1093 Vals: getTriple(Job: *NamedModuleNode->Job))
1094 .str();
1095 })
1096 .Case(caseFn: [](const NonModuleTUJobNode *NonModuleTUNode) {
1097 const auto &Job = *NonModuleTUNode->Job;
1098 return llvm::formatv(Fmt: "{0}-{1}", Vals: getFirstInputFilename(Job),
1099 Vals: getTriple(Job))
1100 .str();
1101 })
1102 .DefaultUnreachable(message: "Unexpected node kind! Is this node hidden?");
1103 }
1104
1105 static std::string getNodeLabel(NodeRef N, GraphRef) {
1106 return llvm::TypeSwitch<NodeRef, std::string>(N)
1107 .Case(caseFn: [](const ClangModuleJobNode *ClangModuleNode) {
1108 const auto &ID = ClangModuleNode->MD.ID;
1109 return llvm::formatv(Fmt: "Module type: Clang module \\| Module name: {0} "
1110 "\\| Hash: {1}",
1111 Vals: ID.ModuleName, Vals: ID.ContextHash)
1112 .str();
1113 })
1114 .Case(caseFn: [](const NamedModuleJobNode *NamedModuleNode) {
1115 const auto &Job = *NamedModuleNode->Job;
1116 return llvm::formatv(
1117 Fmt: "Filename: {0} \\| Module type: Named module \\| "
1118 "Module name: {1} \\| Triple: {2}",
1119 Vals: getFirstInputFilename(Job),
1120 Vals: NamedModuleNode->InputDeps.ModuleName, Vals: getTriple(Job))
1121 .str();
1122 })
1123 .Case(caseFn: [](const NonModuleTUJobNode *NonModuleTUNode) {
1124 const auto &Job = *NonModuleTUNode->Job;
1125 return llvm::formatv(Fmt: "Filename: {0} \\| Triple: {1}",
1126 Vals: getFirstInputFilename(Job), Vals: getTriple(Job))
1127 .str();
1128 })
1129 .DefaultUnreachable(message: "Unexpected node kind! Is this node hidden?");
1130 }
1131
1132 static std::string getNodeAttributes(NodeRef N, GraphRef) {
1133 switch (N->getKind()) {
1134 case CGNode::NodeKind::ClangModuleCC1Job:
1135 return "fillcolor=1";
1136 case CGNode::NodeKind::NamedModuleCC1Job:
1137 return "fillcolor=2";
1138 case CGNode::NodeKind::NonModuleCC1Job:
1139 return "fillcolor=3";
1140 default:
1141 llvm_unreachable("Unexpected node kind! Is this node hidden?");
1142 }
1143 }
1144};
1145
1146/// GraphWriter specialization for CompilationGraph that emits a more
1147/// human-readable DOT graph.
1148template <>
1149class GraphWriter<const CompilationGraph *>
1150 : public GraphWriterBase<const CompilationGraph *,
1151 GraphWriter<const CompilationGraph *>> {
1152public:
1153 using GraphType = const CompilationGraph *;
1154 using Base = GraphWriterBase<GraphType, GraphWriter<GraphType>>;
1155
1156 GraphWriter(llvm::raw_ostream &O, const GraphType &G, bool IsSimple)
1157 : Base(O, G, IsSimple), EscapedIDByNodeRef(G->size()) {}
1158
1159 void writeNodes() {
1160 auto IsNodeVisible = [&](NodeRef N) { return !DTraits.isNodeHidden(N, G); };
1161 auto VisibleNodes = llvm::filter_to_vector(C: nodes(G), Pred&: IsNodeVisible);
1162
1163 writeNodeDefinitions(VisibleNodes);
1164 O << "\n";
1165 writeNodeRelations(VisibleNodes);
1166 }
1167
1168private:
1169 using Base::DOTTraits;
1170 using Base::GTraits;
1171 using Base::NodeRef;
1172
1173 void writeNodeDefinitions(ArrayRef<NodeRef> VisibleNodes) {
1174 for (NodeRef Node : VisibleNodes) {
1175 std::string EscapedNodeID =
1176 DOT::EscapeString(Label: DTraits.getNodeIdentifier(N: Node, G));
1177 const std::string NodeLabel = DTraits.getNodeLabel(N: Node, G);
1178 const std::string NodeAttrs = DTraits.getNodeAttributes(N: Node, G);
1179 O << '\t' << '"' << EscapedNodeID << "\" [" << NodeAttrs << ", label=\"{ "
1180 << DOT::EscapeString(Label: NodeLabel) << " }\"];\n";
1181 EscapedIDByNodeRef.try_emplace(Key: Node, Args: std::move(EscapedNodeID));
1182 }
1183 }
1184
1185 void writeNodeRelations(ArrayRef<NodeRef> VisibleNodes) {
1186 auto IsNodeVisible = [&](NodeRef N) { return !DTraits.isNodeHidden(N, G); };
1187 for (NodeRef Node : VisibleNodes) {
1188 auto DstNodes = llvm::make_range(x: GTraits::child_begin(N: Node),
1189 y: GTraits::child_end(N: Node));
1190 auto VisibleDstNodes = llvm::make_filter_range(Range&: DstNodes, Pred: IsNodeVisible);
1191 StringRef EscapedSrcNodeID = EscapedIDByNodeRef.at(Val: Node);
1192 for (NodeRef DstNode : VisibleDstNodes) {
1193 StringRef EscapedTgtNodeID = EscapedIDByNodeRef.at(Val: DstNode);
1194 O << '\t' << '"' << EscapedSrcNodeID << "\" -> \"" << EscapedTgtNodeID
1195 << "\";\n";
1196 }
1197 }
1198 }
1199
1200 DenseMap<NodeRef, std::string> EscapedIDByNodeRef;
1201};
1202} // namespace llvm
1203
1204/// Validates that each module-defining source is of type \c TY_CXXModule.
1205///
1206/// \returns false on error, with diagnostics emitted via \p Diags.
1207static bool validateScannedJobInputKinds(
1208 ArrayRef<std::unique_ptr<Command>> ScannedJobs,
1209 ArrayRef<InputDependencies> InputDepsForScannedJobs,
1210 DiagnosticsEngine &Diags) {
1211 for (const auto &&[Job, InputDeps] : llvm::zip_equal(
1212 t: llvm::make_pointee_range(Range&: ScannedJobs), u&: InputDepsForScannedJobs)) {
1213 const auto &MainInput = Job.getInputInfos().front();
1214 const bool DefinesNamedModule = !InputDeps.ModuleName.empty();
1215
1216 if (DefinesNamedModule && MainInput.getType() != types::TY_CXXModule &&
1217 MainInput.getType() != types::TY_CXXStdModule) {
1218 Diags.Report(DiagID: diag::err_module_defined_outside_of_module_source)
1219 << InputDeps.ModuleName << MainInput.getFilename();
1220 return false;
1221 }
1222 }
1223 return true;
1224}
1225
1226static SmallVector<std::unique_ptr<Command>>
1227takeJobsAtIndices(SmallVectorImpl<std::unique_ptr<Command>> &Jobs,
1228 ArrayRef<size_t> Indices) {
1229 SmallVector<std::unique_ptr<Command>> Out;
1230 for (const auto JobIndex : Indices) {
1231 assert(Jobs[JobIndex] && "Expected valid job!");
1232 Out.push_back(Elt: std::move(Jobs[JobIndex]));
1233 }
1234 return Out;
1235}
1236
1237/// Creates nodes for all jobs that could not be scanned (e.g. image jobs, ...).
1238static void createNodesForNonScannableJobs(
1239 CompilationGraph &Graph,
1240 SmallVectorImpl<std::unique_ptr<Command>> &&NonScannableJobs) {
1241 for (auto &Job : NonScannableJobs) {
1242 if (Job->getCreator().isLinkJob())
1243 Graph.createJobNode<ImageJobNode>(Arg: std::move(Job));
1244 else
1245 Graph.createJobNode<MiscJobNode>(Arg: std::move(Job));
1246 }
1247}
1248
1249/// Creates nodes for the Standard library module jobs not discovered as
1250/// dependencies.
1251///
1252/// These and any dependent (non-image) job nodes should be pruned from the
1253/// graph later.
1254static SmallVector<JobNode *> createNodesForUnusedStdlibModuleJobs(
1255 CompilationGraph &Graph,
1256 SmallVectorImpl<std::unique_ptr<Command>> &&UnusedStdlibModuleJobs) {
1257 SmallVector<JobNode *> StdlibModuleNodesToPrune;
1258 for (auto &Job : UnusedStdlibModuleJobs) {
1259 auto &NewNode = Graph.createJobNode<MiscJobNode>(Arg: std::move(Job));
1260 StdlibModuleNodesToPrune.push_back(Elt: &NewNode);
1261 }
1262 return StdlibModuleNodesToPrune;
1263}
1264
1265// Returns the derived argument list for the tool chain responsible
1266// for creating \p Job.
1267static const DerivedArgList &getToolChainArgs(Compilation &C,
1268 const Command &Job) {
1269 const auto &TC = Job.getCreator().getToolChain();
1270 const auto &SourceAction = Job.getSource();
1271 return C.getArgsForToolChain(TC: &TC, BA: SourceAction.getOffloadingArch(),
1272 DeviceOffloadKind: SourceAction.getOffloadingDeviceKind());
1273}
1274
1275/// Creates a job for the Clang module described by \p MD.
1276static std::unique_ptr<Command>
1277createClangModulePrecompileJob(Compilation &C, const Command &ImportingJob,
1278 const deps::ModuleDeps &MD) {
1279 DerivedArgList &Args = C.getArgs();
1280 const OptTable &Opts = C.getDriver().getOpts();
1281 Arg *InputArg = makeInputArg(Args, Opts, Value: "<discovered clang module>");
1282 Action *IA = C.MakeAction<InputAction>(Arg&: *InputArg, Arg: types::ID::TY_ModuleFile);
1283 Action *PA = C.MakeAction<PrecompileJobAction>(Arg&: IA, Arg: types::ID::TY_ModuleFile);
1284 PA->propagateOffloadInfo(A: &ImportingJob.getSource());
1285
1286 const auto &TCArgs = getToolChainArgs(C, Job: ImportingJob);
1287
1288 const auto &BuildArgs = MD.getBuildArguments();
1289 ArgStringList JobArgs;
1290 JobArgs.reserve(N: BuildArgs.size());
1291 for (const auto &Arg : BuildArgs)
1292 JobArgs.push_back(Elt: TCArgs.MakeArgString(Str: Arg));
1293
1294 const auto &D = C.getDriver();
1295 return std::make_unique<Command>(
1296 args&: *PA, args: ImportingJob.getCreator(), args: ResponseFileSupport::AtFileUTF8(),
1297 args: D.getDriverProgramPath(), args&: JobArgs,
1298 /*Inputs=*/args: ArrayRef<InputInfo>{},
1299 /*Outputs=*/args: ArrayRef<InputInfo>{}, args: D.getPrependArg());
1300}
1301
1302/// Creates a \c ClangModuleJobNode with associated job for each unique Clang
1303/// module in \p ModuleDepGraphsForScannedJobs.
1304///
1305/// \param ImportingJobs Jobs whose module dependencies were scanned.
1306/// \param ModuleDepGraphsForScannedJobs Full Clang module dependency graphs
1307/// corresponding to \p ImportingJobs, in order.
1308static void createClangModuleJobsAndNodes(
1309 CompilationGraph &Graph, Compilation &C,
1310 ArrayRef<std::unique_ptr<Command>> ImportingJobs,
1311 SmallVectorImpl<deps::ModuleDepsGraph> &&ModuleDepGraphsForScannedJobs) {
1312 llvm::DenseSet<deps::ModuleID> AlreadySeen;
1313 for (auto &&[ImportingJob, ModuleDepsGraph] :
1314 llvm::zip_equal(t: llvm::make_pointee_range(Range&: ImportingJobs),
1315 u&: ModuleDepGraphsForScannedJobs)) {
1316 for (auto &MD : ModuleDepsGraph) {
1317 const auto Inserted = AlreadySeen.insert(V: MD.ID).second;
1318 if (!Inserted)
1319 continue;
1320
1321 auto ClangModuleJob = createClangModulePrecompileJob(C, ImportingJob, MD);
1322 Graph.createJobNode<ClangModuleJobNode>(Arg: std::move(ClangModuleJob),
1323 Arg: std::move(MD));
1324 }
1325 }
1326}
1327
1328/// Installs the command lines produced by the dependency scan into
1329/// \p ScannedJobs.
1330static void
1331installScanCommandLines(Compilation &C,
1332 MutableArrayRef<std::unique_ptr<Command>> ScannedJobs,
1333 ArrayRef<InputDependencies> InputDepsForScannedJobs) {
1334 for (auto &&[Job, InputDeps] : llvm::zip_equal(
1335 t: llvm::make_pointee_range(Range&: ScannedJobs), u&: InputDepsForScannedJobs)) {
1336 const auto &BuildArgs = InputDeps.BuildArgs;
1337 ArgStringList JobArgs;
1338 JobArgs.reserve(N: BuildArgs.size());
1339
1340 auto &TCArgs = getToolChainArgs(C, Job);
1341 for (const auto &Arg : BuildArgs)
1342 JobArgs.push_back(Elt: TCArgs.MakeArgString(Str: Arg));
1343
1344 Job.replaceArguments(List: std::move(JobArgs));
1345 }
1346}
1347
1348/// Creates nodes for all jobs which were scanned for dependencies.
1349///
1350/// The updated command lines produced by the dependency scan are installed at a
1351/// later point.
1352static void createNodesForScannedJobs(
1353 CompilationGraph &Graph,
1354 SmallVectorImpl<std::unique_ptr<Command>> &&ScannedJobs,
1355 SmallVectorImpl<InputDependencies> &&InputDepsForScannedJobs) {
1356 for (auto &&[Job, InputDeps] :
1357 llvm::zip_equal(t&: ScannedJobs, u&: InputDepsForScannedJobs)) {
1358 if (InputDeps.ModuleName.empty())
1359 Graph.createJobNode<NonModuleTUJobNode>(Arg: std::move(Job),
1360 Arg: std::move(InputDeps));
1361 else
1362 Graph.createJobNode<NamedModuleJobNode>(Arg: std::move(Job),
1363 Arg: std::move(InputDeps));
1364 }
1365}
1366
1367template <typename LookupT, typename KeyRangeT>
1368static void connectEdgesViaLookup(CompilationGraph &Graph, CGNode &TgtNode,
1369 const LookupT &SrcNodeLookup,
1370 const KeyRangeT &SrcNodeLookupKeys,
1371 CGEdge::EdgeKind Kind) {
1372 for (const auto &Key : SrcNodeLookupKeys) {
1373 const auto It = SrcNodeLookup.find(Key);
1374 if (It == SrcNodeLookup.end())
1375 continue;
1376
1377 auto &SrcNode = *It->second;
1378 Graph.createEdge(Kind, Src&: SrcNode, Dst&: TgtNode);
1379 }
1380}
1381
1382/// Create edges for regular (non-module) dependencies in \p Graph.
1383static void createRegularEdges(CompilationGraph &Graph) {
1384 llvm::DenseMap<StringRef, CGNode *> NodeByOutputFiles;
1385 for (auto *Node : Graph) {
1386 for (const auto &Output : cast<JobNode>(Val: Node)->Job->getOutputFilenames()) {
1387 [[maybe_unused]] const bool Inserted =
1388 NodeByOutputFiles.try_emplace(Key: Output, Args&: Node).second;
1389 assert(Inserted &&
1390 "Driver should not produce multiple jobs with identical outputs!");
1391 }
1392 }
1393
1394 for (auto *Node : Graph) {
1395 const auto &InputInfos = cast<JobNode>(Val: Node)->Job->getInputInfos();
1396 auto InputFilenames = llvm::map_range(
1397 C: InputInfos, F: [](const auto &II) { return II.getFilename(); });
1398
1399 connectEdgesViaLookup(Graph, TgtNode&: *Node, SrcNodeLookup: NodeByOutputFiles, SrcNodeLookupKeys: InputFilenames,
1400 Kind: CGEdge::EdgeKind::Regular);
1401 }
1402}
1403
1404/// Create edges for module dependencies in \p Graph.
1405///
1406/// \returns false if there are multiple definitions for a named module, with
1407/// diagnostics reported to \p Diags; otherwise returns true.
1408static bool createModuleDependencyEdges(CompilationGraph &Graph,
1409 DiagnosticsEngine &Diags) {
1410 llvm::DenseMap<deps::ModuleID, CGNode *> ClangModuleNodeByID;
1411 llvm::DenseMap<ModuleNameAndTriple, CGNode *> NamedModuleNodeByID;
1412
1413 // Map each module to the job that produces it.
1414 bool HasDuplicateModuleError = false;
1415 for (auto *Node : Graph) {
1416 llvm::TypeSwitch<CGNode *>(Node)
1417 .Case(caseFn: [&](ClangModuleJobNode *ClangModuleNode) {
1418 [[maybe_unused]] const bool Inserted =
1419 ClangModuleNodeByID.try_emplace(Key: ClangModuleNode->MD.ID, Args&: Node)
1420 .second;
1421 assert(Inserted &&
1422 "Multiple Clang module nodes with the same module ID!");
1423 })
1424 .Case(caseFn: [&](NamedModuleJobNode *NamedModuleNode) {
1425 StringRef ModuleName = NamedModuleNode->InputDeps.ModuleName;
1426 ModuleNameAndTriple ID{ModuleName, getTriple(Job: *NamedModuleNode->Job)};
1427 const auto [It, Inserted] = NamedModuleNodeByID.try_emplace(Key: ID, Args&: Node);
1428 if (!Inserted) {
1429 // For scan input jobs, their first input is always a filename and
1430 // the scanned source.
1431 // We don't use InputDeps.FileDeps here because diagnostics should
1432 // refer to the filename as specified on the command line, not the
1433 // canonical absolute path.
1434 StringRef PrevFile =
1435 getFirstInputFilename(Job: *cast<JobNode>(Val: It->second)->Job);
1436 StringRef CurFile = getFirstInputFilename(Job: *NamedModuleNode->Job);
1437 Diags.Report(DiagID: diag::err_modules_driver_named_module_redefinition)
1438 << ModuleName << PrevFile << CurFile;
1439 HasDuplicateModuleError = true;
1440 }
1441 });
1442 }
1443 if (HasDuplicateModuleError)
1444 return false;
1445
1446 // Create edges from the module nodes to their importers.
1447 for (auto *Node : Graph) {
1448 llvm::TypeSwitch<CGNode *>(Node)
1449 .Case(caseFn: [&](ClangModuleJobNode *ClangModuleNode) {
1450 connectEdgesViaLookup(Graph, TgtNode&: *ClangModuleNode, SrcNodeLookup: ClangModuleNodeByID,
1451 SrcNodeLookupKeys: ClangModuleNode->MD.ClangModuleDeps,
1452 Kind: CGEdge::EdgeKind::ModuleDependency);
1453 })
1454 .Case(caseFn: [&](ScannedJobNode *NodeWithInputDeps) {
1455 connectEdgesViaLookup(Graph, TgtNode&: *NodeWithInputDeps, SrcNodeLookup: ClangModuleNodeByID,
1456 SrcNodeLookupKeys: NodeWithInputDeps->InputDeps.ClangModuleDeps,
1457 Kind: CGEdge::EdgeKind::ModuleDependency);
1458
1459 StringRef Triple = getTriple(Job: *NodeWithInputDeps->Job);
1460 const auto NamedModuleDepIDs =
1461 llvm::map_range(C&: NodeWithInputDeps->InputDeps.NamedModuleDeps,
1462 F: [&](StringRef ModuleName) {
1463 return ModuleNameAndTriple{ModuleName, Triple};
1464 });
1465 connectEdgesViaLookup(Graph, TgtNode&: *NodeWithInputDeps, SrcNodeLookup: NamedModuleNodeByID,
1466 SrcNodeLookupKeys: NamedModuleDepIDs,
1467 Kind: CGEdge::EdgeKind::ModuleDependency);
1468 });
1469 }
1470
1471 return true;
1472}
1473
1474/// Prunes the compilation graph of any jobs which build Standard library
1475/// modules not required in this compilation.
1476static void
1477pruneUnusedStdlibModuleJobs(CompilationGraph &Graph,
1478 ArrayRef<JobNode *> UnusedStdlibModuleJobNodes) {
1479 // Collect all reachable non-image job nodes.
1480 llvm::SmallPtrSet<JobNode *, 16> PrunableJobNodes;
1481 for (auto *PrunableJobNodeRoot : UnusedStdlibModuleJobNodes) {
1482 auto ReachableJobNodes =
1483 llvm::map_range(C: llvm::depth_first(G: cast<CGNode>(Val: PrunableJobNodeRoot)),
1484 F: llvm::CastTo<JobNode>);
1485 auto ReachableNonImageNodes = llvm::make_filter_range(
1486 Range&: ReachableJobNodes, Pred: [](auto *N) { return !llvm::isa<ImageJobNode>(N); });
1487 PrunableJobNodes.insert_range(R&: ReachableNonImageNodes);
1488 }
1489
1490 // Map image job nodes to the prunable job nodes that feed into them.
1491 llvm::DenseMap<ImageJobNode *, llvm::SmallPtrSet<JobNode *, 4>>
1492 PrunableJobNodesByImageNode;
1493 for (auto *PrunableJobNode : PrunableJobNodes) {
1494 auto ReachableJobNodes = llvm::depth_first(G: cast<CGNode>(Val: PrunableJobNode));
1495 auto ReachableImageJobNodes = llvm::map_range(
1496 C: llvm::make_filter_range(Range&: ReachableJobNodes, Pred: llvm::IsaPred<ImageJobNode>),
1497 F: llvm::CastTo<ImageJobNode>);
1498
1499 for (auto *ImageNode : ReachableImageJobNodes)
1500 PrunableJobNodesByImageNode[ImageNode].insert(Ptr: PrunableJobNode);
1501 }
1502
1503 // Remove from each affected image job node any arguments corresponding to
1504 // outputs of the connected prunable job nodes.
1505 for (auto &[ImageNode, PrunableJobNodeInputs] : PrunableJobNodesByImageNode) {
1506 SmallVector<StringRef, 4> OutputsToRemove;
1507 for (auto *JN : PrunableJobNodeInputs)
1508 llvm::append_range(C&: OutputsToRemove, R: JN->Job->getOutputFilenames());
1509
1510 auto NewArgs = ImageNode->Job->getArguments();
1511 llvm::erase_if(C&: NewArgs, P: [&](StringRef Arg) {
1512 return llvm::is_contained(Range&: OutputsToRemove, Element: Arg);
1513 });
1514 ImageNode->Job->replaceArguments(List: NewArgs);
1515 }
1516
1517 // Erase all prunable job nodes from the graph.
1518 for (auto *JN : PrunableJobNodes) {
1519 // Nodes are owned by the graph, but we can release the associated job.
1520 JN->Job.reset();
1521 Graph.removeNode(N&: *JN);
1522 }
1523}
1524
1525/// Creates the root node and connects it to all nodes with no incoming edges
1526/// ensuring that every node in the graph is reachable from the root.
1527static void createAndConnectRoot(CompilationGraph &Graph) {
1528 llvm::SmallPtrSet<CGNode *, 16> HasIncomingEdge;
1529 for (auto *Node : Graph)
1530 for (auto *Edge : Node->getEdges())
1531 HasIncomingEdge.insert(Ptr: &Edge->getTargetNode());
1532
1533 auto AllNonRootNodes = llvm::iterator_range(Graph);
1534 auto &Root = Graph.createRoot();
1535
1536 for (auto *Node : AllNonRootNodes) {
1537 if (HasIncomingEdge.contains(Ptr: Node))
1538 continue;
1539 Graph.createEdge(Kind: CGEdge::EdgeKind::Rooted, Src&: Root, Dst&: *Node);
1540 }
1541}
1542
1543/// Creates a temporary output path for \p ModuleName.
1544static std::string createModuleOutputPath(const Compilation &C,
1545 StringRef ModuleName) {
1546 // Sanitize the ':' included in parition names. It is illegal for filenames on
1547 // Windows.
1548 SmallString<32> SanitizedModuleName(ModuleName);
1549 llvm::replace(Range&: SanitizedModuleName, OldValue: ':', NewValue: '-');
1550 auto ModuleOutputPath = C.getDriver().GetTemporaryPath(
1551 Prefix: SanitizedModuleName, Suffix: types::getTypeTempSuffix(Id: types::TY_ModuleFile));
1552 return ModuleOutputPath;
1553}
1554
1555/// Adds the '-fmodule-output=' argument for the module produced by \p Node.
1556static void configureNamedModuleOutputArg(Compilation &C,
1557 NamedModuleJobNode &Node,
1558 StringRef ModuleOutputPath) {
1559 auto &Job = *Node.Job;
1560 const auto &TCArgs = getToolChainArgs(C, Job);
1561 auto JobArgs = Job.getArguments();
1562 JobArgs.push_back(
1563 Elt: TCArgs.MakeArgString(Str: "-fmodule-output=" + ModuleOutputPath));
1564 Job.replaceArguments(List: std::move(JobArgs));
1565}
1566
1567/// Propagates the '-fmodule-file=' mapping for the named module described by
1568/// \p Node to each dependent job.
1569static void propagateModuleFileMappingArg(Compilation &C,
1570 NamedModuleJobNode &Node,
1571 StringRef ModuleOutputPath) {
1572 const StringRef ModuleName = Node.InputDeps.ModuleName;
1573
1574 auto DependentNodes = llvm::drop_begin(RangeOrContainer: llvm::depth_first<CGNode *>(G: &Node));
1575 auto DependentScannedNodes = llvm::map_range(
1576 C: llvm::make_filter_range(Range&: DependentNodes, Pred: llvm::IsaPred<ScannedJobNode>),
1577 F: llvm::CastTo<ScannedJobNode>);
1578
1579 for (ScannedJobNode *DependentNode : DependentScannedNodes) {
1580 auto &DependentJob = *DependentNode->Job;
1581 const auto &TCArgs = getToolChainArgs(C, Job: DependentJob);
1582 auto JobArgs = DependentJob.getArguments();
1583 JobArgs.push_back(Elt: TCArgs.MakeArgString(Str: "-fmodule-file=" + ModuleName + "=" +
1584 ModuleOutputPath));
1585 DependentJob.replaceArguments(List: std::move(JobArgs));
1586 }
1587}
1588
1589/// Finalizes command lines for C++20 named module dependencies.
1590///
1591/// The command lines produced by dependency scanning are only adjusted to
1592/// handle discovered Clang modules. For C++20 named modules, we update the
1593/// command-lines here.
1594static void fixupNamedModuleCommandLines(Compilation &C,
1595 CompilationGraph &Graph) {
1596 const auto NamedModuleNodes = llvm::map_range(
1597 C: llvm::make_filter_range(Range&: Graph, Pred: llvm::IsaPred<NamedModuleJobNode>),
1598 F: llvm::CastTo<NamedModuleJobNode>);
1599
1600 for (NamedModuleJobNode *Node : NamedModuleNodes) {
1601 const auto &Job = *Node->Job;
1602
1603 // For Standard library modules, the driver already creates the module
1604 // output as a temp file, so we can use that path directly.
1605 const bool IsStdModule =
1606 Job.getInputInfos().front().getType() == types::TY_CXXStdModule;
1607 if (IsStdModule) {
1608 StringRef ModuleOutputPath = Job.getOutputFilenames().front();
1609 propagateModuleFileMappingArg(C, Node&: *Node, ModuleOutputPath);
1610 continue;
1611 }
1612
1613 const StringRef ModuleName = Node->InputDeps.ModuleName;
1614 const auto ModuleOutputPath = createModuleOutputPath(C, ModuleName);
1615 C.addTempFile(Name: C.getArgs().MakeArgString(Str: ModuleOutputPath));
1616
1617 configureNamedModuleOutputArg(C, Node&: *Node, ModuleOutputPath);
1618 propagateModuleFileMappingArg(C, Node&: *Node, ModuleOutputPath);
1619 }
1620}
1621
1622/// Moves jobs from \p Graph into \p C in the graph's topological order.
1623static void feedJobsBackIntoCompilation(Compilation &C,
1624 CompilationGraph &&Graph) {
1625 llvm::ReversePostOrderTraversal<CompilationGraph *> TopologicallySortedNodes(
1626 &Graph);
1627 assert(isa<RootNode>(*TopologicallySortedNodes.begin()) &&
1628 "First node in topological order must be the root!");
1629 auto TopologicallySortedJobNodes = llvm::map_range(
1630 C: llvm::drop_begin(RangeOrContainer&: TopologicallySortedNodes), F: llvm::CastTo<JobNode>);
1631 for (auto *JN : TopologicallySortedJobNodes)
1632 C.addCommand(Cmd: std::move(JN->Job));
1633}
1634
1635void driver::modules::runModulesDriver(
1636 Compilation &C, ArrayRef<StdModuleManifest::Module> ManifestEntries) {
1637 llvm::PrettyStackTraceString CrashInfo("Running modules driver.");
1638
1639 auto Jobs = C.getJobs().takeJobs();
1640
1641 const auto ManifestEntryBySource = buildManifestLookupMap(ManifestEntries);
1642 // Apply manifest-entry specific command-line modifications before the scan as
1643 // they might affect it.
1644 applyArgsForStdModuleManifestInputs(C, ManifestEntryBySource, Jobs);
1645
1646 DiagnosticsEngine &Diags = C.getDriver().getDiags();
1647
1648 // Run the dependency scan.
1649 const auto MaybeModuleCachePath = getModuleCachePath(Args&: C.getArgs());
1650 if (!MaybeModuleCachePath) {
1651 Diags.Report(DiagID: diag::err_default_modules_cache_not_available);
1652 return;
1653 }
1654
1655 auto MaybeCWD = C.getDriver().getVFS().getCurrentWorkingDirectory();
1656 const auto CWD = MaybeCWD ? std::move(*MaybeCWD) : ".";
1657
1658 auto MaybeScanResults = scanDependencies(Jobs, ManifestLookup: ManifestEntryBySource,
1659 ModuleCachePath: *MaybeModuleCachePath, WorkingDirectory: CWD, Diags);
1660 if (!MaybeScanResults) {
1661 Diags.Report(DiagID: diag::err_dependency_scan_failed);
1662 return;
1663 }
1664 auto &ScanResult = *MaybeScanResults;
1665
1666 // Build the compilation graph.
1667 CompilationGraph Graph;
1668 createNodesForNonScannableJobs(
1669 Graph, NonScannableJobs: takeJobsAtIndices(Jobs, Indices: ScanResult.NonScannableJobIndices));
1670 auto UnusedStdlibModuleJobNodes = createNodesForUnusedStdlibModuleJobs(
1671 Graph, UnusedStdlibModuleJobs: takeJobsAtIndices(Jobs, Indices: ScanResult.UnusedStdlibModuleJobIndices));
1672
1673 auto ScannedJobs = takeJobsAtIndices(Jobs, Indices: ScanResult.ScannedJobIndices);
1674 if (!validateScannedJobInputKinds(ScannedJobs,
1675 InputDepsForScannedJobs: ScanResult.InputDepsForScannedJobs, Diags))
1676 return;
1677 installScanCommandLines(C, ScannedJobs, InputDepsForScannedJobs: ScanResult.InputDepsForScannedJobs);
1678
1679 createClangModuleJobsAndNodes(
1680 Graph, C, /*ImportingJobs*/ ScannedJobs,
1681 ModuleDepGraphsForScannedJobs: std::move(ScanResult.ModuleDepGraphsForScannedJobs));
1682 createNodesForScannedJobs(Graph, ScannedJobs: std::move(ScannedJobs),
1683 InputDepsForScannedJobs: std::move(ScanResult.InputDepsForScannedJobs));
1684
1685 createRegularEdges(Graph);
1686 pruneUnusedStdlibModuleJobs(Graph, UnusedStdlibModuleJobNodes);
1687 if (!createModuleDependencyEdges(Graph, Diags))
1688 return;
1689 createAndConnectRoot(Graph);
1690
1691 Diags.Report(DiagID: diag::remark_printing_module_graph);
1692 if (!Diags.isLastDiagnosticIgnored())
1693 llvm::WriteGraph<const CompilationGraph *>(O&: llvm::errs(), G: &Graph);
1694
1695 fixupNamedModuleCommandLines(C, Graph);
1696 feedJobsBackIntoCompilation(C, Graph: std::move(Graph));
1697}
1698