xref: /llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp (revision df9a14d7bbf1180e4f1474254c9d7ed6bcb4ce55)
1 //===- DependencyScanningWorker.cpp - clang-scan-deps worker --------------===//
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 #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
10 #include "clang/Basic/DiagnosticDriver.h"
11 #include "clang/Basic/DiagnosticFrontend.h"
12 #include "clang/Basic/DiagnosticSerialization.h"
13 #include "clang/Driver/Compilation.h"
14 #include "clang/Driver/Driver.h"
15 #include "clang/Driver/Job.h"
16 #include "clang/Driver/Tool.h"
17 #include "clang/Frontend/CompilerInstance.h"
18 #include "clang/Frontend/CompilerInvocation.h"
19 #include "clang/Frontend/FrontendActions.h"
20 #include "clang/Frontend/TextDiagnosticPrinter.h"
21 #include "clang/Frontend/Utils.h"
22 #include "clang/Lex/PreprocessorOptions.h"
23 #include "clang/Serialization/ObjectFilePCHContainerReader.h"
24 #include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
25 #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
26 #include "clang/Tooling/Tooling.h"
27 #include "llvm/ADT/ScopeExit.h"
28 #include "llvm/Support/Allocator.h"
29 #include "llvm/Support/Error.h"
30 #include "llvm/TargetParser/Host.h"
31 #include <optional>
32 
33 using namespace clang;
34 using namespace tooling;
35 using namespace dependencies;
36 
37 namespace {
38 
39 /// Forwards the gatherered dependencies to the consumer.
40 class DependencyConsumerForwarder : public DependencyFileGenerator {
41 public:
42   DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
43                               StringRef WorkingDirectory, DependencyConsumer &C)
44       : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory),
45         Opts(std::move(Opts)), C(C) {}
46 
47   void finishedMainFile(DiagnosticsEngine &Diags) override {
48     C.handleDependencyOutputOpts(*Opts);
49     llvm::SmallString<256> CanonPath;
50     for (const auto &File : getDependencies()) {
51       CanonPath = File;
52       llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
53       llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath);
54       C.handleFileDependency(CanonPath);
55     }
56   }
57 
58 private:
59   StringRef WorkingDirectory;
60   std::unique_ptr<DependencyOutputOptions> Opts;
61   DependencyConsumer &C;
62 };
63 
64 static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
65                                    const HeaderSearchOptions &ExistingHSOpts,
66                                    DiagnosticsEngine *Diags,
67                                    const LangOptions &LangOpts) {
68   if (LangOpts.Modules) {
69     if (HSOpts.VFSOverlayFiles != ExistingHSOpts.VFSOverlayFiles) {
70       if (Diags) {
71         Diags->Report(diag::warn_pch_vfsoverlay_mismatch);
72         auto VFSNote = [&](int Type, ArrayRef<std::string> VFSOverlays) {
73           if (VFSOverlays.empty()) {
74             Diags->Report(diag::note_pch_vfsoverlay_empty) << Type;
75           } else {
76             std::string Files = llvm::join(VFSOverlays, "\n");
77             Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files;
78           }
79         };
80         VFSNote(0, HSOpts.VFSOverlayFiles);
81         VFSNote(1, ExistingHSOpts.VFSOverlayFiles);
82       }
83     }
84   }
85   return false;
86 }
87 
88 using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
89 
90 /// A listener that collects the imported modules and optionally the input
91 /// files.
92 class PrebuiltModuleListener : public ASTReaderListener {
93 public:
94   PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
95                          llvm::SmallVector<std::string> &NewModuleFiles,
96                          PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
97                          const HeaderSearchOptions &HSOpts,
98                          const LangOptions &LangOpts, DiagnosticsEngine &Diags)
99       : PrebuiltModuleFiles(PrebuiltModuleFiles),
100         NewModuleFiles(NewModuleFiles),
101         PrebuiltModuleVFSMap(PrebuiltModuleVFSMap), ExistingHSOpts(HSOpts),
102         ExistingLangOpts(LangOpts), Diags(Diags) {}
103 
104   bool needsImportVisitation() const override { return true; }
105 
106   void visitImport(StringRef ModuleName, StringRef Filename) override {
107     if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
108       NewModuleFiles.push_back(Filename.str());
109   }
110 
111   void visitModuleFile(StringRef Filename,
112                        serialization::ModuleKind Kind) override {
113     CurrentFile = Filename;
114   }
115 
116   bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
117                              bool Complain) override {
118     std::vector<std::string> VFSOverlayFiles = HSOpts.VFSOverlayFiles;
119     PrebuiltModuleVFSMap.insert(
120         {CurrentFile, llvm::StringSet<>(VFSOverlayFiles)});
121     return checkHeaderSearchPaths(
122         HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);
123   }
124 
125 private:
126   PrebuiltModuleFilesT &PrebuiltModuleFiles;
127   llvm::SmallVector<std::string> &NewModuleFiles;
128   PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap;
129   const HeaderSearchOptions &ExistingHSOpts;
130   const LangOptions &ExistingLangOpts;
131   DiagnosticsEngine &Diags;
132   std::string CurrentFile;
133 };
134 
135 /// Visit the given prebuilt module and collect all of the modules it
136 /// transitively imports and contributing input files.
137 static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
138                                 CompilerInstance &CI,
139                                 PrebuiltModuleFilesT &ModuleFiles,
140                                 PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
141                                 DiagnosticsEngine &Diags) {
142   // List of module files to be processed.
143   llvm::SmallVector<std::string> Worklist;
144   PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModuleVFSMap,
145                                   CI.getHeaderSearchOpts(), CI.getLangOpts(),
146                                   Diags);
147 
148   Listener.visitModuleFile(PrebuiltModuleFilename,
149                            serialization::MK_ExplicitModule);
150   if (ASTReader::readASTFileControlBlock(
151           PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(),
152           CI.getPCHContainerReader(),
153           /*FindModuleFileExtensions=*/false, Listener,
154           /*ValidateDiagnosticOptions=*/false, ASTReader::ARR_OutOfDate))
155     return true;
156 
157   while (!Worklist.empty()) {
158     Listener.visitModuleFile(Worklist.back(), serialization::MK_ExplicitModule);
159     if (ASTReader::readASTFileControlBlock(
160             Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
161             CI.getPCHContainerReader(),
162             /*FindModuleFileExtensions=*/false, Listener,
163             /*ValidateDiagnosticOptions=*/false))
164       return true;
165   }
166   return false;
167 }
168 
169 /// Transform arbitrary file name into an object-like file name.
170 static std::string makeObjFileName(StringRef FileName) {
171   SmallString<128> ObjFileName(FileName);
172   llvm::sys::path::replace_extension(ObjFileName, "o");
173   return std::string(ObjFileName);
174 }
175 
176 /// Deduce the dependency target based on the output file and input files.
177 static std::string
178 deduceDepTarget(const std::string &OutputFile,
179                 const SmallVectorImpl<FrontendInputFile> &InputFiles) {
180   if (OutputFile != "-")
181     return OutputFile;
182 
183   if (InputFiles.empty() || !InputFiles.front().isFile())
184     return "clang-scan-deps\\ dependency";
185 
186   return makeObjFileName(InputFiles.front().getFile());
187 }
188 
189 /// Sanitize diagnostic options for dependency scan.
190 static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
191   // Don't print 'X warnings and Y errors generated'.
192   DiagOpts.ShowCarets = false;
193   // Don't write out diagnostic file.
194   DiagOpts.DiagnosticSerializationFile.clear();
195   // Don't emit warnings except for scanning specific warnings.
196   // TODO: It would be useful to add a more principled way to ignore all
197   //       warnings that come from source code. The issue is that we need to
198   //       ignore warnings that could be surpressed by
199   //       `#pragma clang diagnostic`, while still allowing some scanning
200   //       warnings for things we're not ready to turn into errors yet.
201   //       See `test/ClangScanDeps/diagnostic-pragmas.c` for an example.
202   llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) {
203     return llvm::StringSwitch<bool>(Warning)
204         .Cases("pch-vfs-diff", "error=pch-vfs-diff", false)
205         .StartsWith("no-error=", false)
206         .Default(true);
207   });
208 }
209 
210 // Clang implements -D and -U by splatting text into a predefines buffer. This
211 // allows constructs such as `-DFඞ=3 "-D F\u{0D9E} 4 3 2”` to be accepted and
212 // define the same macro, or adding C++ style comments before the macro name.
213 //
214 // This function checks that the first non-space characters in the macro
215 // obviously form an identifier that can be uniqued on without lexing. Failing
216 // to do this could lead to changing the final definition of a macro.
217 //
218 // We could set up a preprocessor and actually lex the name, but that's very
219 // heavyweight for a situation that will almost never happen in practice.
220 static std::optional<StringRef> getSimpleMacroName(StringRef Macro) {
221   StringRef Name = Macro.split("=").first.ltrim(" \t");
222   std::size_t I = 0;
223 
224   auto FinishName = [&]() -> std::optional<StringRef> {
225     StringRef SimpleName = Name.slice(0, I);
226     if (SimpleName.empty())
227       return std::nullopt;
228     return SimpleName;
229   };
230 
231   for (; I != Name.size(); ++I) {
232     switch (Name[I]) {
233     case '(': // Start of macro parameter list
234     case ' ': // End of macro name
235     case '\t':
236       return FinishName();
237     case '_':
238       continue;
239     default:
240       if (llvm::isAlnum(Name[I]))
241         continue;
242       return std::nullopt;
243     }
244   }
245   return FinishName();
246 }
247 
248 static void canonicalizeDefines(PreprocessorOptions &PPOpts) {
249   using MacroOpt = std::pair<StringRef, std::size_t>;
250   std::vector<MacroOpt> SimpleNames;
251   SimpleNames.reserve(PPOpts.Macros.size());
252   std::size_t Index = 0;
253   for (const auto &M : PPOpts.Macros) {
254     auto SName = getSimpleMacroName(M.first);
255     // Skip optimizing if we can't guarantee we can preserve relative order.
256     if (!SName)
257       return;
258     SimpleNames.emplace_back(*SName, Index);
259     ++Index;
260   }
261 
262   llvm::stable_sort(SimpleNames, llvm::less_first());
263   // Keep the last instance of each macro name by going in reverse
264   auto NewEnd = std::unique(
265       SimpleNames.rbegin(), SimpleNames.rend(),
266       [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; });
267   SimpleNames.erase(SimpleNames.begin(), NewEnd.base());
268 
269   // Apply permutation.
270   decltype(PPOpts.Macros) NewMacros;
271   NewMacros.reserve(SimpleNames.size());
272   for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) {
273     std::size_t OriginalIndex = SimpleNames[I].second;
274     // We still emit undefines here as they may be undefining a predefined macro
275     NewMacros.push_back(std::move(PPOpts.Macros[OriginalIndex]));
276   }
277   std::swap(PPOpts.Macros, NewMacros);
278 }
279 
280 /// A clang tool that runs the preprocessor in a mode that's optimized for
281 /// dependency scanning for the given compiler invocation.
282 class DependencyScanningAction : public tooling::ToolAction {
283 public:
284   DependencyScanningAction(
285       StringRef WorkingDirectory, DependencyConsumer &Consumer,
286       DependencyActionController &Controller,
287       llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
288       ScanningOutputFormat Format, ScanningOptimizations OptimizeArgs,
289       bool EagerLoadModules, bool DisableFree,
290       std::optional<StringRef> ModuleName = std::nullopt)
291       : WorkingDirectory(WorkingDirectory), Consumer(Consumer),
292         Controller(Controller), DepFS(std::move(DepFS)), Format(Format),
293         OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules),
294         DisableFree(DisableFree), ModuleName(ModuleName) {}
295 
296   bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
297                      FileManager *DriverFileMgr,
298                      std::shared_ptr<PCHContainerOperations> PCHContainerOps,
299                      DiagnosticConsumer *DiagConsumer) override {
300     // Make a deep copy of the original Clang invocation.
301     CompilerInvocation OriginalInvocation(*Invocation);
302     // Restore the value of DisableFree, which may be modified by Tooling.
303     OriginalInvocation.getFrontendOpts().DisableFree = DisableFree;
304     if (any(OptimizeArgs & ScanningOptimizations::Macros))
305       canonicalizeDefines(OriginalInvocation.getPreprocessorOpts());
306 
307     if (Scanned) {
308       // Scanning runs once for the first -cc1 invocation in a chain of driver
309       // jobs. For any dependent jobs, reuse the scanning result and just
310       // update the LastCC1Arguments to correspond to the new invocation.
311       // FIXME: to support multi-arch builds, each arch requires a separate scan
312       setLastCC1Arguments(std::move(OriginalInvocation));
313       return true;
314     }
315 
316     Scanned = true;
317 
318     // Create a compiler instance to handle the actual work.
319     ScanInstanceStorage.emplace(std::move(PCHContainerOps));
320     CompilerInstance &ScanInstance = *ScanInstanceStorage;
321     ScanInstance.setInvocation(std::move(Invocation));
322 
323     // Create the compiler's actual diagnostics engine.
324     sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
325     ScanInstance.createDiagnostics(DriverFileMgr->getVirtualFileSystem(),
326                                    DiagConsumer, /*ShouldOwnClient=*/false);
327     if (!ScanInstance.hasDiagnostics())
328       return false;
329 
330     // Some DiagnosticConsumers require that finish() is called.
331     auto DiagConsumerFinisher =
332         llvm::make_scope_exit([DiagConsumer]() { DiagConsumer->finish(); });
333 
334     ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =
335         true;
336 
337     ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
338     ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
339     // This will prevent us compiling individual modules asynchronously since
340     // FileManager is not thread-safe, but it does improve performance for now.
341     ScanInstance.getFrontendOpts().ModulesShareFileManager = true;
342     ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
343     ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage =
344         any(OptimizeArgs & ScanningOptimizations::VFS);
345 
346     // Support for virtual file system overlays.
347     auto FS = createVFSFromCompilerInvocation(
348         ScanInstance.getInvocation(), ScanInstance.getDiagnostics(),
349         DriverFileMgr->getVirtualFileSystemPtr());
350 
351     // Use the dependency scanning optimized file system if requested to do so.
352     if (DepFS) {
353       StringRef ModulesCachePath =
354           ScanInstance.getHeaderSearchOpts().ModuleCachePath;
355 
356       DepFS->resetBypassedPathPrefix();
357       if (!ModulesCachePath.empty())
358         DepFS->setBypassedPathPrefix(ModulesCachePath);
359 
360       ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
361           [LocalDepFS = DepFS](FileEntryRef File)
362           -> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
363         if (llvm::ErrorOr<EntryRef> Entry =
364                 LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
365           if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
366             return Entry->getDirectiveTokens();
367         return std::nullopt;
368       };
369     }
370 
371     // Create a new FileManager to match the invocation's FileSystemOptions.
372     auto *FileMgr = ScanInstance.createFileManager(FS);
373     ScanInstance.createSourceManager(*FileMgr);
374 
375     // Store the list of prebuilt module files into header search options. This
376     // will prevent the implicit build to create duplicate modules and will
377     // force reuse of the existing prebuilt module files instead.
378     PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
379     if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
380       if (visitPrebuiltModule(
381               ScanInstance.getPreprocessorOpts().ImplicitPCHInclude,
382               ScanInstance,
383               ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
384               PrebuiltModuleVFSMap, ScanInstance.getDiagnostics()))
385         return false;
386 
387     // Create the dependency collector that will collect the produced
388     // dependencies.
389     //
390     // This also moves the existing dependency output options from the
391     // invocation to the collector. The options in the invocation are reset,
392     // which ensures that the compiler won't create new dependency collectors,
393     // and thus won't write out the extra '.d' files to disk.
394     auto Opts = std::make_unique<DependencyOutputOptions>();
395     std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());
396     // We need at least one -MT equivalent for the generator of make dependency
397     // files to work.
398     if (Opts->Targets.empty())
399       Opts->Targets = {
400           deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile,
401                           ScanInstance.getFrontendOpts().Inputs)};
402     Opts->IncludeSystemHeaders = true;
403 
404     switch (Format) {
405     case ScanningOutputFormat::Make:
406       ScanInstance.addDependencyCollector(
407           std::make_shared<DependencyConsumerForwarder>(
408               std::move(Opts), WorkingDirectory, Consumer));
409       break;
410     case ScanningOutputFormat::P1689:
411     case ScanningOutputFormat::Full:
412       MDC = std::make_shared<ModuleDepCollector>(
413           std::move(Opts), ScanInstance, Consumer, Controller,
414           OriginalInvocation, std::move(PrebuiltModuleVFSMap), OptimizeArgs,
415           EagerLoadModules, Format == ScanningOutputFormat::P1689);
416       ScanInstance.addDependencyCollector(MDC);
417       break;
418     }
419 
420     // Consider different header search and diagnostic options to create
421     // different modules. This avoids the unsound aliasing of module PCMs.
422     //
423     // TODO: Implement diagnostic bucketing to reduce the impact of strict
424     // context hashing.
425     ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
426     ScanInstance.getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true;
427     ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
428     ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;
429     ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings =
430         true;
431 
432     // Avoid some checks and module map parsing when loading PCM files.
433     ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
434 
435     std::unique_ptr<FrontendAction> Action;
436 
437     if (Format == ScanningOutputFormat::P1689)
438       Action = std::make_unique<PreprocessOnlyAction>();
439     else if (ModuleName)
440       Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
441     else
442       Action = std::make_unique<ReadPCHAndPreprocessAction>();
443 
444     if (ScanInstance.getDiagnostics().hasErrorOccurred())
445       return false;
446 
447     // Each action is responsible for calling finish.
448     DiagConsumerFinisher.release();
449     const bool Result = ScanInstance.ExecuteAction(*Action);
450 
451     if (Result)
452       setLastCC1Arguments(std::move(OriginalInvocation));
453 
454     // Propagate the statistics to the parent FileManager.
455     DriverFileMgr->AddStats(ScanInstance.getFileManager());
456 
457     return Result;
458   }
459 
460   bool hasScanned() const { return Scanned; }
461 
462   /// Take the cc1 arguments corresponding to the most recent invocation used
463   /// with this action. Any modifications implied by the discovered dependencies
464   /// will have already been applied.
465   std::vector<std::string> takeLastCC1Arguments() {
466     std::vector<std::string> Result;
467     std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty.
468     return Result;
469   }
470 
471 private:
472   void setLastCC1Arguments(CompilerInvocation &&CI) {
473     if (MDC)
474       MDC->applyDiscoveredDependencies(CI);
475     LastCC1Arguments = CI.getCC1CommandLine();
476   }
477 
478 private:
479   StringRef WorkingDirectory;
480   DependencyConsumer &Consumer;
481   DependencyActionController &Controller;
482   llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
483   ScanningOutputFormat Format;
484   ScanningOptimizations OptimizeArgs;
485   bool EagerLoadModules;
486   bool DisableFree;
487   std::optional<StringRef> ModuleName;
488   std::optional<CompilerInstance> ScanInstanceStorage;
489   std::shared_ptr<ModuleDepCollector> MDC;
490   std::vector<std::string> LastCC1Arguments;
491   bool Scanned = false;
492 };
493 
494 } // end anonymous namespace
495 
496 DependencyScanningWorker::DependencyScanningWorker(
497     DependencyScanningService &Service,
498     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
499     : Format(Service.getFormat()), OptimizeArgs(Service.getOptimizeArgs()),
500       EagerLoadModules(Service.shouldEagerLoadModules()) {
501   PCHContainerOps = std::make_shared<PCHContainerOperations>();
502   // We need to read object files from PCH built outside the scanner.
503   PCHContainerOps->registerReader(
504       std::make_unique<ObjectFilePCHContainerReader>());
505   // The scanner itself writes only raw ast files.
506   PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
507 
508   if (Service.shouldTraceVFS())
509     FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS));
510 
511   switch (Service.getMode()) {
512   case ScanningMode::DependencyDirectivesScan:
513     DepFS =
514         new DependencyScanningWorkerFilesystem(Service.getSharedCache(), FS);
515     BaseFS = DepFS;
516     break;
517   case ScanningMode::CanonicalPreprocessing:
518     DepFS = nullptr;
519     BaseFS = FS;
520     break;
521   }
522 }
523 
524 llvm::Error DependencyScanningWorker::computeDependencies(
525     StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
526     DependencyConsumer &Consumer, DependencyActionController &Controller,
527     std::optional<StringRef> ModuleName) {
528   std::vector<const char *> CLI;
529   for (const std::string &Arg : CommandLine)
530     CLI.push_back(Arg.c_str());
531   auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
532   sanitizeDiagOpts(*DiagOpts);
533 
534   // Capture the emitted diagnostics and report them to the client
535   // in the case of a failure.
536   std::string DiagnosticOutput;
537   llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
538   TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
539 
540   if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
541                           DiagPrinter, ModuleName))
542     return llvm::Error::success();
543   return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
544                                              llvm::inconvertibleErrorCode());
545 }
546 
547 static bool forEachDriverJob(
548     ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, FileManager &FM,
549     llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
550   SmallVector<const char *, 256> Argv;
551   Argv.reserve(ArgStrs.size());
552   for (const std::string &Arg : ArgStrs)
553     Argv.push_back(Arg.c_str());
554 
555   llvm::vfs::FileSystem *FS = &FM.getVirtualFileSystem();
556 
557   std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
558       Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
559       "clang LLVM compiler", FS);
560   Driver->setTitle("clang_based_tool");
561 
562   llvm::BumpPtrAllocator Alloc;
563   bool CLMode = driver::IsClangCL(
564       driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
565 
566   if (llvm::Error E = driver::expandResponseFiles(Argv, CLMode, Alloc, FS)) {
567     Diags.Report(diag::err_drv_expand_response_file)
568         << llvm::toString(std::move(E));
569     return false;
570   }
571 
572   const std::unique_ptr<driver::Compilation> Compilation(
573       Driver->BuildCompilation(llvm::ArrayRef(Argv)));
574   if (!Compilation)
575     return false;
576 
577   if (Compilation->containsError())
578     return false;
579 
580   for (const driver::Command &Job : Compilation->getJobs()) {
581     if (!Callback(Job))
582       return false;
583   }
584   return true;
585 }
586 
587 static bool createAndRunToolInvocation(
588     std::vector<std::string> CommandLine, DependencyScanningAction &Action,
589     FileManager &FM,
590     std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
591     DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {
592 
593   // Save executable path before providing CommandLine to ToolInvocation
594   std::string Executable = CommandLine[0];
595   ToolInvocation Invocation(std::move(CommandLine), &Action, &FM,
596                             PCHContainerOps);
597   Invocation.setDiagnosticConsumer(Diags.getClient());
598   Invocation.setDiagnosticOptions(&Diags.getDiagnosticOptions());
599   if (!Invocation.run())
600     return false;
601 
602   std::vector<std::string> Args = Action.takeLastCC1Arguments();
603   Consumer.handleBuildCommand({std::move(Executable), std::move(Args)});
604   return true;
605 }
606 
607 bool DependencyScanningWorker::computeDependencies(
608     StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
609     DependencyConsumer &Consumer, DependencyActionController &Controller,
610     DiagnosticConsumer &DC, std::optional<StringRef> ModuleName) {
611   // Reset what might have been modified in the previous worker invocation.
612   BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
613 
614   std::optional<std::vector<std::string>> ModifiedCommandLine;
615   llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
616 
617   // If we're scanning based on a module name alone, we don't expect the client
618   // to provide us with an input file. However, the driver really wants to have
619   // one. Let's just make it up to make the driver happy.
620   if (ModuleName) {
621     auto OverlayFS =
622         llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
623     auto InMemoryFS =
624         llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
625     InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
626     OverlayFS->pushOverlay(InMemoryFS);
627     ModifiedFS = OverlayFS;
628 
629     SmallString<128> FakeInputPath;
630     // TODO: We should retry the creation if the path already exists.
631     llvm::sys::fs::createUniquePath(*ModuleName + "-%%%%%%%%.input",
632                                     FakeInputPath,
633                                     /*MakeAbsolute=*/false);
634     InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
635 
636     ModifiedCommandLine = CommandLine;
637     ModifiedCommandLine->emplace_back(FakeInputPath);
638   }
639 
640   const std::vector<std::string> &FinalCommandLine =
641       ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
642   auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
643 
644   auto FileMgr =
645       llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions{}, FinalFS);
646 
647   std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(), nullptr);
648   llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),
649                   [](const std::string &Str) { return Str.c_str(); });
650 
651   auto DiagOpts = CreateAndPopulateDiagOpts(FinalCCommandLine);
652   sanitizeDiagOpts(*DiagOpts);
653   IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
654       CompilerInstance::createDiagnostics(*FinalFS, DiagOpts.release(), &DC,
655                                           /*ShouldOwnClient=*/false);
656 
657   // Although `Diagnostics` are used only for command-line parsing, the
658   // custom `DiagConsumer` might expect a `SourceManager` to be present.
659   SourceManager SrcMgr(*Diags, *FileMgr);
660   Diags->setSourceManager(&SrcMgr);
661   // DisableFree is modified by Tooling for running
662   // in-process; preserve the original value, which is
663   // always true for a driver invocation.
664   bool DisableFree = true;
665   DependencyScanningAction Action(WorkingDirectory, Consumer, Controller, DepFS,
666                                   Format, OptimizeArgs, EagerLoadModules,
667                                   DisableFree, ModuleName);
668 
669   bool Success = false;
670   if (FinalCommandLine[1] == "-cc1") {
671     Success = createAndRunToolInvocation(FinalCommandLine, Action, *FileMgr,
672                                          PCHContainerOps, *Diags, Consumer);
673   } else {
674     Success = forEachDriverJob(
675         FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
676           if (StringRef(Cmd.getCreator().getName()) != "clang") {
677             // Non-clang command. Just pass through to the dependency
678             // consumer.
679             Consumer.handleBuildCommand(
680                 {Cmd.getExecutable(),
681                  {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
682             return true;
683           }
684 
685           // Insert -cc1 comand line options into Argv
686           std::vector<std::string> Argv;
687           Argv.push_back(Cmd.getExecutable());
688           Argv.insert(Argv.end(), Cmd.getArguments().begin(),
689                       Cmd.getArguments().end());
690 
691           // Create an invocation that uses the underlying file
692           // system to ensure that any file system requests that
693           // are made by the driver do not go through the
694           // dependency scanning filesystem.
695           return createAndRunToolInvocation(std::move(Argv), Action, *FileMgr,
696                                             PCHContainerOps, *Diags, Consumer);
697         });
698   }
699 
700   if (Success && !Action.hasScanned())
701     Diags->Report(diag::err_fe_expected_compiler_job)
702         << llvm::join(FinalCommandLine, " ");
703   return Success && Action.hasScanned();
704 }
705 
706 DependencyActionController::~DependencyActionController() {}
707