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