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"
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
33using namespace clang;
34using namespace tooling;
35using namespace dependencies;
36
37namespace {
38
39/// Forwards the gatherered dependencies to the consumer.
40class DependencyConsumerForwarder : public DependencyFileGenerator {
41public:
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
58private:
59 StringRef WorkingDirectory;
60 std::unique_ptr<DependencyOutputOptions> Opts;
62};
63
64static 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
88using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
89
90/// A listener that collects the imported modules and optionally the input
91/// files.
92class PrebuiltModuleListener : public ASTReaderListener {
93public:
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
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
125private:
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.
137static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
139 PrebuiltModuleFilesT &ModuleFiles,
140 PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
141 DiagnosticsEngine &Diags) {
142 // List of module files to be processed.
144 PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModuleVFSMap,
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.
170static 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.
177static std::string
178deduceDepTarget(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.
190static 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.
220static 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
248static 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.
282class DependencyScanningAction : public tooling::ToolAction {
283public:
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 =
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) {
402 case ScanningOutputFormat::Make:
403 ScanInstance.addDependencyCollector(
404 std::make_shared<DependencyConsumerForwarder>(
405 std::move(Opts), WorkingDirectory, Consumer));
406 break;
407 case ScanningOutputFormat::P1689:
408 case ScanningOutputFormat::Full:
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
465private:
466 void setLastCC1Arguments(CompilerInvocation &&CI) {
467 if (MDC)
468 MDC->applyDiscoveredDependencies(CI);
469 LastCC1Arguments = CI.getCC1CommandLine();
470 }
471
472private:
473 StringRef WorkingDirectory;
474 DependencyConsumer &Consumer;
475 DependencyActionController &Controller;
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
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
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 {
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
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:3014
CompileCommand Cmd
Abstract interface for callback invocations by the ASTReader.
Definition: ASTReader.h:114
virtual bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts, bool Complain)
Receives the header search paths.
Definition: ASTReader.h:191
virtual void visitImport(StringRef ModuleName, StringRef Filename)
If needsImportVisitation returns true, this is called for each AST file imported by this AST file.
Definition: ASTReader.h:244
virtual void visitModuleFile(StringRef Filename, serialization::ModuleKind Kind)
This is called for each AST file loaded.
Definition: ASTReader.h:216
virtual bool needsImportVisitation() const
Returns true if this ASTReaderListener wants to receive the imports of the AST file via visitImport,...
Definition: ASTReader.h:240
@ 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:1647
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.
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.
const PCHContainerReader & getPCHContainerReader() const
Return the appropriate PCHContainerReader depending on the current CodeGenOptions.
DiagnosticsEngine & getDiagnostics() const
Get the current diagnostics engine.
void setInvocation(std::shared_ptr< CompilerInvocation > Value)
setInvocation - Replace the current invocation.
FileManager & getFileManager() const
Return the current file manager to the caller.
InMemoryModuleCache & getModuleCache() const
void addDependencyCollector(std::shared_ptr< DependencyCollector > Listener)
FrontendOptions & getFrontendOpts()
HeaderSearchOptions & getHeaderSearchOpts()
CompilerInvocation & getInvocation()
PreprocessorOptions & getPreprocessorOpts()
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.
DiagnosticOptions & getDiagnosticOpts()
LangOptions & getLangOpts()
Helper class for holding the data necessary to invoke the compiler.
DependencyOutputOptions & getDependencyOutputOpts()
ArrayRef< std::string > getDependencies() const
Definition: Utils.h:69
Builds a dependency file when attached to a Preprocessor (for includes) and ASTReader (for module imp...
Definition: Utils.h:104
void finishedMainFile(DiagnosticsEngine &Diags) override
Called when the end of the main file is reached.
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Definition: Diagnostic.h:1745
virtual void finish()
Callback to inform the diagnostic client that processing of all source files has ended.
Definition: Diagnostic.h:1781
Options for controlling the compiler diagnostics engine.
std::vector< std::string > Warnings
The list of -W... options used to alter the diagnostic mappings, with the prefixes removed.
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:192
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1547
bool hasErrorOccurred() const
Definition: Diagnostic.h:843
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
Definition: Diagnostic.h:562
DiagnosticConsumer * getClient()
Definition: Diagnostic.h:572
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:478
PreprocessorOptions - This class is used for passing the various options used in preprocessor initial...
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
std::function< std::optional< ArrayRef< dependency_directives_scan::Directive > >(FileEntryRef)> DependencyDirectivesForFile
Function for getting the dependency preprocessor directives of a file.
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:106
Interface to process a clang::CompilerInvocation.
Definition: Tooling.h:80
virtual bool runInvocation(std::shared_ptr< CompilerInvocation > Invocation, FileManager *Files, std::shared_ptr< PCHContainerOperations > PCHContainerOps, DiagnosticConsumer *DiagConsumer)=0
Perform an action for an invocation.
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:6722
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:6739
bool IsClangCL(StringRef DriverMode)
Checks whether the value produced by getDriverMode is for CL mode.
Definition: Driver.cpp:6737
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
ScanningOutputFormat
The format that is output by the dependency scanner.
@ 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.
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)
@ Result
The result type of a method or function.
IntrusiveRefCntPtr< llvm::vfs::FileSystem > createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags)
@ Success
Template argument deduction was successful.
int __ovld __cnfn any(char)
Returns 1 if the most significant bit in any component of x is set; otherwise returns 0.