clang  20.0.0git
DependencyScanningWorker.cpp
Go to the documentation of this file.
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 
14 #include "clang/Driver/Driver.h"
15 #include "clang/Driver/Job.h"
16 #include "clang/Driver/Tool.h"
21 #include "clang/Frontend/Utils.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;
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.
144  PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModuleVFSMap,
145  CI.getHeaderSearchOpts(), CI.getLangOpts(),
146  Diags);
147 
148  Listener.visitModuleFile(PrebuiltModuleFilename,
151  PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(),
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);
160  Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
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,
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(DiagConsumer, /*ShouldOwnClient=*/false);
326  if (!ScanInstance.hasDiagnostics())
327  return false;
328 
329  // Some DiagnosticConsumers require that finish() is called.
330  auto DiagConsumerFinisher =
331  llvm::make_scope_exit([DiagConsumer]() { DiagConsumer->finish(); });
332 
334  true;
335 
336  ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
337  ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
338  ScanInstance.getFrontendOpts().ModulesShareFileManager = false;
339  ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
341  any(OptimizeArgs & ScanningOptimizations::VFS);
342 
343  // Support for virtual file system overlays.
345  ScanInstance.getInvocation(), ScanInstance.getDiagnostics(),
346  DriverFileMgr->getVirtualFileSystemPtr());
347 
348  // Use the dependency scanning optimized file system if requested to do so.
349  if (DepFS) {
350  StringRef ModulesCachePath =
351  ScanInstance.getHeaderSearchOpts().ModuleCachePath;
352 
353  DepFS->resetBypassedPathPrefix();
354  if (!ModulesCachePath.empty())
355  DepFS->setBypassedPathPrefix(ModulesCachePath);
356 
358  [LocalDepFS = DepFS](FileEntryRef File)
360  if (llvm::ErrorOr<EntryRef> Entry =
361  LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
362  if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
363  return Entry->getDirectiveTokens();
364  return std::nullopt;
365  };
366  }
367 
368  // Create a new FileManager to match the invocation's FileSystemOptions.
369  auto *FileMgr = ScanInstance.createFileManager(FS);
370  ScanInstance.createSourceManager(*FileMgr);
371 
372  // Store the list of prebuilt module files into header search options. This
373  // will prevent the implicit build to create duplicate modules and will
374  // force reuse of the existing prebuilt module files instead.
375  PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
376  if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
377  if (visitPrebuiltModule(
379  ScanInstance,
381  PrebuiltModuleVFSMap, ScanInstance.getDiagnostics()))
382  return false;
383 
384  // Create the dependency collector that will collect the produced
385  // dependencies.
386  //
387  // This also moves the existing dependency output options from the
388  // invocation to the collector. The options in the invocation are reset,
389  // which ensures that the compiler won't create new dependency collectors,
390  // and thus won't write out the extra '.d' files to disk.
391  auto Opts = std::make_unique<DependencyOutputOptions>();
392  std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());
393  // We need at least one -MT equivalent for the generator of make dependency
394  // files to work.
395  if (Opts->Targets.empty())
396  Opts->Targets = {
397  deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile,
398  ScanInstance.getFrontendOpts().Inputs)};
399  Opts->IncludeSystemHeaders = true;
400 
401  switch (Format) {
403  ScanInstance.addDependencyCollector(
404  std::make_shared<DependencyConsumerForwarder>(
405  std::move(Opts), WorkingDirectory, Consumer));
406  break;
409  MDC = std::make_shared<ModuleDepCollector>(
410  std::move(Opts), ScanInstance, Consumer, Controller,
411  OriginalInvocation, std::move(PrebuiltModuleVFSMap), OptimizeArgs,
412  EagerLoadModules, Format == ScanningOutputFormat::P1689);
413  ScanInstance.addDependencyCollector(MDC);
414  break;
415  }
416 
417  // Consider different header search and diagnostic options to create
418  // different modules. This avoids the unsound aliasing of module PCMs.
419  //
420  // TODO: Implement diagnostic bucketing to reduce the impact of strict
421  // context hashing.
422  ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
426  true;
427 
428  // Avoid some checks and module map parsing when loading PCM files.
429  ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
430 
431  std::unique_ptr<FrontendAction> Action;
432 
433  if (ModuleName)
434  Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
435  else
436  Action = std::make_unique<ReadPCHAndPreprocessAction>();
437 
438  if (ScanInstance.getDiagnostics().hasErrorOccurred())
439  return false;
440 
441  // Each action is responsible for calling finish.
442  DiagConsumerFinisher.release();
443  const bool Result = ScanInstance.ExecuteAction(*Action);
444 
445  if (Result)
446  setLastCC1Arguments(std::move(OriginalInvocation));
447 
448  // Propagate the statistics to the parent FileManager.
449  DriverFileMgr->AddStats(ScanInstance.getFileManager());
450 
451  return Result;
452  }
453 
454  bool hasScanned() const { return Scanned; }
455 
456  /// Take the cc1 arguments corresponding to the most recent invocation used
457  /// with this action. Any modifications implied by the discovered dependencies
458  /// will have already been applied.
459  std::vector<std::string> takeLastCC1Arguments() {
460  std::vector<std::string> Result;
461  std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty.
462  return Result;
463  }
464 
465 private:
466  void setLastCC1Arguments(CompilerInvocation &&CI) {
467  if (MDC)
468  MDC->applyDiscoveredDependencies(CI);
469  LastCC1Arguments = CI.getCC1CommandLine();
470  }
471 
472 private:
473  StringRef WorkingDirectory;
474  DependencyConsumer &Consumer;
475  DependencyActionController &Controller;
477  ScanningOutputFormat Format;
478  ScanningOptimizations OptimizeArgs;
479  bool EagerLoadModules;
480  bool DisableFree;
481  std::optional<StringRef> ModuleName;
482  std::optional<CompilerInstance> ScanInstanceStorage;
483  std::shared_ptr<ModuleDepCollector> MDC;
484  std::vector<std::string> LastCC1Arguments;
485  bool Scanned = false;
486 };
487 
488 } // end anonymous namespace
489 
491  DependencyScanningService &Service,
493  : Format(Service.getFormat()), OptimizeArgs(Service.getOptimizeArgs()),
494  EagerLoadModules(Service.shouldEagerLoadModules()) {
495  PCHContainerOps = std::make_shared<PCHContainerOperations>();
496  // We need to read object files from PCH built outside the scanner.
497  PCHContainerOps->registerReader(
498  std::make_unique<ObjectFilePCHContainerReader>());
499  // The scanner itself writes only raw ast files.
500  PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
501 
502  switch (Service.getMode()) {
504  DepFS =
506  BaseFS = DepFS;
507  break;
509  DepFS = nullptr;
510  BaseFS = FS;
511  break;
512  }
513 }
514 
516  StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
517  DependencyConsumer &Consumer, DependencyActionController &Controller,
518  std::optional<StringRef> ModuleName) {
519  std::vector<const char *> CLI;
520  for (const std::string &Arg : CommandLine)
521  CLI.push_back(Arg.c_str());
522  auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
523  sanitizeDiagOpts(*DiagOpts);
524 
525  // Capture the emitted diagnostics and report them to the client
526  // in the case of a failure.
527  std::string DiagnosticOutput;
528  llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
529  TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
530 
531  if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
532  DiagPrinter, ModuleName))
533  return llvm::Error::success();
534  return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
535  llvm::inconvertibleErrorCode());
536 }
537 
538 static bool forEachDriverJob(
540  llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
542  Argv.reserve(ArgStrs.size());
543  for (const std::string &Arg : ArgStrs)
544  Argv.push_back(Arg.c_str());
545 
546  llvm::vfs::FileSystem *FS = &FM.getVirtualFileSystem();
547 
548  std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
549  Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
550  "clang LLVM compiler", FS);
551  Driver->setTitle("clang_based_tool");
552 
553  llvm::BumpPtrAllocator Alloc;
554  bool CLMode = driver::IsClangCL(
555  driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
556 
557  if (llvm::Error E = driver::expandResponseFiles(Argv, CLMode, Alloc, FS)) {
558  Diags.Report(diag::err_drv_expand_response_file)
559  << llvm::toString(std::move(E));
560  return false;
561  }
562 
563  const std::unique_ptr<driver::Compilation> Compilation(
564  Driver->BuildCompilation(llvm::ArrayRef(Argv)));
565  if (!Compilation)
566  return false;
567 
568  if (Compilation->containsError())
569  return false;
570 
571  for (const driver::Command &Job : Compilation->getJobs()) {
572  if (!Callback(Job))
573  return false;
574  }
575  return true;
576 }
577 
579  std::vector<std::string> CommandLine, DependencyScanningAction &Action,
580  FileManager &FM,
581  std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
582  DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {
583 
584  // Save executable path before providing CommandLine to ToolInvocation
585  std::string Executable = CommandLine[0];
586  ToolInvocation Invocation(std::move(CommandLine), &Action, &FM,
587  PCHContainerOps);
588  Invocation.setDiagnosticConsumer(Diags.getClient());
589  Invocation.setDiagnosticOptions(&Diags.getDiagnosticOptions());
590  if (!Invocation.run())
591  return false;
592 
593  std::vector<std::string> Args = Action.takeLastCC1Arguments();
594  Consumer.handleBuildCommand({std::move(Executable), std::move(Args)});
595  return true;
596 }
597 
599  StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
600  DependencyConsumer &Consumer, DependencyActionController &Controller,
601  DiagnosticConsumer &DC, std::optional<StringRef> ModuleName) {
602  // Reset what might have been modified in the previous worker invocation.
603  BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
604 
605  std::optional<std::vector<std::string>> ModifiedCommandLine;
607 
608  // If we're scanning based on a module name alone, we don't expect the client
609  // to provide us with an input file. However, the driver really wants to have
610  // one. Let's just make it up to make the driver happy.
611  if (ModuleName) {
612  auto OverlayFS =
613  llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
614  auto InMemoryFS =
615  llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
616  InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
617  OverlayFS->pushOverlay(InMemoryFS);
618  ModifiedFS = OverlayFS;
619 
620  SmallString<128> FakeInputPath;
621  // TODO: We should retry the creation if the path already exists.
622  llvm::sys::fs::createUniquePath(*ModuleName + "-%%%%%%%%.input",
623  FakeInputPath,
624  /*MakeAbsolute=*/false);
625  InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
626 
627  ModifiedCommandLine = CommandLine;
628  ModifiedCommandLine->emplace_back(FakeInputPath);
629  }
630 
631  const std::vector<std::string> &FinalCommandLine =
632  ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
633  auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
634 
635  auto FileMgr =
636  llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions{}, FinalFS);
637 
638  std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(), nullptr);
639  llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),
640  [](const std::string &Str) { return Str.c_str(); });
641 
642  auto DiagOpts = CreateAndPopulateDiagOpts(FinalCCommandLine);
643  sanitizeDiagOpts(*DiagOpts);
645  CompilerInstance::createDiagnostics(DiagOpts.release(), &DC,
646  /*ShouldOwnClient=*/false);
647 
648  // Although `Diagnostics` are used only for command-line parsing, the
649  // custom `DiagConsumer` might expect a `SourceManager` to be present.
650  SourceManager SrcMgr(*Diags, *FileMgr);
651  Diags->setSourceManager(&SrcMgr);
652  // DisableFree is modified by Tooling for running
653  // in-process; preserve the original value, which is
654  // always true for a driver invocation.
655  bool DisableFree = true;
656  DependencyScanningAction Action(WorkingDirectory, Consumer, Controller, DepFS,
657  Format, OptimizeArgs, EagerLoadModules,
658  DisableFree, ModuleName);
659 
660  bool Success = false;
661  if (FinalCommandLine[1] == "-cc1") {
662  Success = createAndRunToolInvocation(FinalCommandLine, Action, *FileMgr,
663  PCHContainerOps, *Diags, Consumer);
664  } else {
665  Success = forEachDriverJob(
666  FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
667  if (StringRef(Cmd.getCreator().getName()) != "clang") {
668  // Non-clang command. Just pass through to the dependency
669  // consumer.
670  Consumer.handleBuildCommand(
671  {Cmd.getExecutable(),
672  {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
673  return true;
674  }
675 
676  // Insert -cc1 comand line options into Argv
677  std::vector<std::string> Argv;
678  Argv.push_back(Cmd.getExecutable());
679  Argv.insert(Argv.end(), Cmd.getArguments().begin(),
680  Cmd.getArguments().end());
681 
682  // Create an invocation that uses the underlying file
683  // system to ensure that any file system requests that
684  // are made by the driver do not go through the
685  // dependency scanning filesystem.
686  return createAndRunToolInvocation(std::move(Argv), Action, *FileMgr,
687  PCHContainerOps, *Diags, Consumer);
688  });
689  }
690 
691  if (Success && !Action.hasScanned())
692  Diags->Report(diag::err_fe_expected_compiler_job)
693  << llvm::join(FinalCommandLine, " ");
694  return Success && Action.hasScanned();
695 }
696 
697 DependencyActionController::~DependencyActionController() {}
enum clang::sema::@1659::IndirectLocalPathEntry::EntryKind Kind
Expr * E
static bool forEachDriverJob(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, FileManager &FM, llvm::function_ref< bool(const driver::Command &Cmd)> Callback)
static bool createAndRunToolInvocation(std::vector< std::string > CommandLine, DependencyScanningAction &Action, FileManager &FM, std::shared_ptr< clang::PCHContainerOperations > &PCHContainerOps, DiagnosticsEngine &Diags, DependencyConsumer &Consumer)
StringRef Filename
Definition: Format.cpp:3001
CompileCommand Cmd
__SIZE_TYPE__ size_t
Abstract interface for callback invocations by the ASTReader.
Definition: ASTReader.h:114
@ ARR_OutOfDate
The client can handle an AST file that cannot load because it is out-of-date relative to its input fi...
Definition: ASTReader.h:1641
static bool readASTFileControlBlock(StringRef Filename, FileManager &FileMgr, const InMemoryModuleCache &ModuleCache, const PCHContainerReader &PCHContainerRdr, bool FindModuleFileExtensions, ASTReaderListener &Listener, bool ValidateDiagnosticOptions, unsigned ClientLoadCapabilities=ARR_ConfigurationMismatch|ARR_OutOfDate)
Read the control block for the named AST file.
Definition: ASTReader.cpp:5440
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
void createSourceManager(FileManager &FileMgr)
Create the source manager and replace any existing one with it.
CompilerInvocation & getInvocation()
void createDiagnostics(DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
FileManager * createFileManager(IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS=nullptr)
Create the file manager and replace any existing one with it.
FrontendOptions & getFrontendOpts()
void setInvocation(std::shared_ptr< CompilerInvocation > Value)
setInvocation - Replace the current invocation.
PreprocessorOptions & getPreprocessorOpts()
InMemoryModuleCache & getModuleCache() const
void addDependencyCollector(std::shared_ptr< DependencyCollector > Listener)
FileManager & getFileManager() const
Return the current file manager to the caller.
HeaderSearchOptions & getHeaderSearchOpts()
DiagnosticsEngine & getDiagnostics() const
Get the current diagnostics engine.
LangOptions & getLangOpts()
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.
DiagnosticOptions & getDiagnosticOpts()
const PCHContainerReader & getPCHContainerReader() const
Return the appropriate PCHContainerReader depending on the current CodeGenOptions.
Helper class for holding the data necessary to invoke the compiler.
DependencyOutputOptions & getDependencyOutputOpts()
Builds a dependency file when attached to a Preprocessor (for includes) and ASTReader (for module imp...
Definition: Utils.h:104
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Definition: Diagnostic.h:1751
virtual void finish()
Callback to inform the diagnostic client that processing of all source files has ended.
Definition: Diagnostic.h:1787
Options for controlling the compiler diagnostics engine.
std::vector< std::string > Warnings
The list of -W...
std::string DiagnosticSerializationFile
The file to serialize diagnostics to (non-appending).
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:193
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1553
bool hasErrorOccurred() const
Definition: Diagnostic.h:849
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
Definition: Diagnostic.h:568
DiagnosticConsumer * getClient()
Definition: Diagnostic.h:578
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
Definition: FileEntry.h:57
Implements support for file system lookup, file system caching, and directory search management.
Definition: FileManager.h:53
void AddStats(const FileManager &Other)
Import statistics from a child FileManager and add them to this current FileManager.
llvm::vfs::FileSystem & getVirtualFileSystem() const
Definition: FileManager.h:251
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > getVirtualFileSystemPtr() const
Definition: FileManager.h:253
Keeps track of options that affect how file operations are performed.
unsigned ModulesShareFileManager
Whether to share the FileManager when building modules.
std::string OutputFile
The output file, if any.
unsigned GenerateGlobalModuleIndex
Whether we can generate the global module index if needed.
SmallVector< FrontendInputFile, 0 > Inputs
The input files and their types.
unsigned UseGlobalModuleIndex
Whether we can use the global module index if available.
HeaderSearchOptions - Helper class for storing options related to the initialization of the HeaderSea...
unsigned ModulesStrictContextHash
Whether we should include all things that could impact the module in the hash.
std::map< std::string, std::string, std::less<> > PrebuiltModuleFiles
The mapping of module names to prebuilt module files.
unsigned ModulesSkipHeaderSearchPaths
Whether to entirely skip writing header search paths.
std::string ModuleFormat
The module/pch container format.
unsigned ModulesSkipDiagnosticOptions
Whether to entirely skip writing diagnostic options.
std::string ModuleCachePath
The directory used for the module cache.
std::vector< std::string > VFSOverlayFiles
The set of user-provided virtual filesystem overlay files.
unsigned ModulesSkipPragmaDiagnosticMappings
Whether to entirely skip writing pragma diagnostic mappings.
unsigned ModulesIncludeVFSUsage
Whether to include ivfsoverlay usage information in written AST files.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:480
PreprocessorOptions - This class is used for passing the various options used in preprocessor initial...
std::function< std::optional< ArrayRef< dependency_directives_scan::Directive > > FileEntryRef)> DependencyDirectivesForFile
Function for getting the dependency preprocessor directives of a file.
bool ModulesCheckRelocated
Perform extra checks when loading PCM files for mutable file systems.
std::string ImplicitPCHInclude
The implicit PCH included at the start of the translation unit, or empty.
bool AllowPCHWithDifferentModulesCachePath
When true, a PCH with modules cache path different to the current compilation will not be rejected.
std::vector< std::pair< std::string, bool > > Macros
This class handles loading and caching of source files into memory.
The base class of the type hierarchy.
Definition: Type.h:1829
Command - An executable path/name and argument vector to execute.
Definition: Job.h:107
Interface to process a clang::CompilerInvocation.
Definition: Tooling.h:80
Utility to run a FrontendAction in a single clang invocation.
Definition: Tooling.h:239
void setDiagnosticConsumer(DiagnosticConsumer *DiagConsumer)
Set a DiagnosticConsumer to use during driver command-line parsing and the action invocation itself.
Definition: Tooling.h:275
void setDiagnosticOptions(DiagnosticOptions *DiagOpts)
Set a DiagnosticOptions to use during driver command-line parsing.
Definition: Tooling.h:280
bool run()
Run the clang invocation.
Definition: Tooling.cpp:372
Dependency scanner callbacks that are used during scanning to influence the behaviour of the scan - f...
The dependency scanning service contains shared configuration and state that is used by the individua...
DependencyScanningFilesystemSharedCache & getSharedCache()
A virtual file system optimized for the dependency discovery.
bool computeDependencies(StringRef WorkingDirectory, const std::vector< std::string > &CommandLine, DependencyConsumer &DepConsumer, DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, std::optional< StringRef > ModuleName=std::nullopt)
Run the dependency scanning tool for a given clang driver command-line, and report the discovered dep...
DependencyScanningWorker(DependencyScanningService &Service, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS)
llvm::StringRef getDriverMode(StringRef ProgName, ArrayRef< const char * > Args)
Returns the driver mode option's value, i.e.
Definition: Driver.cpp:10419
llvm::Error expandResponseFiles(SmallVectorImpl< const char * > &Args, bool ClangCLMode, llvm::BumpPtrAllocator &Alloc, llvm::vfs::FileSystem *FS=nullptr)
Expand response files from a clang driver or cc1 invocation.
Definition: Driver.cpp:10436
bool IsClangCL(StringRef DriverMode)
Checks whether the value produced by getDriverMode is for CL mode.
Definition: Driver.cpp:10434
bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc)
Definition: Interp.h:2934
ModuleKind
Specifies the kind of module that has been loaded.
Definition: ModuleFile.h:42
@ MK_ExplicitModule
File is an explicitly-loaded module.
Definition: ModuleFile.h:47
std::string toString(const til::SExpr *E)
ScanningOutputFormat
The format that is output by the dependency scanner.
@ Make
This is the Makefile compatible dep format.
@ Full
This outputs the full clang module dependency graph suitable for use for explicitly building modules.
@ P1689
This outputs the dependency graph for standard c++ modules in P1689R5 format.
@ DependencyDirectivesScan
This mode is used to compute the dependencies by running the preprocessor with special kind of lexing...
@ CanonicalPreprocessing
This mode is used to compute the dependencies by running the preprocessor over the source files.
@ VFS
Remove unused -ivfsoverlay arguments.
llvm::StringMap< llvm::StringSet<> > PrebuiltModuleVFSMapT
The JSON file list parser is used to communicate input to InstallAPI.
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
IntrusiveRefCntPtr< llvm::vfs::FileSystem > createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags)
for(const auto &A :T->param_types())
bool any(half)