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 StringRef ModulesCachePath =
454
455 DepFS->resetBypassedPathPrefix();
456 if (!ModulesCachePath.empty())
457 DepFS->setBypassedPathPrefix(ModulesCachePath);
458
460 std::make_unique<ScanningDependencyDirectivesGetter>(*FileMgr));
461 }
462
463 ScanInstance.createSourceManager(*FileMgr);
464
465 // Create a collection of stable directories derived from the ScanInstance
466 // for determining whether module dependencies would fully resolve from
467 // those directories.
468 llvm::SmallVector<StringRef> StableDirs;
469 const StringRef Sysroot = ScanInstance.getHeaderSearchOpts().Sysroot;
470 if (!Sysroot.empty() &&
471 (llvm::sys::path::root_directory(Sysroot) != Sysroot))
472 StableDirs = {Sysroot, ScanInstance.getHeaderSearchOpts().ResourceDir};
473
474 // Store a mapping of prebuilt module files and their properties like header
475 // search options. This will prevent the implicit build to create duplicate
476 // modules and will force reuse of the existing prebuilt module files
477 // instead.
478 PrebuiltModulesAttrsMap PrebuiltModulesASTMap;
479
480 if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
481 if (visitPrebuiltModule(
483 ScanInstance,
485 PrebuiltModulesASTMap, ScanInstance.getDiagnostics(), StableDirs))
486 return false;
487
488 // Create the dependency collector that will collect the produced
489 // dependencies.
490 //
491 // This also moves the existing dependency output options from the
492 // invocation to the collector. The options in the invocation are reset,
493 // which ensures that the compiler won't create new dependency collectors,
494 // and thus won't write out the extra '.d' files to disk.
495 auto Opts = std::make_unique<DependencyOutputOptions>();
496 std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());
497 // We need at least one -MT equivalent for the generator of make dependency
498 // files to work.
499 if (Opts->Targets.empty())
500 Opts->Targets = {
501 deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile,
502 ScanInstance.getFrontendOpts().Inputs)};
503 Opts->IncludeSystemHeaders = true;
504
505 switch (Service.getFormat()) {
506 case ScanningOutputFormat::Make:
507 ScanInstance.addDependencyCollector(
508 std::make_shared<DependencyConsumerForwarder>(
509 std::move(Opts), WorkingDirectory, Consumer));
510 break;
511 case ScanningOutputFormat::P1689:
512 case ScanningOutputFormat::Full:
513 MDC = std::make_shared<ModuleDepCollector>(
514 Service, std::move(Opts), ScanInstance, Consumer, Controller,
515 OriginalInvocation, std::move(PrebuiltModulesASTMap), StableDirs);
516 ScanInstance.addDependencyCollector(MDC);
517 break;
518 }
519
520 // Consider different header search and diagnostic options to create
521 // different modules. This avoids the unsound aliasing of module PCMs.
522 //
523 // TODO: Implement diagnostic bucketing to reduce the impact of strict
524 // context hashing.
525 ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
530 true;
532
533 // Avoid some checks and module map parsing when loading PCM files.
534 ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
535
536 std::unique_ptr<FrontendAction> Action;
537
538 if (Service.getFormat() == ScanningOutputFormat::P1689)
539 Action = std::make_unique<PreprocessOnlyAction>();
540 else if (ModuleName)
541 Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
542 else
543 Action = std::make_unique<ReadPCHAndPreprocessAction>();
544
545 if (ScanInstance.getDiagnostics().hasErrorOccurred())
546 return false;
547
548 const bool Result = ScanInstance.ExecuteAction(*Action);
549
550 // ExecuteAction is responsible for calling finish.
551 DiagConsumerFinished = true;
552
553 if (Result)
554 setLastCC1Arguments(std::move(OriginalInvocation));
555
556 return Result;
557 }
558
559 bool hasScanned() const { return Scanned; }
560 bool hasDiagConsumerFinished() const { return DiagConsumerFinished; }
561
562 /// Take the cc1 arguments corresponding to the most recent invocation used
563 /// with this action. Any modifications implied by the discovered dependencies
564 /// will have already been applied.
565 std::vector<std::string> takeLastCC1Arguments() {
566 std::vector<std::string> Result;
567 std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty.
568 return Result;
569 }
570
571private:
572 void setLastCC1Arguments(CompilerInvocation &&CI) {
573 if (MDC)
574 MDC->applyDiscoveredDependencies(CI);
575 LastCC1Arguments = CI.getCC1CommandLine();
576 }
577
578 DependencyScanningService &Service;
579 StringRef WorkingDirectory;
580 DependencyConsumer &Consumer;
581 DependencyActionController &Controller;
582 llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
583 std::optional<StringRef> ModuleName;
584 std::optional<CompilerInstance> ScanInstanceStorage;
585 std::shared_ptr<ModuleDepCollector> MDC;
586 std::vector<std::string> LastCC1Arguments;
587 bool Scanned = false;
588 bool DiagConsumerFinished = false;
589};
590
591} // end anonymous namespace
592
596 : Service(Service) {
597 PCHContainerOps = std::make_shared<PCHContainerOperations>();
598 // We need to read object files from PCH built outside the scanner.
599 PCHContainerOps->registerReader(
600 std::make_unique<ObjectFilePCHContainerReader>());
601 // The scanner itself writes only raw ast files.
602 PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
603
604 if (Service.shouldTraceVFS())
605 FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS));
606
607 switch (Service.getMode()) {
609 DepFS = llvm::makeIntrusiveRefCnt<DependencyScanningWorkerFilesystem>(
610 Service.getSharedCache(), FS);
611 BaseFS = DepFS;
612 break;
614 DepFS = nullptr;
615 BaseFS = FS;
616 break;
617 }
618}
619
620static std::unique_ptr<DiagnosticOptions>
621createDiagOptions(const std::vector<std::string> &CommandLine) {
622 std::vector<const char *> CLI;
623 for (const std::string &Arg : CommandLine)
624 CLI.push_back(Arg.c_str());
625 auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
626 sanitizeDiagOpts(*DiagOpts);
627 return DiagOpts;
628}
629
631 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
632 DependencyConsumer &Consumer, DependencyActionController &Controller,
633 std::optional<llvm::MemoryBufferRef> TUBuffer) {
634 // Capture the emitted diagnostics and report them to the client
635 // in the case of a failure.
636 std::string DiagnosticOutput;
637 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
638 auto DiagOpts = createDiagOptions(CommandLine);
639 TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, *DiagOpts);
640
641 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
642 DiagPrinter, TUBuffer))
643 return llvm::Error::success();
644 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
645 llvm::inconvertibleErrorCode());
646}
647
649 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
650 DependencyConsumer &Consumer, DependencyActionController &Controller,
651 StringRef ModuleName) {
652 // Capture the emitted diagnostics and report them to the client
653 // in the case of a failure.
654 std::string DiagnosticOutput;
655 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
656 auto DiagOpts = createDiagOptions(CommandLine);
657 TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, *DiagOpts);
658
659 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
660 DiagPrinter, ModuleName))
661 return llvm::Error::success();
662 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
663 llvm::inconvertibleErrorCode());
664}
665
669 llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
671 Argv.reserve(ArgStrs.size());
672 for (const std::string &Arg : ArgStrs)
673 Argv.push_back(Arg.c_str());
674
675 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
676 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
677 "clang LLVM compiler", FS);
678 Driver->setTitle("clang_based_tool");
679
680 llvm::BumpPtrAllocator Alloc;
681 bool CLMode = driver::IsClangCL(
682 driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
683
684 if (llvm::Error E =
685 driver::expandResponseFiles(Argv, CLMode, Alloc, FS.get())) {
686 Diags.Report(diag::err_drv_expand_response_file)
687 << llvm::toString(std::move(E));
688 return false;
689 }
690
691 const std::unique_ptr<driver::Compilation> Compilation(
692 Driver->BuildCompilation(llvm::ArrayRef(Argv)));
693 if (!Compilation)
694 return false;
695
696 if (Compilation->containsError())
697 return false;
698
699 for (const driver::Command &Job : Compilation->getJobs()) {
700 if (!Callback(Job))
701 return false;
702 }
703 return true;
704}
705
707 std::vector<std::string> CommandLine, DependencyScanningAction &Action,
709 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
710 DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {
711
712 // Save executable path before providing CommandLine to ToolInvocation
713 std::string Executable = CommandLine[0];
714
715 llvm::opt::ArgStringList Argv;
716 for (const std::string &Str : ArrayRef(CommandLine).drop_front())
717 Argv.push_back(Str.c_str());
718
719 auto Invocation = std::make_shared<CompilerInvocation>();
720 if (!CompilerInvocation::CreateFromArgs(*Invocation, Argv, Diags)) {
721 // FIXME: Should we just go on like cc1_main does?
722 return false;
723 }
724
725 if (!Action.runInvocation(std::move(Invocation), std::move(FS),
726 PCHContainerOps, Diags.getClient()))
727 return false;
728
729 std::vector<std::string> Args = Action.takeLastCC1Arguments();
730 Consumer.handleBuildCommand({std::move(Executable), std::move(Args)});
731 return true;
732}
733
734bool DependencyScanningWorker::scanDependencies(
735 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
736 DependencyConsumer &Consumer, DependencyActionController &Controller,
737 DiagnosticConsumer &DC, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
738 std::optional<StringRef> ModuleName) {
739 std::vector<const char *> CCommandLine(CommandLine.size(), nullptr);
740 llvm::transform(CommandLine, CCommandLine.begin(),
741 [](const std::string &Str) { return Str.c_str(); });
742 auto DiagOpts = CreateAndPopulateDiagOpts(CCommandLine);
743 sanitizeDiagOpts(*DiagOpts);
744 auto Diags = CompilerInstance::createDiagnostics(*FS, *DiagOpts, &DC,
745 /*ShouldOwnClient=*/false);
746
747 DependencyScanningAction Action(Service, WorkingDirectory, Consumer,
748 Controller, DepFS, ModuleName);
749
750 bool Success = false;
751 if (CommandLine[1] == "-cc1") {
752 Success = createAndRunToolInvocation(CommandLine, Action, FS,
753 PCHContainerOps, *Diags, Consumer);
754 } else {
756 CommandLine, *Diags, FS, [&](const driver::Command &Cmd) {
757 if (StringRef(Cmd.getCreator().getName()) != "clang") {
758 // Non-clang command. Just pass through to the dependency
759 // consumer.
760 Consumer.handleBuildCommand(
761 {Cmd.getExecutable(),
762 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
763 return true;
764 }
765
766 // Insert -cc1 comand line options into Argv
767 std::vector<std::string> Argv;
768 Argv.push_back(Cmd.getExecutable());
769 llvm::append_range(Argv, Cmd.getArguments());
770
771 // Create an invocation that uses the underlying file
772 // system to ensure that any file system requests that
773 // are made by the driver do not go through the
774 // dependency scanning filesystem.
775 return createAndRunToolInvocation(std::move(Argv), Action, FS,
776 PCHContainerOps, *Diags, Consumer);
777 });
778 }
779
780 if (Success && !Action.hasScanned())
781 Diags->Report(diag::err_fe_expected_compiler_job)
782 << llvm::join(CommandLine, " ");
783
784 // Ensure finish() is called even if we never reached ExecuteAction().
785 if (!Action.hasDiagConsumerFinished())
786 DC.finish();
787
788 return Success && Action.hasScanned();
789}
790
792 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
793 DependencyConsumer &Consumer, DependencyActionController &Controller,
794 DiagnosticConsumer &DC, std::optional<llvm::MemoryBufferRef> TUBuffer) {
795 // Reset what might have been modified in the previous worker invocation.
796 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
797
798 std::optional<std::vector<std::string>> ModifiedCommandLine;
800
801 // If we're scanning based on a module name alone, we don't expect the client
802 // to provide us with an input file. However, the driver really wants to have
803 // one. Let's just make it up to make the driver happy.
804 if (TUBuffer) {
805 auto OverlayFS =
806 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
807 auto InMemoryFS =
808 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
809 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
810 auto InputPath = TUBuffer->getBufferIdentifier();
811 InMemoryFS->addFile(
812 InputPath, 0,
813 llvm::MemoryBuffer::getMemBufferCopy(TUBuffer->getBuffer()));
815 InMemoryFS;
816
817 OverlayFS->pushOverlay(InMemoryOverlay);
818 ModifiedFS = OverlayFS;
819 ModifiedCommandLine = CommandLine;
820 ModifiedCommandLine->emplace_back(InputPath);
821 }
822
823 const std::vector<std::string> &FinalCommandLine =
824 ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
825 auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
826
827 return scanDependencies(WorkingDirectory, FinalCommandLine, Consumer,
828 Controller, DC, FinalFS, /*ModuleName=*/std::nullopt);
829}
830
832 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
833 DependencyConsumer &Consumer, DependencyActionController &Controller,
834 DiagnosticConsumer &DC, StringRef ModuleName) {
835 // Reset what might have been modified in the previous worker invocation.
836 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
837
838 // If we're scanning based on a module name alone, we don't expect the client
839 // to provide us with an input file. However, the driver really wants to have
840 // one. Let's just make it up to make the driver happy.
841 auto OverlayFS =
842 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
843 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
844 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
845 SmallString<128> FakeInputPath;
846 // TODO: We should retry the creation if the path already exists.
847 llvm::sys::fs::createUniquePath(ModuleName + "-%%%%%%%%.input", FakeInputPath,
848 /*MakeAbsolute=*/false);
849 InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
850 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
851
852 OverlayFS->pushOverlay(InMemoryOverlay);
853 auto ModifiedCommandLine = CommandLine;
854 ModifiedCommandLine.emplace_back(FakeInputPath);
855
856 return scanDependencies(WorkingDirectory, ModifiedCommandLine, Consumer,
857 Controller, DC, OverlayFS, ModuleName);
858}
859
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)
int __ovld __cnfn any(char)
Returns 1 if the most significant bit in any component of x is set; otherwise returns 0.