clang 22.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/IntrusiveRefCntPtr.h"
28#include "llvm/Support/Allocator.h"
29#include "llvm/Support/Error.h"
30#include "llvm/Support/MemoryBuffer.h"
31#include "llvm/TargetParser/Host.h"
32#include <optional>
33
34using namespace clang;
35using namespace tooling;
36using namespace dependencies;
37
38namespace {
39
40/// Forwards the gatherered dependencies to the consumer.
41class DependencyConsumerForwarder : public DependencyFileGenerator {
42public:
43 DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
44 StringRef WorkingDirectory, DependencyConsumer &C)
45 : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory),
46 Opts(std::move(Opts)), C(C) {}
47
48 void finishedMainFile(DiagnosticsEngine &Diags) override {
49 C.handleDependencyOutputOpts(*Opts);
50 llvm::SmallString<256> CanonPath;
51 for (const auto &File : getDependencies()) {
52 CanonPath = File;
53 llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
54 llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath);
55 C.handleFileDependency(CanonPath);
56 }
57 }
58
59private:
60 StringRef WorkingDirectory;
61 std::unique_ptr<DependencyOutputOptions> Opts;
62 DependencyConsumer &C;
63};
64
65static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
66 const HeaderSearchOptions &ExistingHSOpts,
67 DiagnosticsEngine *Diags,
68 const LangOptions &LangOpts) {
69 if (LangOpts.Modules) {
70 if (HSOpts.VFSOverlayFiles != ExistingHSOpts.VFSOverlayFiles) {
71 if (Diags) {
72 Diags->Report(diag::warn_pch_vfsoverlay_mismatch);
73 auto VFSNote = [&](int Type, ArrayRef<std::string> VFSOverlays) {
74 if (VFSOverlays.empty()) {
75 Diags->Report(diag::note_pch_vfsoverlay_empty) << Type;
76 } else {
77 std::string Files = llvm::join(VFSOverlays, "\n");
78 Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files;
79 }
80 };
81 VFSNote(0, HSOpts.VFSOverlayFiles);
82 VFSNote(1, ExistingHSOpts.VFSOverlayFiles);
83 }
84 }
85 }
86 return false;
87}
88
89using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
90
91/// A listener that collects the imported modules and the input
92/// files. While visiting, collect vfsoverlays and file inputs that determine
93/// whether prebuilt modules fully resolve in stable directories.
94class PrebuiltModuleListener : public ASTReaderListener {
95public:
96 PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
97 llvm::SmallVector<std::string> &NewModuleFiles,
98 PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
99 const HeaderSearchOptions &HSOpts,
100 const LangOptions &LangOpts, DiagnosticsEngine &Diags,
101 const ArrayRef<StringRef> StableDirs)
102 : PrebuiltModuleFiles(PrebuiltModuleFiles),
103 NewModuleFiles(NewModuleFiles),
104 PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts),
105 ExistingLangOpts(LangOpts), Diags(Diags), StableDirs(StableDirs) {}
106
107 bool needsImportVisitation() const override { return true; }
108 bool needsInputFileVisitation() override { return true; }
109 bool needsSystemInputFileVisitation() override { return true; }
110
111 /// Accumulate the modules are transitively depended on by the initial
112 /// prebuilt module.
113 void visitImport(StringRef ModuleName, StringRef Filename) override {
114 if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
115 NewModuleFiles.push_back(Filename.str());
116
117 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(Filename);
118 PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
119 if (PrebuiltMapEntry.second)
120 PrebuiltModule.setInStableDir(!StableDirs.empty());
121
122 if (auto It = PrebuiltModulesASTMap.find(CurrentFile);
123 It != PrebuiltModulesASTMap.end() && CurrentFile != Filename)
124 PrebuiltModule.addDependent(It->getKey());
125 }
126
127 /// For each input file discovered, check whether it's external path is in a
128 /// stable directory. Traversal is stopped if the current module is not
129 /// considered stable.
130 bool visitInputFileAsRequested(StringRef FilenameAsRequested,
131 StringRef Filename, bool isSystem,
132 bool isOverridden,
133 bool isExplicitModule) override {
134 if (StableDirs.empty())
135 return false;
136 auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
137 if ((PrebuiltEntryIt == PrebuiltModulesASTMap.end()) ||
138 (!PrebuiltEntryIt->second.isInStableDir()))
139 return false;
140
141 PrebuiltEntryIt->second.setInStableDir(
142 isPathInStableDir(StableDirs, Filename));
143 return PrebuiltEntryIt->second.isInStableDir();
144 }
145
146 /// Update which module that is being actively traversed.
147 void visitModuleFile(StringRef Filename,
148 serialization::ModuleKind Kind) override {
149 // If the CurrentFile is not
150 // considered stable, update any of it's transitive dependents.
151 auto PrebuiltEntryIt = PrebuiltModulesASTMap.find(CurrentFile);
152 if ((PrebuiltEntryIt != PrebuiltModulesASTMap.end()) &&
153 !PrebuiltEntryIt->second.isInStableDir())
154 PrebuiltEntryIt->second.updateDependentsNotInStableDirs(
155 PrebuiltModulesASTMap);
156 CurrentFile = Filename;
157 }
158
159 /// Check the header search options for a given module when considering
160 /// if the module comes from stable directories.
161 bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
162 StringRef ModuleFilename,
163 StringRef SpecificModuleCachePath,
164 bool Complain) override {
165
166 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
167 PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
168 if (PrebuiltMapEntry.second)
169 PrebuiltModule.setInStableDir(!StableDirs.empty());
170
171 if (PrebuiltModule.isInStableDir())
172 PrebuiltModule.setInStableDir(areOptionsInStableDir(StableDirs, HSOpts));
173
174 return false;
175 }
176
177 /// Accumulate vfsoverlays used to build these prebuilt modules.
178 bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
179 bool Complain) override {
180
181 auto PrebuiltMapEntry = PrebuiltModulesASTMap.try_emplace(CurrentFile);
182 PrebuiltModuleASTAttrs &PrebuiltModule = PrebuiltMapEntry.first->second;
183 if (PrebuiltMapEntry.second)
184 PrebuiltModule.setInStableDir(!StableDirs.empty());
185
186 PrebuiltModule.setVFS(
187 llvm::StringSet<>(llvm::from_range, HSOpts.VFSOverlayFiles));
188
189 return checkHeaderSearchPaths(
190 HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);
191 }
192
193private:
194 PrebuiltModuleFilesT &PrebuiltModuleFiles;
195 llvm::SmallVector<std::string> &NewModuleFiles;
196 PrebuiltModulesAttrsMap &PrebuiltModulesASTMap;
197 const HeaderSearchOptions &ExistingHSOpts;
198 const LangOptions &ExistingLangOpts;
199 DiagnosticsEngine &Diags;
200 std::string CurrentFile;
201 const ArrayRef<StringRef> StableDirs;
202};
203
204/// Visit the given prebuilt module and collect all of the modules it
205/// transitively imports and contributing input files.
206static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
208 PrebuiltModuleFilesT &ModuleFiles,
209 PrebuiltModulesAttrsMap &PrebuiltModulesASTMap,
210 DiagnosticsEngine &Diags,
211 const ArrayRef<StringRef> StableDirs) {
212 // List of module files to be processed.
214
215 PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModulesASTMap,
217 Diags, StableDirs);
218
219 Listener.visitModuleFile(PrebuiltModuleFilename,
222 PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(),
224 /*FindModuleFileExtensions=*/false, Listener,
225 /*ValidateDiagnosticOptions=*/false, ASTReader::ARR_OutOfDate))
226 return true;
227
228 while (!Worklist.empty()) {
229 Listener.visitModuleFile(Worklist.back(), serialization::MK_ExplicitModule);
231 Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
233 /*FindModuleFileExtensions=*/false, Listener,
234 /*ValidateDiagnosticOptions=*/false))
235 return true;
236 }
237 return false;
238}
239
240/// Transform arbitrary file name into an object-like file name.
241static std::string makeObjFileName(StringRef FileName) {
242 SmallString<128> ObjFileName(FileName);
243 llvm::sys::path::replace_extension(ObjFileName, "o");
244 return std::string(ObjFileName);
245}
246
247/// Deduce the dependency target based on the output file and input files.
248static std::string
249deduceDepTarget(const std::string &OutputFile,
250 const SmallVectorImpl<FrontendInputFile> &InputFiles) {
251 if (OutputFile != "-")
252 return OutputFile;
253
254 if (InputFiles.empty() || !InputFiles.front().isFile())
255 return "clang-scan-deps\\ dependency";
256
257 return makeObjFileName(InputFiles.front().getFile());
258}
259
260/// Sanitize diagnostic options for dependency scan.
261static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
262 // Don't print 'X warnings and Y errors generated'.
263 DiagOpts.ShowCarets = false;
264 // Don't write out diagnostic file.
265 DiagOpts.DiagnosticSerializationFile.clear();
266 // Don't emit warnings except for scanning specific warnings.
267 // TODO: It would be useful to add a more principled way to ignore all
268 // warnings that come from source code. The issue is that we need to
269 // ignore warnings that could be surpressed by
270 // `#pragma clang diagnostic`, while still allowing some scanning
271 // warnings for things we're not ready to turn into errors yet.
272 // See `test/ClangScanDeps/diagnostic-pragmas.c` for an example.
273 llvm::erase_if(DiagOpts.Warnings, [](StringRef Warning) {
274 return llvm::StringSwitch<bool>(Warning)
275 .Cases("pch-vfs-diff", "error=pch-vfs-diff", false)
276 .StartsWith("no-error=", false)
277 .Default(true);
278 });
279}
280
281// Clang implements -D and -U by splatting text into a predefines buffer. This
282// allows constructs such as `-DF࢞=3 "-D F\u{0D9E} 4 3 2”` to be accepted and
283// define the same macro, or adding C++ style comments before the macro name.
284//
285// This function checks that the first non-space characters in the macro
286// obviously form an identifier that can be uniqued on without lexing. Failing
287// to do this could lead to changing the final definition of a macro.
288//
289// We could set up a preprocessor and actually lex the name, but that's very
290// heavyweight for a situation that will almost never happen in practice.
291static std::optional<StringRef> getSimpleMacroName(StringRef Macro) {
292 StringRef Name = Macro.split("=").first.ltrim(" \t");
293 std::size_t I = 0;
294
295 auto FinishName = [&]() -> std::optional<StringRef> {
296 StringRef SimpleName = Name.slice(0, I);
297 if (SimpleName.empty())
298 return std::nullopt;
299 return SimpleName;
300 };
301
302 for (; I != Name.size(); ++I) {
303 switch (Name[I]) {
304 case '(': // Start of macro parameter list
305 case ' ': // End of macro name
306 case '\t':
307 return FinishName();
308 case '_':
309 continue;
310 default:
311 if (llvm::isAlnum(Name[I]))
312 continue;
313 return std::nullopt;
314 }
315 }
316 return FinishName();
317}
318
319static void canonicalizeDefines(PreprocessorOptions &PPOpts) {
320 using MacroOpt = std::pair<StringRef, std::size_t>;
321 std::vector<MacroOpt> SimpleNames;
322 SimpleNames.reserve(PPOpts.Macros.size());
323 std::size_t Index = 0;
324 for (const auto &M : PPOpts.Macros) {
325 auto SName = getSimpleMacroName(M.first);
326 // Skip optimizing if we can't guarantee we can preserve relative order.
327 if (!SName)
328 return;
329 SimpleNames.emplace_back(*SName, Index);
330 ++Index;
331 }
332
333 llvm::stable_sort(SimpleNames, llvm::less_first());
334 // Keep the last instance of each macro name by going in reverse
335 auto NewEnd = std::unique(
336 SimpleNames.rbegin(), SimpleNames.rend(),
337 [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; });
338 SimpleNames.erase(SimpleNames.begin(), NewEnd.base());
339
340 // Apply permutation.
341 decltype(PPOpts.Macros) NewMacros;
342 NewMacros.reserve(SimpleNames.size());
343 for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) {
344 std::size_t OriginalIndex = SimpleNames[I].second;
345 // We still emit undefines here as they may be undefining a predefined macro
346 NewMacros.push_back(std::move(PPOpts.Macros[OriginalIndex]));
347 }
348 std::swap(PPOpts.Macros, NewMacros);
349}
350
351class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter {
352 DependencyScanningWorkerFilesystem *DepFS;
353
354public:
355 ScanningDependencyDirectivesGetter(FileManager &FileMgr) : DepFS(nullptr) {
356 FileMgr.getVirtualFileSystem().visit([&](llvm::vfs::FileSystem &FS) {
357 auto *DFS = llvm::dyn_cast<DependencyScanningWorkerFilesystem>(&FS);
358 if (DFS) {
359 assert(!DepFS && "Found multiple scanning VFSs");
360 DepFS = DFS;
361 }
362 });
363 assert(DepFS && "Did not find scanning VFS");
364 }
365
366 std::unique_ptr<DependencyDirectivesGetter>
367 cloneFor(FileManager &FileMgr) override {
368 return std::make_unique<ScanningDependencyDirectivesGetter>(FileMgr);
369 }
370
371 std::optional<ArrayRef<dependency_directives_scan::Directive>>
372 operator()(FileEntryRef File) override {
373 return DepFS->getDirectiveTokens(File.getName());
374 }
375};
376
377/// A clang tool that runs the preprocessor in a mode that's optimized for
378/// dependency scanning for the given compiler invocation.
379class DependencyScanningAction {
380public:
381 DependencyScanningAction(
382 DependencyScanningService &Service, StringRef WorkingDirectory,
383 DependencyConsumer &Consumer, DependencyActionController &Controller,
384 llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
385 std::optional<StringRef> ModuleName = std::nullopt)
386 : Service(Service), WorkingDirectory(WorkingDirectory),
387 Consumer(Consumer), Controller(Controller), DepFS(std::move(DepFS)),
388 ModuleName(ModuleName) {}
389
390 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
391 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
392 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
393 DiagnosticConsumer *DiagConsumer) {
394 // Make a deep copy of the original Clang invocation.
395 CompilerInvocation OriginalInvocation(*Invocation);
396 if (any(Service.getOptimizeArgs() & ScanningOptimizations::Macros))
397 canonicalizeDefines(OriginalInvocation.getPreprocessorOpts());
398
399 if (Scanned) {
400 // Scanning runs once for the first -cc1 invocation in a chain of driver
401 // jobs. For any dependent jobs, reuse the scanning result and just
402 // update the LastCC1Arguments to correspond to the new invocation.
403 // FIXME: to support multi-arch builds, each arch requires a separate scan
404 setLastCC1Arguments(std::move(OriginalInvocation));
405 return true;
406 }
407
408 Scanned = true;
409
410 // Create a compiler instance to handle the actual work.
411 auto ModCache = makeInProcessModuleCache(Service.getModuleCacheEntries());
412 ScanInstanceStorage.emplace(std::move(Invocation),
413 std::move(PCHContainerOps), ModCache.get());
414 CompilerInstance &ScanInstance = *ScanInstanceStorage;
415 ScanInstance.setBuildingModule(false);
416
417 // Create the compiler's actual diagnostics engine.
418 sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
419 assert(!DiagConsumerFinished && "attempt to reuse finished consumer");
420 ScanInstance.createDiagnostics(*FS, DiagConsumer,
421 /*ShouldOwnClient=*/false);
422 if (!ScanInstance.hasDiagnostics())
423 return false;
424
426 true;
427
430 Service.getBuildSessionTimestamp();
431
432 ScanInstance.getFrontendOpts().DisableFree = false;
433 ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
434 ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
435 // This will prevent us compiling individual modules asynchronously since
436 // FileManager is not thread-safe, but it does improve performance for now.
437 ScanInstance.getFrontendOpts().ModulesShareFileManager = true;
438 ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
440 any(Service.getOptimizeArgs() & ScanningOptimizations::VFS);
441
442 // Support for virtual file system overlays.
444 ScanInstance.getDiagnostics(),
445 std::move(FS));
446
447 // Create a new FileManager to match the invocation's FileSystemOptions.
448 auto *FileMgr = ScanInstance.createFileManager(FS);
449
450 // Use the dependency scanning optimized file system if requested to do so.
451 if (DepFS) {
452 SmallString<256> ModulesCachePath;
454 *FileMgr, ScanInstance.getHeaderSearchOpts().ModuleCachePath,
455 ModulesCachePath);
456 DepFS->resetBypassedPathPrefix();
457 if (!ModulesCachePath.empty())
458 DepFS->setBypassedPathPrefix(ModulesCachePath);
459
461 std::make_unique<ScanningDependencyDirectivesGetter>(*FileMgr));
462 }
463
464 ScanInstance.createSourceManager(*FileMgr);
465
466 // Create a collection of stable directories derived from the ScanInstance
467 // for determining whether module dependencies would fully resolve from
468 // those directories.
469 llvm::SmallVector<StringRef> StableDirs;
470 const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot;
471 if (!Sysroot.empty() &&
472 (llvm::sys::path::root_directory(Sysroot) != Sysroot))
473 StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts().ResourceDir};
474
475 // Store a mapping of prebuilt module files and their properties like header
476 // search options. This will prevent the implicit build to create duplicate
477 // modules and will force reuse of the existing prebuilt module files
478 // instead.
479 PrebuiltModulesAttrsMap PrebuiltModulesASTMap;
480
481 if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
482 if (visitPrebuiltModule(
484 ScanInstance,
486 PrebuiltModulesASTMap, ScanInstance.getDiagnostics(), StableDirs))
487 return false;
488
489 // Create the dependency collector that will collect the produced
490 // dependencies.
491 //
492 // This also moves the existing dependency output options from the
493 // invocation to the collector. The options in the invocation are reset,
494 // which ensures that the compiler won't create new dependency collectors,
495 // and thus won't write out the extra '.d' files to disk.
496 auto Opts = std::make_unique<DependencyOutputOptions>();
497 std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());
498 // We need at least one -MT equivalent for the generator of make dependency
499 // files to work.
500 if (Opts->Targets.empty())
501 Opts->Targets = {
502 deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile,
503 ScanInstance.getFrontendOpts().Inputs)};
504 Opts->IncludeSystemHeaders = true;
505
506 switch (Service.getFormat()) {
507 case ScanningOutputFormat::Make:
508 ScanInstance.addDependencyCollector(
509 std::make_shared<DependencyConsumerForwarder>(
510 std::move(Opts), WorkingDirectory, Consumer));
511 break;
512 case ScanningOutputFormat::P1689:
513 case ScanningOutputFormat::Full:
514 MDC = std::make_shared<ModuleDepCollector>(
515 Service, std::move(Opts), ScanInstance, Consumer, Controller,
516 OriginalInvocation, std::move(PrebuiltModulesASTMap), StableDirs);
517 ScanInstance.addDependencyCollector(MDC);
518 break;
519 }
520
521 // Consider different header search and diagnostic options to create
522 // different modules. This avoids the unsound aliasing of module PCMs.
523 //
524 // TODO: Implement diagnostic bucketing to reduce the impact of strict
525 // context hashing.
526 ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
531 true;
533
534 // Avoid some checks and module map parsing when loading PCM files.
535 ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
536
537 std::unique_ptr<FrontendAction> Action;
538
539 if (Service.getFormat() == ScanningOutputFormat::P1689)
540 Action = std::make_unique<PreprocessOnlyAction>();
541 else if (ModuleName)
542 Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
543 else
544 Action = std::make_unique<ReadPCHAndPreprocessAction>();
545
546 if (ScanInstance.getDiagnostics().hasErrorOccurred())
547 return false;
548
549 const bool Result = ScanInstance.ExecuteAction(*Action);
550
551 // ExecuteAction is responsible for calling finish.
552 DiagConsumerFinished = true;
553
554 if (Result)
555 setLastCC1Arguments(std::move(OriginalInvocation));
556
557 return Result;
558 }
559
560 bool hasScanned() const { return Scanned; }
561 bool hasDiagConsumerFinished() const { return DiagConsumerFinished; }
562
563 /// Take the cc1 arguments corresponding to the most recent invocation used
564 /// with this action. Any modifications implied by the discovered dependencies
565 /// will have already been applied.
566 std::vector<std::string> takeLastCC1Arguments() {
567 std::vector<std::string> Result;
568 std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty.
569 return Result;
570 }
571
572private:
573 void setLastCC1Arguments(CompilerInvocation &&CI) {
574 if (MDC)
575 MDC->applyDiscoveredDependencies(CI);
576 LastCC1Arguments = CI.getCC1CommandLine();
577 }
578
579 DependencyScanningService &Service;
580 StringRef WorkingDirectory;
581 DependencyConsumer &Consumer;
582 DependencyActionController &Controller;
583 llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
584 std::optional<StringRef> ModuleName;
585 std::optional<CompilerInstance> ScanInstanceStorage;
586 std::shared_ptr<ModuleDepCollector> MDC;
587 std::vector<std::string> LastCC1Arguments;
588 bool Scanned = false;
589 bool DiagConsumerFinished = false;
590};
591
592} // end anonymous namespace
593
597 : Service(Service) {
598 PCHContainerOps = std::make_shared<PCHContainerOperations>();
599 // We need to read object files from PCH built outside the scanner.
600 PCHContainerOps->registerReader(
601 std::make_unique<ObjectFilePCHContainerReader>());
602 // The scanner itself writes only raw ast files.
603 PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
604
605 if (Service.shouldTraceVFS())
606 FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS));
607
608 switch (Service.getMode()) {
610 DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
611 Service.getSharedCache(), FS);
612 BaseFS = DepFS;
613 break;
615 DepFS = nullptr;
616 BaseFS = FS;
617 break;
618 }
619}
620
621static std::unique_ptr<DiagnosticOptions>
622createDiagOptions(const std::vector<std::string> &CommandLine) {
623 std::vector<const char *> CLI;
624 for (const std::string &Arg : CommandLine)
625 CLI.push_back(Arg.c_str());
626 auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
627 sanitizeDiagOpts(*DiagOpts);
628 return DiagOpts;
629}
630
632 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
633 DependencyConsumer &Consumer, DependencyActionController &Controller,
634 std::optional<llvm::MemoryBufferRef> TUBuffer) {
635 // Capture the emitted diagnostics and report them to the client
636 // in the case of a failure.
637 std::string DiagnosticOutput;
638 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
639 auto DiagOpts = createDiagOptions(CommandLine);
640 TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, *DiagOpts);
641
642 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
643 DiagPrinter, TUBuffer))
644 return llvm::Error::success();
645 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
646 llvm::inconvertibleErrorCode());
647}
648
650 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
651 DependencyConsumer &Consumer, DependencyActionController &Controller,
652 StringRef ModuleName) {
653 // Capture the emitted diagnostics and report them to the client
654 // in the case of a failure.
655 std::string DiagnosticOutput;
656 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
657 auto DiagOpts = createDiagOptions(CommandLine);
658 TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, *DiagOpts);
659
660 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
661 DiagPrinter, ModuleName))
662 return llvm::Error::success();
663 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
664 llvm::inconvertibleErrorCode());
665}
666
670 llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
672 Argv.reserve(ArgStrs.size());
673 for (const std::string &Arg : ArgStrs)
674 Argv.push_back(Arg.c_str());
675
676 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
677 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
678 "clang LLVM compiler", FS);
679 Driver->setTitle("clang_based_tool");
680
681 llvm::BumpPtrAllocator Alloc;
682 bool CLMode = driver::IsClangCL(
683 driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
684
685 if (llvm::Error E =
686 driver::expandResponseFiles(Argv, CLMode, Alloc, FS.get())) {
687 Diags.Report(diag::err_drv_expand_response_file)
688 << llvm::toString(std::move(E));
689 return false;
690 }
691
692 const std::unique_ptr<driver::Compilation> Compilation(
693 Driver->BuildCompilation(llvm::ArrayRef(Argv)));
694 if (!Compilation)
695 return false;
696
697 if (Compilation->containsError())
698 return false;
699
700 for (const driver::Command &Job : Compilation->getJobs()) {
701 if (!Callback(Job))
702 return false;
703 }
704 return true;
705}
706
708 std::vector<std::string> CommandLine, DependencyScanningAction &Action,
710 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
711 DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {
712
713 // Save executable path before providing CommandLine to ToolInvocation
714 std::string Executable = CommandLine[0];
715
716 llvm::opt::ArgStringList Argv;
717 for (const std::string &Str : ArrayRef(CommandLine).drop_front())
718 Argv.push_back(Str.c_str());
719
720 auto Invocation = std::make_shared<CompilerInvocation>();
721 if (!CompilerInvocation::CreateFromArgs(*Invocation, Argv, Diags)) {
722 // FIXME: Should we just go on like cc1_main does?
723 return false;
724 }
725
726 if (!Action.runInvocation(std::move(Invocation), std::move(FS),
727 PCHContainerOps, Diags.getClient()))
728 return false;
729
730 std::vector<std::string> Args = Action.takeLastCC1Arguments();
731 Consumer.handleBuildCommand({std::move(Executable), std::move(Args)});
732 return true;
733}
734
735bool DependencyScanningWorker::scanDependencies(
736 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
737 DependencyConsumer &Consumer, DependencyActionController &Controller,
738 DiagnosticConsumer &DC, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
739 std::optional<StringRef> ModuleName) {
740 std::vector<const char *> CCommandLine(CommandLine.size(), nullptr);
741 llvm::transform(CommandLine, CCommandLine.begin(),
742 [](const std::string &Str) { return Str.c_str(); });
743 auto DiagOpts = CreateAndPopulateDiagOpts(CCommandLine);
744 sanitizeDiagOpts(*DiagOpts);
745 auto Diags = CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DC,
746 /*ShouldOwnClient=*/false);
747
748 DependencyScanningAction Action(Service, WorkingDirectory, Consumer,
749 Controller, DepFS, ModuleName);
750
751 bool Success = false;
752 if (CommandLine[1] == "-cc1") {
753 Success = createAndRunToolInvocation(CommandLine, Action, FS,
754 PCHContainerOps, *Diags, Consumer);
755 } else {
757 CommandLine, *Diags, FS, [&](const driver::Command &Cmd) {
758 if (StringRef(Cmd.getCreator().getName()) != "clang") {
759 // Non-clang command. Just pass through to the dependency
760 // consumer.
761 Consumer.handleBuildCommand(
762 {Cmd.getExecutable(),
763 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
764 return true;
765 }
766
767 // Insert -cc1 comand line options into Argv
768 std::vector<std::string> Argv;
769 Argv.push_back(Cmd.getExecutable());
770 llvm::append_range(Argv, Cmd.getArguments());
771
772 // Create an invocation that uses the underlying file
773 // system to ensure that any file system requests that
774 // are made by the driver do not go through the
775 // dependency scanning filesystem.
776 return createAndRunToolInvocation(std::move(Argv), Action, FS,
777 PCHContainerOps, *Diags, Consumer);
778 });
779 }
780
781 if (Success && !Action.hasScanned())
782 Diags->Report(diag::err_fe_expected_compiler_job)
783 << llvm::join(CommandLine, " ");
784
785 // Ensure finish() is called even if we never reached ExecuteAction().
786 if (!Action.hasDiagConsumerFinished())
787 DC.finish();
788
789 return Success && Action.hasScanned();
790}
791
793 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
794 DependencyConsumer &Consumer, DependencyActionController &Controller,
795 DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) {
796 // Reset what might have been modified in the previous worker invocation.
797 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
798
799 std::optional<std::vector<std::string>> ModifiedCommandLine;
801
802 // If we're scanning based on a module name alone, we don't expect the client
803 // to provide us with an input file. However, the driver really wants to have
804 // one. Let's just make it up to make the driver happy.
805 if (TUBuffer) {
806 auto OverlayFS =
807 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
808 auto InMemoryFS =
809 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
810 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
811 auto InputPath = TUBuffer->getBufferIdentifier();
812 InMemoryFS->addFile(
813 InputPath, 0,
814 llvm::MemoryBuffer::getMemBufferCopy(TUBuffer->getBuffer()));
816 InMemoryFS;
817
818 OverlayFS->pushOverlay(InMemoryOverlay);
819 ModifiedFS = OverlayFS;
820 ModifiedCommandLine = CommandLine;
821 ModifiedCommandLine->emplace_back(InputPath);
822 }
823
824 const std::vector<std::string> &FinalCommandLine =
825 ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
826 auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
827
828 return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer,
829 Controller, DC, FinalFS, /*ModuleName=*/std::nullopt);
830}
831
833 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
834 DependencyConsumer &Consumer, DependencyActionController &Controller,
835 DiagnosticConsumer &DC, StringRef ModuleName) {
836 // Reset what might have been modified in the previous worker invocation.
837 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
838
839 // If we're scanning based on a module name alone, we don't expect the client
840 // to provide us with an input file. However, the driver really wants to have
841 // one. Let's just make it up to make the driver happy.
842 auto OverlayFS =
843 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
844 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
845 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
846 SmallString<128> FakeInputPath;
847 // TODO: We should retry the creation if the path already exists.
848 llvm::sys::fs::createUniquePath(ModuleName + "-%%%%%%%%.input", FakeInputPath,
849 /*MakeAbsolute=*/false);
850 InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
851 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
852
853 OverlayFS->pushOverlay(InMemoryOverlay);
854 auto ModifiedCommandLine = CommandLine;
855 ModifiedCommandLine.emplace_back(FakeInputPath);
856
857 return scanDependencies(WorkingDirectory, ModifiedCommandLine, Consumer,
858 Controller, DC, OverlayFS, ModuleName);
859}
860
static bool createAndRunToolInvocation(std::vector< std::string > CommandLine, DependencyScanningAction &Action, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, std::shared_ptr< clang::PCHContainerOperations > &PCHContainerOps, DiagnosticsEngine &Diags, DependencyConsumer &Consumer)
static std::unique_ptr< DiagnosticOptions > createDiagOptions(const std::vector< std::string > &CommandLine)
static bool forEachDriverJob(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, llvm::function_ref< bool(const driver::Command &Cmd)> Callback)
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 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 ...
FileManager & getFileManager() const
Return the current file manager to the caller.
ModuleCache & 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()
void setDependencyDirectivesGetter(std::unique_ptr< DependencyDirectivesGetter > Getter)
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...
virtual void finish()
Callback to inform the diagnostic client that processing of all source files has ended.
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.
bool hasErrorOccurred() const
Definition Diagnostic.h:871
DiagnosticConsumer * getClient()
Definition Diagnostic.h:606
llvm::vfs::FileSystem & getVirtualFileSystem() const
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.
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
Command - An executable path/name and argument vector to execute.
Definition Job.h:106
const Tool & getCreator() const
getCreator - Return the Tool which caused the creation of this job.
Definition Job.h:191
const llvm::opt::ArgStringList & getArguments() const
Definition Job.h:224
const char * getExecutable() const
Definition Job.h:222
const char * getName() const
Definition Tool.h:48
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...
bool computeDependencies(StringRef WorkingDirectory, const std::vector< std::string > &CommandLine, DependencyConsumer &DepConsumer, DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, std::optional< llvm::MemoryBufferRef > TUBuffer=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)
Construct a dependency scanning worker.
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:7143
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:7160
bool IsClangCL(StringRef DriverMode)
Checks whether the value produced by getDriverMode is for CL mode.
Definition Driver.cpp:7158
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
@ 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.
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)
bool isPathInStableDir(const ArrayRef< StringRef > Directories, const StringRef Input)
Determine if Input can be resolved within a stable directory.
llvm::StringMap< PrebuiltModuleASTAttrs > PrebuiltModulesAttrsMap
Attributes loaded from AST files of prebuilt modules collected prior to ModuleDepCollector creation.
The JSON file list parser is used to communicate input to InstallAPI.
std::unique_ptr< DiagnosticOptions > CreateAndPopulateDiagOpts(ArrayRef< const char * > Argv)
@ Success
Annotation was successful.
Definition Parser.h:65
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
IntrusiveRefCntPtr< llvm::vfs::FileSystem > createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags)
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.