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(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
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";
344 any(OptimizeArgs & ScanningOptimizations::VFS);
345
346 // Support for virtual file system overlays.
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 =
355
356 DepFS->resetBypassedPathPrefix();
357 if (!ModulesCachePath.empty())
358 DepFS->setBypassedPathPrefix(ModulesCachePath);
359
361 [LocalDepFS = DepFS](FileEntryRef File)
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(
382 ScanInstance,
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;
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
471private:
472 void setLastCC1Arguments(CompilerInvocation &&CI) {
473 if (MDC)
474 MDC->applyDiscoveredDependencies(CI);
475 LastCC1Arguments = CI.getCC1CommandLine();
476 }
477
478private:
479 StringRef WorkingDirectory;
480 DependencyConsumer &Consumer;
481 DependencyActionController &Controller;
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
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()) {
513 DepFS =
515 BaseFS = DepFS;
516 break;
518 DepFS = nullptr;
519 BaseFS = FS;
520 break;
521 }
522}
523
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
549 llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
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
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
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;
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);
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 {
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
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:3032
CompileCommand Cmd
Abstract interface for callback invocations by the ASTReader.
Definition: ASTReader.h:116
virtual bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts, bool Complain)
Receives the header search paths.
Definition: ASTReader.h:193
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:246
virtual void visitModuleFile(StringRef Filename, serialization::ModuleKind Kind)
This is called for each AST file loaded.
Definition: ASTReader.h:218
virtual bool needsImportVisitation() const
Returns true if this ASTReaderListener wants to receive the imports of the AST file via visitImport,...
Definition: ASTReader.h:242
@ 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:1736
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:5544
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.
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 createDiagnostics(llvm::vfs::FileSystem &VFS, DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
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:1684
virtual void finish()
Callback to inform the diagnostic client that processing of all source files has ended.
Definition: Diagnostic.h:1720
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:231
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1493
bool hasErrorOccurred() const
Definition: Diagnostic.h:866
DiagnosticOptions & getDiagnosticOptions() const
Retrieve the diagnostic options.
Definition: Diagnostic.h:585
DiagnosticConsumer * getClient()
Definition: Diagnostic.h:595
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:256
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > getVirtualFileSystemPtr() const
Definition: FileManager.h:258
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 ModulesSerializeOnlyPreprocessor
Whether AST files should only contain the preprocessor information.
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:499
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:1828
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:6809
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:6826
bool IsClangCL(StringRef DriverMode)
Checks whether the value produced by getDriverMode is for CL mode.
Definition: Driver.cpp:6824
ModuleKind
Specifies the kind of module that has been loaded.
Definition: ModuleFile.h:43
@ MK_ExplicitModule
File is an explicitly-loaded module.
Definition: ModuleFile.h:48
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.