clang 22.0.0git
DependencyScannerImpl.cpp
Go to the documentation of this file.
1//===- DependencyScanner.cpp - Performs module dependency scanning --------===//
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
12#include "clang/Driver/Driver.h"
15#include "llvm/TargetParser/Host.h"
16
17using namespace clang;
18using namespace tooling;
19using namespace dependencies;
20
21namespace {
22/// Forwards the gatherered dependencies to the consumer.
23class DependencyConsumerForwarder : public DependencyFileGenerator {
24public:
25 DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
26 StringRef WorkingDirectory, DependencyConsumer &C)
27 : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory),
28 Opts(std::move(Opts)), C(C) {}
29
30 void finishedMainFile(DiagnosticsEngine &Diags) override {
31 C.handleDependencyOutputOpts(*Opts);
32 llvm::SmallString<256> CanonPath;
33 for (const auto &File : getDependencies()) {
34 CanonPath = File;
35 llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
36 llvm::sys::path::make_absolute(WorkingDirectory, CanonPath);
37 C.handleFileDependency(CanonPath);
38 }
39 }
40
41private:
42 StringRef WorkingDirectory;
43 std::unique_ptr<DependencyOutputOptions> Opts;
44 DependencyConsumer &C;
45};
46
47static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
48 const HeaderSearchOptions &ExistingHSOpts,
49 DiagnosticsEngine *Diags,
50 const LangOptions &LangOpts) {
51 if (LangOpts.Modules) {
52 if (HSOpts.VFSOverlayFiles != ExistingHSOpts.VFSOverlayFiles) {
53 if (Diags) {
54 Diags->Report(diag::warn_pch_vfsoverlay_mismatch);
55 auto VFSNote = [&](int Type, ArrayRef<std::string> VFSOverlays) {
56 if (VFSOverlays.empty()) {
57 Diags->Report(diag::note_pch_vfsoverlay_empty) << Type;
58 } else {
59 std::string Files = llvm::join(VFSOverlays, "\n");
60 Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files;
61 }
62 };
63 VFSNote(0, HSOpts.VFSOverlayFiles);
64 VFSNote(1, ExistingHSOpts.VFSOverlayFiles);
65 }
66 }
67 }
68 return false;
69}
70
71using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
72
73/// A listener that collects the imported modules and the input
74/// files. While visiting, collect vfsoverlays and file inputs that determine
75/// whether prebuilt modules fully resolve in stable directories.
76class PrebuiltModuleListener : public ASTReaderListener {
77public:
78 PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
79 llvm::SmallVector<std::string> &NewModuleFiles,
80 PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
81 const HeaderSearchOptions &HSOpts,
82 const LangOptions &LangOpts, DiagnosticsEngine &Diags,
83 const ArrayRef<StringRef> StableDirs)
84 : PrebuiltModuleFiles(PrebuiltModuleFiles),
85 NewModuleFiles(NewModuleFiles),
86 PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts),
87 ExistingLangOpts(LangOpts), Diags(Diags), StableDirs(StableDirs) {}
88
89 bool needsImportVisitation() const override { return true; }
90 bool needsInputFileVisitation() override { return true; }
91 bool needsSystemInputFileVisitation() override { return true; }
92
93 /// Accumulate the modules are transitively depended on by the initial
94 /// prebuilt module.
95 void visitImport(StringRef ModuleName, StringRef Filename) override {
96 if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
97 NewModuleFiles.push_back(Filename.str());
98
99 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(Filename);
100 PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
101 if (PrebuiltMapEntry.second)
102 PrebuiltModule.setInStableDir(!StableDirs.empty());
103
104 if (auto It = PrebuiltModulesASTMap.find(CurrentFile);
105 It != PrebuiltModulesASTMap.end() && CurrentFile != Filename)
106 PrebuiltModule.addDependent(It->getKey());
107 }
108
109 /// For each input file discovered, check whether it's external path is in a
110 /// stable directory. Traversal is stopped if the current module is not
111 /// considered stable.
112 bool visitInputFileAsRequested(StringRef FilenameAsRequested,
113 StringRef Filename, bool isSystem,
114 bool isOverridden,
115 bool isExplicitModule) override {
116 if (StableDirs.empty())
117 return false;
118 auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
119 if ((PrebuiltEntryIt == PrebuiltModulesASTMap.end()) ||
120 (!PrebuiltEntryIt->second.isInStableDir()))
121 return false;
122
123 PrebuiltEntryIt->second.setInStableDir(
124 isPathInStableDir(StableDirs, Filename));
125 return PrebuiltEntryIt->second.isInStableDir();
126 }
127
128 /// Update which module that is being actively traversed.
129 void visitModuleFile(StringRef Filename,
130 serialization::ModuleKind Kind) override {
131 // If the CurrentFile is not
132 // considered stable, update any of it's transitive dependents.
133 auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
134 if ((PrebuiltEntryIt != PrebuiltModulesASTMap.end()) &&
135 !PrebuiltEntryIt->second.isInStableDir())
136 PrebuiltEntryIt->second.updateDependentsNotInStableDirs(
137 PrebuiltModulesASTMap);
138 CurrentFile = Filename;
139 }
140
141 /// Check the header search options for a given module when considering
142 /// if the module comes from stable directories.
143 bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
144 StringRef ModuleFilename,
145 StringRef SpecificModuleCachePath,
146 bool Complain) override {
147
148 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
149 PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
150 if (PrebuiltMapEntry.second)
151 PrebuiltModule.setInStableDir(!StableDirs.empty());
152
153 if (PrebuiltModule.isInStableDir())
154 PrebuiltModule.setInStableDir(areOptionsInStableDir(StableDirs, HSOpts));
155
156 return false;
157 }
158
159 /// Accumulate vfsoverlays used to build these prebuilt modules.
160 bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
161 bool Complain) override {
162
163 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
164 PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
165 if (PrebuiltMapEntry.second)
166 PrebuiltModule.setInStableDir(!StableDirs.empty());
167
168 PrebuiltModule.setVFS(
169 llvm::StringSet<>(llvm::from_range, HSOpts.VFSOverlayFiles));
170
171 return checkHeaderSearchPaths(
172 HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);
173 }
174
175private:
176 PrebuiltModuleFilesT &PrebuiltModuleFiles;
177 llvm::SmallVector<std::string> &NewModuleFiles;
178 PrebuiltModulesAttrsMap &PrebuiltModulesASTMap;
179 const HeaderSearchOptions &ExistingHSOpts;
180 const LangOptions &ExistingLangOpts;
181 DiagnosticsEngine &Diags;
182 std::string CurrentFile;
183 const ArrayRef<StringRef> StableDirs;
184};
185
186/// Visit the given prebuilt module and collect all of the modules it
187/// transitively imports and contributing input files.
188static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
190 PrebuiltModuleFilesT &ModuleFiles,
191 PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
192 DiagnosticsEngine &Diags,
193 const ArrayRef<StringRef> StableDirs) {
194 // List of module files to be processed.
196
197 PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModulesASTMap,
199 Diags, StableDirs);
200
201 Listener.visitModuleFile(PrebuiltModuleFilename,
204 PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(),
206 /*FindModuleFileExtensions=*/false, Listener,
207 /*ValidateDiagnosticOptions=*/false, ASTReader::ARR_OutOfDate))
208 return true;
209
210 while (!Worklist.empty()) {
211 Listener.visitModuleFile(Worklist.back(), serialization::MK_ExplicitModule);
213 Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
215 /*FindModuleFileExtensions=*/false, Listener,
216 /*ValidateDiagnosticOptions=*/false))
217 return true;
218 }
219 return false;
220}
221
222/// Transform arbitrary file name into an object-like file name.
223static std::string makeObjFileName(StringRef FileName) {
224 SmallString<128> ObjFileName(FileName);
225 llvm::sys::path::replace_extension(ObjFileName, "o");
226 return std::string(ObjFileName);
227}
228
229/// Deduce the dependency target based on the output file and input files.
230static std::string
231deduceDepTarget(const std::string &OutputFile,
232 const SmallVectorImpl<FrontendInputFile> &InputFiles) {
233 if (OutputFile != "-")
234 return OutputFile;
235
236 if (InputFiles.empty() || !InputFiles.front().isFile())
237 return "clang-scan-deps\\ dependency";
238
239 return makeObjFileName(InputFiles.front().getFile());
240}
241
242// Clang implements -D and -U by splatting text into a predefines buffer. This
243// allows constructs such as `-DFඞ=3 "-D F\u{0D9E} 4 3 2”` to be accepted and
244// define the same macro, or adding C++ style comments before the macro name.
245//
246// This function checks that the first non-space characters in the macro
247// obviously form an identifier that can be uniqued on without lexing. Failing
248// to do this could lead to changing the final definition of a macro.
249//
250// We could set up a preprocessor and actually lex the name, but that's very
251// heavyweight for a situation that will almost never happen in practice.
252static std::optional<StringRef> getSimpleMacroName(StringRef Macro) {
253 StringRef Name = Macro.split("=").first.ltrim(" \t");
254 std::size_t I = 0;
255
256 auto FinishName = [&]() -> std::optional<StringRef> {
257 StringRef SimpleName = Name.slice(0, I);
258 if (SimpleName.empty())
259 return std::nullopt;
260 return SimpleName;
261 };
262
263 for (; I != Name.size(); ++I) {
264 switch (Name[I]) {
265 case '(': // Start of macro parameter list
266 case ' ': // End of macro name
267 case '\t':
268 return FinishName();
269 case '_':
270 continue;
271 default:
272 if (llvm::isAlnum(Name[I]))
273 continue;
274 return std::nullopt;
275 }
276 }
277 return FinishName();
278}
279
280static void canonicalizeDefines(PreprocessorOptions &PPOpts) {
281 using MacroOpt = std::pair<StringRef, std::size_t>;
282 std::vector<MacroOpt> SimpleNames;
283 SimpleNames.reserve(PPOpts.Macros.size());
284 std::size_t Index = 0;
285 for (const auto &M : PPOpts.Macros) {
286 auto SName = getSimpleMacroName(M.first);
287 // Skip optimizing if we can't guarantee we can preserve relative order.
288 if (!SName)
289 return;
290 SimpleNames.emplace_back(*SName, Index);
291 ++Index;
292 }
293
294 llvm::stable_sort(SimpleNames, llvm::less_first());
295 // Keep the last instance of each macro name by going in reverse
296 auto NewEnd = std::unique(
297 SimpleNames.rbegin(), SimpleNames.rend(),
298 [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; });
299 SimpleNames.erase(SimpleNames.begin(), NewEnd.base());
300
301 // Apply permutation.
302 decltype(PPOpts.Macros) NewMacros;
303 NewMacros.reserve(SimpleNames.size());
304 for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) {
305 std::size_t OriginalIndex = SimpleNames[I].second;
306 // We still emit undefines here as they may be undefining a predefined macro
307 NewMacros.push_back(std::move(PPOpts.Macros[OriginalIndex]));
308 }
309 std::swap(PPOpts.Macros, NewMacros);
310}
311
312class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter {
313 DependencyScanningWorkerFilesystem *DepFS;
314
315public:
316 ScanningDependencyDirectivesGetter(FileManager &FileMgr) : DepFS(nullptr) {
317 FileMgr.getVirtualFileSystem().visit([&](llvm::vfs::FileSystem &FS) {
318 auto *DFS = llvm::dyn_cast<DependencyScanningWorkerFilesystem>(&FS);
319 if (DFS) {
320 assert(!DepFS && "Found multiple scanning VFSs");
321 DepFS = DFS;
322 }
323 });
324 assert(DepFS && "Did not find scanning VFS");
325 }
326
327 std::unique_ptr<DependencyDirectivesGetter>
328 cloneFor(FileManager &FileMgr) override {
329 return std::make_unique<ScanningDependencyDirectivesGetter>(FileMgr);
330 }
331
332 std::optional<ArrayRef<dependency_directives_scan::Directive>>
333 operator()(FileEntryRef File) override {
334 return DepFS->getDirectiveTokens(File.getName());
335 }
336};
337
338/// Sanitize diagnostic options for dependency scan.
339void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
340 // Don't print 'X warnings and Y errors generated'.
341 DiagOpts.ShowCarets = false;
342 // Don't write out diagnostic file.
343 DiagOpts.DiagnosticSerializationFile.clear();
344 // Don't emit warnings except for scanning specific warnings.
345 // TODO: It would be useful to add a more principled way to ignore all
346 // warnings that come from source code. The issue is that we need to
347 // ignore warnings that could be surpressed by
348 // `#pragma clang diagnostic`, while still allowing some scanning
349 // warnings for things we're not ready to turn into errors yet.
350 // See `test/ClangScanDeps/diagnostic-pragmas.c` for an example.
351 llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) {
352 return llvm::StringSwitch<bool>(Warning)
353 .Cases("pch-vfs-diff", "error=pch-vfs-diff", false)
354 .StartsWith("no-error=", false)
355 .Default(true);
356 });
357}
358} // namespace
359
361std::unique_ptr<DiagnosticOptions>
363 std::vector<const char *> CLI;
364 for (const std::string &Arg : CommandLine)
365 CLI.push_back(Arg.c_str());
366 auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
367 sanitizeDiagOpts(*DiagOpts);
368 return DiagOpts;
369}
370
372 ArrayRef<std::string> CommandLine,
374 std::vector<const char *> CCommandLine(CommandLine.size(), nullptr);
375 llvm::transform(CommandLine, CCommandLine.begin(),
376 [](const std::string &Str) { return Str.c_str(); });
377 DiagOpts = CreateAndPopulateDiagOpts(CCommandLine);
378 sanitizeDiagOpts(*DiagOpts);
380 /*ShouldOwnClient=*/false);
381}
382
383std::pair<std::unique_ptr<driver::Driver>, std::unique_ptr<driver::Compilation>>
387 Argv.reserve(ArgStrs.size());
388 for (const std::string &Arg : ArgStrs)
389 Argv.push_back(Arg.c_str());
390
391 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
392 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
393 "clang LLVM compiler", FS);
394 Driver->setTitle("clang_based_tool");
395
396 llvm::BumpPtrAllocator Alloc;
397 bool CLMode = driver::IsClangCL(
398 driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
399
400 if (llvm::Error E =
401 driver::expandResponseFiles(Argv, CLMode, Alloc, FS.get())) {
402 Diags.Report(diag::err_drv_expand_response_file)
403 << llvm::toString(std::move(E));
404 return std::make_pair(nullptr, nullptr);
405 }
406
407 std::unique_ptr<driver::Compilation> Compilation(
408 Driver->BuildCompilation(Argv));
409 if (!Compilation)
410 return std::make_pair(nullptr, nullptr);
411
412 if (Compilation->containsError())
413 return std::make_pair(nullptr, nullptr);
414
415 return std::make_pair(std::move(Driver), std::move(Compilation));
416}
417
418std::unique_ptr<CompilerInvocation>
420 DiagnosticsEngine &Diags) {
421 llvm::opt::ArgStringList Argv;
422 for (const std::string &Str : ArrayRef(CommandLine).drop_front())
423 Argv.push_back(Str.c_str());
424
425 auto Invocation = std::make_unique<CompilerInvocation>();
426 if (!CompilerInvocation::CreateFromArgs(*Invocation, Argv, Diags)) {
427 // FIXME: Should we just go on like cc1_main does?
428 return nullptr;
429 }
430 return Invocation;
431}
432
433std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>>
435 ArrayRef<std::string> CommandLine,
436 StringRef WorkingDirectory,
437 llvm::MemoryBufferRef TUBuffer) {
438 // Reset what might have been modified in the previous worker invocation.
439 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
440
442 auto OverlayFS =
443 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
444 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
445 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
446 auto InputPath = TUBuffer.getBufferIdentifier();
447 InMemoryFS->addFile(
448 InputPath, 0, llvm::MemoryBuffer::getMemBufferCopy(TUBuffer.getBuffer()));
449 IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
450
451 OverlayFS->pushOverlay(InMemoryOverlay);
452 ModifiedFS = OverlayFS;
453 std::vector<std::string> ModifiedCommandLine(CommandLine);
454 ModifiedCommandLine.emplace_back(InputPath);
455
456 return std::make_pair(ModifiedFS, ModifiedCommandLine);
457}
458
459std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>>
461 ArrayRef<std::string> CommandLine,
462 StringRef WorkingDirectory, StringRef ModuleName) {
463 // Reset what might have been modified in the previous worker invocation.
464 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
465
466 // If we're scanning based on a module name alone, we don't expect the client
467 // to provide us with an input file. However, the driver really wants to have
468 // one. Let's just make it up to make the driver happy.
469 auto OverlayFS =
470 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
471 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
472 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
473 SmallString<128> FakeInputPath;
474 // TODO: We should retry the creation if the path already exists.
475 llvm::sys::fs::createUniquePath(ModuleName + "-%%%%%%%%.input", FakeInputPath,
476 /*MakeAbsolute=*/false);
477 InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
478 IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
479 OverlayFS->pushOverlay(InMemoryOverlay);
480
481 std::vector<std::string> ModifiedCommandLine(CommandLine);
482 ModifiedCommandLine.emplace_back(FakeInputPath);
483
484 return std::make_pair(OverlayFS, ModifiedCommandLine);
485}
486
488 CompilerInstance &ScanInstance,
490 DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service,
492 ScanInstance.setBuildingModule(false);
493
494 ScanInstance.createVirtualFileSystem(FS, DiagConsumer);
495
496 // Create the compiler's actual diagnostics engine.
497 sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
498 ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
499 if (!ScanInstance.hasDiagnostics())
500 return false;
501
503 true;
504
507 Service.getBuildSessionTimestamp();
508
509 ScanInstance.getFrontendOpts().DisableFree = false;
510 ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
511 ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
512 ScanInstance.getFrontendOpts().GenReducedBMI = false;
513 ScanInstance.getFrontendOpts().ModuleOutputPath.clear();
514 // This will prevent us compiling individual modules asynchronously since
515 // FileManager is not thread-safe, but it does improve performance for now.
516 ScanInstance.getFrontendOpts().ModulesShareFileManager = true;
517 ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
520
521 // Create a new FileManager to match the invocation's FileSystemOptions.
522 ScanInstance.createFileManager();
523
524 // Use the dependency scanning optimized file system if requested to do so.
525 if (DepFS) {
526 DepFS->resetBypassedPathPrefix();
527 if (!ScanInstance.getHeaderSearchOpts().ModuleCachePath.empty()) {
528 SmallString<256> ModulesCachePath;
530 ScanInstance.getFileManager(),
531 ScanInstance.getHeaderSearchOpts().ModuleCachePath, ModulesCachePath);
532 DepFS->setBypassedPathPrefix(ModulesCachePath);
533 }
534
536 std::make_unique<ScanningDependencyDirectivesGetter>(
537 ScanInstance.getFileManager()));
538 }
539
540 ScanInstance.createSourceManager();
541
542 // Consider different header search and diagnostic options to create
543 // different modules. This avoids the unsound aliasing of module PCMs.
544 //
545 // TODO: Implement diagnostic bucketing to reduce the impact of strict
546 // context hashing.
547 ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
553
554 // Avoid some checks and module map parsing when loading PCM files.
555 ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
556
557 return true;
558}
559
562 // Create a collection of stable directories derived from the ScanInstance
563 // for determining whether module dependencies would fully resolve from
564 // those directories.
566 const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot;
567 if (!Sysroot.empty() && (llvm::sys::path::root_directory(Sysroot) != Sysroot))
568 StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts().ResourceDir};
569 return StableDirs;
570}
571
572std::optional<PrebuiltModulesAttrsMap>
574 llvm::SmallVector<StringRef> &StableDirs) {
575 // Store a mapping of prebuilt module files and their properties like header
576 // search options. This will prevent the implicit build to create duplicate
577 // modules and will force reuse of the existing prebuilt module files
578 // instead.
579 PrebuiltModulesAttrsMap PrebuiltModulesASTMap;
580
581 if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
582 if (visitPrebuiltModule(
583 ScanInstance.getPreprocessorOpts().ImplicitPCHInclude, ScanInstance,
585 PrebuiltModulesASTMap, ScanInstance.getDiagnostics(), StableDirs))
586 return {};
587
588 return PrebuiltModulesASTMap;
589}
590
591std::unique_ptr<DependencyOutputOptions>
593 // This function moves the existing dependency output options from the
594 // invocation to the collector. The options in the invocation are reset,
595 // which ensures that the compiler won't create new dependency collectors,
596 // and thus won't write out the extra '.d' files to disk.
597 auto Opts = std::make_unique<DependencyOutputOptions>();
598 std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());
599 // We need at least one -MT equivalent for the generator of make dependency
600 // files to work.
601 if (Opts->Targets.empty())
602 Opts->Targets = {deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile,
603 ScanInstance.getFrontendOpts().Inputs)};
604 Opts->IncludeSystemHeaders = true;
605
606 return Opts;
607}
608
609std::shared_ptr<ModuleDepCollector> initializeScanInstanceDependencyCollector(
610 CompilerInstance &ScanInstance,
611 std::unique_ptr<DependencyOutputOptions> DepOutputOpts,
612 StringRef WorkingDirectory, DependencyConsumer &Consumer,
614 DependencyActionController &Controller,
615 PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
616 llvm::SmallVector<StringRef> &StableDirs) {
617 std::shared_ptr<ModuleDepCollector> MDC;
618 switch (Service.getFormat()) {
620 ScanInstance.addDependencyCollector(
621 std::make_shared<DependencyConsumerForwarder>(
622 std::move(DepOutputOpts), WorkingDirectory, Consumer));
623 break;
626 MDC = std::make_shared<ModuleDepCollector>(
627 Service, std::move(DepOutputOpts), ScanInstance, Consumer, Controller,
628 Inv, std::move(PrebuiltModulesASTMap), StableDirs);
629 ScanInstance.addDependencyCollector(MDC);
630 break;
631 }
632
633 return MDC;
634}
635} // namespace clang::tooling::dependencies
636
638 std::unique_ptr<CompilerInvocation> Invocation,
640 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
641 DiagnosticConsumer *DiagConsumer) {
642 // Making sure that we canonicalize the defines before we create the deep
643 // copy to avoid unnecessary variants in the scanner and in the resulting
644 // explicit command lines.
645 if (any(Service.getOptimizeArgs() & ScanningOptimizations::Macros))
646 canonicalizeDefines(Invocation->getPreprocessorOpts());
647
648 // Make a deep copy of the original Clang invocation.
649 CompilerInvocation OriginalInvocation(*Invocation);
650
651 if (Scanned) {
652 // Scanning runs once for the first -cc1 invocation in a chain of driver
653 // jobs. For any dependent jobs, reuse the scanning result and just
654 // update the LastCC1Arguments to correspond to the new invocation.
655 // FIXME: to support multi-arch builds, each arch requires a separate scan
656 setLastCC1Arguments(std::move(OriginalInvocation));
657 return true;
658 }
659
660 Scanned = true;
661
662 // Create a compiler instance to handle the actual work.
663 auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries());
664 ScanInstanceStorage.emplace(std::move(Invocation), std::move(PCHContainerOps),
665 ModCache.get());
666 CompilerInstance &ScanInstance = *ScanInstanceStorage;
667
668 assert(!DiagConsumerFinished && "attempt to reuse finished consumer");
669 if (!initializeScanCompilerInstance(ScanInstance, FS, DiagConsumer, Service,
670 DepFS))
671 return false;
672
673 llvm::SmallVector<StringRef> StableDirs = getInitialStableDirs(ScanInstance);
674 auto MaybePrebuiltModulesASTMap =
675 computePrebuiltModulesASTMap(ScanInstance, StableDirs);
676 if (!MaybePrebuiltModulesASTMap)
677 return false;
678
679 auto DepOutputOpts = takeDependencyOutputOptionsFrom(ScanInstance);
680
682 ScanInstance, std::move(DepOutputOpts), WorkingDirectory, Consumer,
683 Service, OriginalInvocation, Controller, *MaybePrebuiltModulesASTMap,
684 StableDirs);
685
686 std::unique_ptr<FrontendAction> Action;
687
688 if (Service.getFormat() == ScanningOutputFormat::P1689)
689 Action = std::make_unique<PreprocessOnlyAction>();
690 else if (ModuleName)
691 Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
692 else
693 Action = std::make_unique<ReadPCHAndPreprocessAction>();
694
695 if (ScanInstance.getDiagnostics().hasErrorOccurred())
696 return false;
697
698 const bool Result = ScanInstance.ExecuteAction(*Action);
699
700 // ExecuteAction is responsible for calling finish.
701 DiagConsumerFinished = true;
702
703 if (Result)
704 setLastCC1Arguments(std::move(OriginalInvocation));
705
706 return Result;
707}
Abstract interface for callback invocations by the ASTReader.
Definition ASTReader.h:117
@ 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:1837
static bool readASTFileControlBlock(StringRef Filename, FileManager &FileMgr, const ModuleCache &ModCache, const PCHContainerReader &PCHContainerRdr, bool FindModuleFileExtensions, ASTReaderListener &Listener, bool ValidateDiagnosticOptions, unsigned ClientLoadCapabilities=ARR_ConfigurationMismatch|ARR_OutOfDate)
Read the control block for the named AST file.
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
void createDiagnostics(DiagnosticConsumer *Client=nullptr, bool ShouldOwnClient=true)
Create the diagnostics engine using the invocation's diagnostic options and replace any existing one ...
const PCHContainerReader & getPCHContainerReader() const
Return the appropriate PCHContainerReader depending on the current CodeGenOptions.
DiagnosticsEngine & getDiagnostics() const
Get the current diagnostics engine.
void createFileManager()
Create the file manager and replace any existing one with it.
FileManager & getFileManager() const
Return the current file manager to the caller.
ModuleCache & getModuleCache() const
void addDependencyCollector(std::shared_ptr< DependencyCollector > Listener)
void createVirtualFileSystem(IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS=llvm::vfs::getRealFileSystem(), DiagnosticConsumer *DC=nullptr)
Create a virtual file system instance based on the invocation.
FrontendOptions & getFrontendOpts()
HeaderSearchOptions & getHeaderSearchOpts()
void createSourceManager()
Create the source manager and replace any existing one with it.
CompilerInvocation & getInvocation()
PreprocessorOptions & getPreprocessorOpts()
bool ExecuteAction(FrontendAction &Act)
ExecuteAction - Execute the provided action against the compiler's CompilerInvocation object.
DiagnosticOptions & getDiagnosticOpts()
void setDependencyDirectivesGetter(std::unique_ptr< DependencyDirectivesGetter > Getter)
Helper class for holding the data necessary to invoke the compiler.
static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef< const char * > CommandLineArgs, DiagnosticsEngine &Diags, const char *Argv0=nullptr)
Create a compiler invocation from a list of input options.
DependencyOutputOptions & getDependencyOutputOpts()
Functor that returns the dependency directives for a given file.
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...
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:232
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
bool hasErrorOccurred() const
Definition Diagnostic.h:872
llvm::vfs::FileSystem & getVirtualFileSystem() const
unsigned ModulesShareFileManager
Whether to share the FileManager when building modules.
std::string OutputFile
The output file, if any.
unsigned GenReducedBMI
Whether to generate reduced BMI for C++20 named modules.
std::string ModuleOutputPath
Output Path for module output file.
unsigned GenerateGlobalModuleIndex
Whether we can generate the global module index if needed.
unsigned DisableFree
Disable memory freeing on exit.
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.
unsigned ModulesForceValidateUserHeaders
Whether to force the validation of user input files when a module is loaded (even despite the build s...
std::map< std::string, std::string, std::less<> > PrebuiltModuleFiles
The mapping of module names to prebuilt module files.
uint64_t BuildSessionTimestamp
The time in seconds when the build session started.
unsigned ModulesSkipHeaderSearchPaths
Whether to entirely skip writing header search paths.
std::string ModuleFormat
The module/pch container format.
std::string Sysroot
If non-empty, the directory to use as a "virtual system root" for include paths.
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.
std::string ResourceDir
The directory which holds the compiler resource files (builtin includes, etc.).
unsigned ModulesValidateOncePerBuildSession
If true, skip verifying input files used by modules if the module was already verified during this bu...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
void setBuildingModule(bool BuildingModuleFlag)
Flag indicating whether this instance is building a module.
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
The base class of the type hierarchy.
Definition TypeBase.h:1833
Dependency scanner callbacks that are used during scanning to influence the behaviour of the scan - f...
bool runInvocation(std::unique_ptr< CompilerInvocation > Invocation, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, std::shared_ptr< PCHContainerOperations > PCHContainerOps, DiagnosticConsumer *DiagConsumer)
The dependency scanning service contains shared configuration and state that is used by the individua...
void setVFS(llvm::StringSet<> &&VFS)
Update the VFSMap to the one discovered from serializing the AST file.
void addDependent(StringRef ModuleFile)
Add a direct dependent module file, so it can be updated if the current module is from stable directo...
void setInStableDir(bool V=false)
Update whether the prebuilt module resolves entirely in a stable directories.
bool isInStableDir() const
Read-only access to whether the module is made up of dependencies in stable directories.
llvm::StringRef getDriverMode(StringRef ProgName, ArrayRef< const char * > Args)
Returns the driver mode option's value, i.e.
Definition Driver.cpp:7148
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:7165
bool IsClangCL(StringRef DriverMode)
Checks whether the value produced by getDriverMode is for CL mode.
Definition Driver.cpp:7163
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
llvm::SmallVector< StringRef > getInitialStableDirs(const CompilerInstance &ScanInstance)
std::unique_ptr< DiagnosticOptions > createDiagOptions(ArrayRef< std::string > CommandLine)
std::pair< std::unique_ptr< driver::Driver >, std::unique_ptr< driver::Compilation > > buildCompilation(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS)
@ 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.
bool initializeScanCompilerInstance(CompilerInstance &ScanInstance, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service, IntrusiveRefCntPtr< DependencyScanningWorkerFilesystem > DepFS)
bool areOptionsInStableDir(const ArrayRef< StringRef > Directories, const HeaderSearchOptions &HSOpts)
Determine if options collected from a module's compilation can safely be considered as stable.
IntrusiveRefCntPtr< ModuleCache > makeInProcessModuleCache(ModuleCacheEntries &Entries)
std::unique_ptr< CompilerInvocation > createCompilerInvocation(ArrayRef< std::string > CommandLine, DiagnosticsEngine &Diags)
bool isPathInStableDir(const ArrayRef< StringRef > Directories, const StringRef Input)
Determine if Input can be resolved within a stable directory.
std::shared_ptr< ModuleDepCollector > initializeScanInstanceDependencyCollector(CompilerInstance &ScanInstance, std::unique_ptr< DependencyOutputOptions > DepOutputOpts, StringRef WorkingDirectory, DependencyConsumer &Consumer, DependencyScanningService &Service, CompilerInvocation &Inv, DependencyActionController &Controller, PrebuiltModulesAttrsMap PrebuiltModulesASTMap, llvm::SmallVector< StringRef > &StableDirs)
Create the dependency collector that will collect the produced dependencies.
std::optional< PrebuiltModulesAttrsMap > computePrebuiltModulesASTMap(CompilerInstance &ScanInstance, llvm::SmallVector< StringRef > &StableDirs)
std::pair< IntrusiveRefCntPtr< llvm::vfs::FileSystem >, std::vector< std::string > > initVFSForByNameScanning(IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS, ArrayRef< std::string > CommandLine, StringRef WorkingDirectory, StringRef ModuleName)
llvm::StringMap< PrebuiltModuleASTAttrs > PrebuiltModulesAttrsMap
Attributes loaded from AST files of prebuilt modules collected prior to ModuleDepCollector creation.
std::unique_ptr< DependencyOutputOptions > takeDependencyOutputOptionsFrom(CompilerInstance &ScanInstance)
std::pair< IntrusiveRefCntPtr< llvm::vfs::FileSystem >, std::vector< std::string > > initVFSForTUBuferScanning(IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS, ArrayRef< std::string > CommandLine, StringRef WorkingDirectory, llvm::MemoryBufferRef TUBuffer)
The JSON file list parser is used to communicate input to InstallAPI.
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
@ Result
The result type of a method or function.
Definition TypeBase.h:905
void normalizeModuleCachePath(FileManager &FileMgr, StringRef Path, SmallVectorImpl< char > &NormalizedPath)
int __ovld __cnfn any(char)
Returns 1 if the most significant bit in any component of x is set; otherwise returns 0.
DignosticsEngineWithDiagOpts(ArrayRef< std::string > CommandLine, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, DiagnosticConsumer &DC)