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
9#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
10#include "clang/Basic/DiagnosticDriver.h"
11#include "clang/Basic/DiagnosticFrontend.h"
12#include "clang/Basic/DiagnosticSerialization.h"
13#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
14#include "clang/Driver/Compilation.h"
15#include "clang/Driver/Driver.h"
16#include "clang/Driver/Job.h"
17#include "clang/Driver/Tool.h"
18#include "clang/Frontend/CompilerInstance.h"
19#include "clang/Frontend/CompilerInvocation.h"
20#include "clang/Frontend/FrontendActions.h"
21#include "clang/Frontend/TextDiagnosticPrinter.h"
22#include "clang/Frontend/Utils.h"
23#include "clang/Lex/PreprocessorOptions.h"
24#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
25#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
26#include "clang/Tooling/Tooling.h"
27#include "llvm/ADT/ScopeExit.h"
28#include "llvm/Support/Allocator.h"
29#include "llvm/Support/Error.h"
30#include "llvm/TargetParser/Host.h"
31#include <optional>
32
33using namespace clang;
34using namespace tooling;
35using namespace dependencies;
36
37namespace {
38
39/// Forwards the gatherered dependencies to the consumer.
40class DependencyConsumerForwarder : public DependencyFileGenerator {
41public:
42 DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
43 StringRef WorkingDirectory, DependencyConsumer &C)
44 : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory),
45 Opts(std::move(Opts)), C(C) {}
46
47 void finishedMainFile(DiagnosticsEngine &Diags) override {
48 C.handleDependencyOutputOpts(Opts: *Opts);
49 llvm::SmallString<256> CanonPath;
50 for (const auto &File : getDependencies()) {
51 CanonPath = File;
52 llvm::sys::path::remove_dots(path&: CanonPath, /*remove_dot_dot=*/true);
53 llvm::sys::fs::make_absolute(current_directory: WorkingDirectory, path&: CanonPath);
54 C.handleFileDependency(Filename: CanonPath);
55 }
56 }
57
58private:
59 StringRef WorkingDirectory;
60 std::unique_ptr<DependencyOutputOptions> Opts;
61 DependencyConsumer &C;
62};
63
64static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
65 const HeaderSearchOptions &ExistingHSOpts,
66 DiagnosticsEngine *Diags,
67 const LangOptions &LangOpts) {
68 if (LangOpts.Modules) {
69 if (HSOpts.VFSOverlayFiles != ExistingHSOpts.VFSOverlayFiles) {
70 if (Diags) {
71 Diags->Report(diag::warn_pch_vfsoverlay_mismatch);
72 auto VFSNote = [&](int Type, ArrayRef<std::string> VFSOverlays) {
73 if (VFSOverlays.empty()) {
74 Diags->Report(diag::note_pch_vfsoverlay_empty) << Type;
75 } else {
76 std::string Files = llvm::join(R&: VFSOverlays, Separator: "\n");
77 Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files;
78 }
79 };
80 VFSNote(0, HSOpts.VFSOverlayFiles);
81 VFSNote(1, ExistingHSOpts.VFSOverlayFiles);
82 }
83 }
84 }
85 return false;
86}
87
88using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
89
90/// A listener that collects the imported modules and optionally the input
91/// files.
92class PrebuiltModuleListener : public ASTReaderListener {
93public:
94 PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles,
95 llvm::SmallVector<std::string> &NewModuleFiles,
96 PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
97 const HeaderSearchOptions &HSOpts,
98 const LangOptions &LangOpts, DiagnosticsEngine &Diags)
99 : PrebuiltModuleFiles(PrebuiltModuleFiles),
100 NewModuleFiles(NewModuleFiles),
101 PrebuiltModuleVFSMap(PrebuiltModuleVFSMap), ExistingHSOpts(HSOpts),
102 ExistingLangOpts(LangOpts), Diags(Diags) {}
103
104 bool needsImportVisitation() const override { return true; }
105
106 void visitImport(StringRef ModuleName, StringRef Filename) override {
107 if (PrebuiltModuleFiles.insert(x: {ModuleName.str(), Filename.str()}).second)
108 NewModuleFiles.push_back(Elt: Filename.str());
109 }
110
111 void visitModuleFile(StringRef Filename,
112 serialization::ModuleKind Kind) override {
113 CurrentFile = Filename;
114 }
115
116 bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
117 bool Complain) override {
118 std::vector<std::string> VFSOverlayFiles = HSOpts.VFSOverlayFiles;
119 PrebuiltModuleVFSMap.insert(
120 KV: {CurrentFile, llvm::StringSet<>(VFSOverlayFiles)});
121 return checkHeaderSearchPaths(
122 HSOpts, ExistingHSOpts, Diags: Complain ? &Diags : nullptr, LangOpts: ExistingLangOpts);
123 }
124
125private:
126 PrebuiltModuleFilesT &PrebuiltModuleFiles;
127 llvm::SmallVector<std::string> &NewModuleFiles;
128 PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap;
129 const HeaderSearchOptions &ExistingHSOpts;
130 const LangOptions &ExistingLangOpts;
131 DiagnosticsEngine &Diags;
132 std::string CurrentFile;
133};
134
135/// Visit the given prebuilt module and collect all of the modules it
136/// transitively imports and contributing input files.
137static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
138 CompilerInstance &CI,
139 PrebuiltModuleFilesT &ModuleFiles,
140 PrebuiltModuleVFSMapT &PrebuiltModuleVFSMap,
141 DiagnosticsEngine &Diags) {
142 // List of module files to be processed.
143 llvm::SmallVector<std::string> Worklist;
144 PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModuleVFSMap,
145 CI.getHeaderSearchOpts(), CI.getLangOpts(),
146 Diags);
147
148 Listener.visitModuleFile(Filename: PrebuiltModuleFilename,
149 Kind: serialization::MK_ExplicitModule);
150 if (ASTReader::readASTFileControlBlock(
151 Filename: PrebuiltModuleFilename, FileMgr&: CI.getFileManager(), ModuleCache: CI.getModuleCache(),
152 PCHContainerRdr: CI.getPCHContainerReader(),
153 /*FindModuleFileExtensions=*/false, Listener,
154 /*ValidateDiagnosticOptions=*/false, ClientLoadCapabilities: ASTReader::ARR_OutOfDate))
155 return true;
156
157 while (!Worklist.empty()) {
158 Listener.visitModuleFile(Filename: Worklist.back(), Kind: serialization::MK_ExplicitModule);
159 if (ASTReader::readASTFileControlBlock(
160 Filename: Worklist.pop_back_val(), FileMgr&: CI.getFileManager(), ModuleCache: CI.getModuleCache(),
161 PCHContainerRdr: CI.getPCHContainerReader(),
162 /*FindModuleFileExtensions=*/false, Listener,
163 /*ValidateDiagnosticOptions=*/false))
164 return true;
165 }
166 return false;
167}
168
169/// Transform arbitrary file name into an object-like file name.
170static std::string makeObjFileName(StringRef FileName) {
171 SmallString<128> ObjFileName(FileName);
172 llvm::sys::path::replace_extension(path&: ObjFileName, extension: "o");
173 return std::string(ObjFileName);
174}
175
176/// Deduce the dependency target based on the output file and input files.
177static std::string
178deduceDepTarget(const std::string &OutputFile,
179 const SmallVectorImpl<FrontendInputFile> &InputFiles) {
180 if (OutputFile != "-")
181 return OutputFile;
182
183 if (InputFiles.empty() || !InputFiles.front().isFile())
184 return "clang-scan-deps\\ dependency";
185
186 return makeObjFileName(FileName: InputFiles.front().getFile());
187}
188
189/// Sanitize diagnostic options for dependency scan.
190static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
191 // Don't print 'X warnings and Y errors generated'.
192 DiagOpts.ShowCarets = false;
193 // Don't write out diagnostic file.
194 DiagOpts.DiagnosticSerializationFile.clear();
195 // Don't emit warnings except for scanning specific warnings.
196 // TODO: It would be useful to add a more principled way to ignore all
197 // warnings that come from source code. The issue is that we need to
198 // ignore warnings that could be surpressed by
199 // `#pragma clang diagnostic`, while still allowing some scanning
200 // warnings for things we're not ready to turn into errors yet.
201 // See `test/ClangScanDeps/diagnostic-pragmas.c` for an example.
202 llvm::erase_if(C&: DiagOpts.Warnings, P: [](StringRef Warning) {
203 return llvm::StringSwitch<bool>(Warning)
204 .Cases(S0: "pch-vfs-diff", S1: "error=pch-vfs-diff", Value: false)
205 .StartsWith(S: "no-error=", Value: false)
206 .Default(Value: true);
207 });
208}
209
210// Clang implements -D and -U by splatting text into a predefines buffer. This
211// allows constructs such as `-DFඞ=3 "-D F\u{0D9E} 4 3 2”` to be accepted and
212// define the same macro, or adding C++ style comments before the macro name.
213//
214// This function checks that the first non-space characters in the macro
215// obviously form an identifier that can be uniqued on without lexing. Failing
216// to do this could lead to changing the final definition of a macro.
217//
218// We could set up a preprocessor and actually lex the name, but that's very
219// heavyweight for a situation that will almost never happen in practice.
220static std::optional<StringRef> getSimpleMacroName(StringRef Macro) {
221 StringRef Name = Macro.split(Separator: "=").first.ltrim(Chars: " \t");
222 std::size_t I = 0;
223
224 auto FinishName = [&]() -> std::optional<StringRef> {
225 StringRef SimpleName = Name.slice(Start: 0, End: I);
226 if (SimpleName.empty())
227 return std::nullopt;
228 return SimpleName;
229 };
230
231 for (; I != Name.size(); ++I) {
232 switch (Name[I]) {
233 case '(': // Start of macro parameter list
234 case ' ': // End of macro name
235 case '\t':
236 return FinishName();
237 case '_':
238 continue;
239 default:
240 if (llvm::isAlnum(C: Name[I]))
241 continue;
242 return std::nullopt;
243 }
244 }
245 return FinishName();
246}
247
248static void canonicalizeDefines(PreprocessorOptions &PPOpts) {
249 using MacroOpt = std::pair<StringRef, std::size_t>;
250 std::vector<MacroOpt> SimpleNames;
251 SimpleNames.reserve(n: PPOpts.Macros.size());
252 std::size_t Index = 0;
253 for (const auto &M : PPOpts.Macros) {
254 auto SName = getSimpleMacroName(Macro: M.first);
255 // Skip optimizing if we can't guarantee we can preserve relative order.
256 if (!SName)
257 return;
258 SimpleNames.emplace_back(args&: *SName, args&: Index);
259 ++Index;
260 }
261
262 llvm::stable_sort(Range&: SimpleNames, C: [](const MacroOpt &A, const MacroOpt &B) {
263 return A.first < B.first;
264 });
265 // Keep the last instance of each macro name by going in reverse
266 auto NewEnd = std::unique(
267 first: SimpleNames.rbegin(), last: SimpleNames.rend(),
268 binary_pred: [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; });
269 SimpleNames.erase(first: SimpleNames.begin(), last: NewEnd.base());
270
271 // Apply permutation.
272 decltype(PPOpts.Macros) NewMacros;
273 NewMacros.reserve(n: SimpleNames.size());
274 for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) {
275 std::size_t OriginalIndex = SimpleNames[I].second;
276 // We still emit undefines here as they may be undefining a predefined macro
277 NewMacros.push_back(x: std::move(PPOpts.Macros[OriginalIndex]));
278 }
279 std::swap(x&: PPOpts.Macros, y&: NewMacros);
280}
281
282/// A clang tool that runs the preprocessor in a mode that's optimized for
283/// dependency scanning for the given compiler invocation.
284class DependencyScanningAction : public tooling::ToolAction {
285public:
286 DependencyScanningAction(
287 StringRef WorkingDirectory, DependencyConsumer &Consumer,
288 DependencyActionController &Controller,
289 llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
290 ScanningOutputFormat Format, ScanningOptimizations OptimizeArgs,
291 bool EagerLoadModules, bool DisableFree,
292 std::optional<StringRef> ModuleName = std::nullopt)
293 : WorkingDirectory(WorkingDirectory), Consumer(Consumer),
294 Controller(Controller), DepFS(std::move(DepFS)), Format(Format),
295 OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules),
296 DisableFree(DisableFree), ModuleName(ModuleName) {}
297
298 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
299 FileManager *DriverFileMgr,
300 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
301 DiagnosticConsumer *DiagConsumer) override {
302 // Make a deep copy of the original Clang invocation.
303 CompilerInvocation OriginalInvocation(*Invocation);
304 // Restore the value of DisableFree, which may be modified by Tooling.
305 OriginalInvocation.getFrontendOpts().DisableFree = DisableFree;
306 if (any(Val: OptimizeArgs & ScanningOptimizations::Macros))
307 canonicalizeDefines(PPOpts&: OriginalInvocation.getPreprocessorOpts());
308
309 if (Scanned) {
310 // Scanning runs once for the first -cc1 invocation in a chain of driver
311 // jobs. For any dependent jobs, reuse the scanning result and just
312 // update the LastCC1Arguments to correspond to the new invocation.
313 // FIXME: to support multi-arch builds, each arch requires a separate scan
314 setLastCC1Arguments(std::move(OriginalInvocation));
315 return true;
316 }
317
318 Scanned = true;
319
320 // Create a compiler instance to handle the actual work.
321 ScanInstanceStorage.emplace(args: std::move(PCHContainerOps));
322 CompilerInstance &ScanInstance = *ScanInstanceStorage;
323 ScanInstance.setInvocation(std::move(Invocation));
324
325 // Create the compiler's actual diagnostics engine.
326 sanitizeDiagOpts(DiagOpts&: ScanInstance.getDiagnosticOpts());
327 ScanInstance.createDiagnostics(Client: DiagConsumer, /*ShouldOwnClient=*/false);
328 if (!ScanInstance.hasDiagnostics())
329 return false;
330
331 // Some DiagnosticConsumers require that finish() is called.
332 auto DiagConsumerFinisher =
333 llvm::make_scope_exit(F: [DiagConsumer]() { DiagConsumer->finish(); });
334
335 ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =
336 true;
337
338 ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
339 ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
340 ScanInstance.getFrontendOpts().ModulesShareFileManager = false;
341 ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
342 ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage =
343 any(Val: OptimizeArgs & ScanningOptimizations::VFS);
344
345 // Support for virtual file system overlays.
346 auto FS = createVFSFromCompilerInvocation(
347 CI: ScanInstance.getInvocation(), Diags&: ScanInstance.getDiagnostics(),
348 BaseFS: DriverFileMgr->getVirtualFileSystemPtr());
349
350 // Create a new FileManager to match the invocation's FileSystemOptions.
351 auto *FileMgr = ScanInstance.createFileManager(VFS: FS);
352 ScanInstance.createSourceManager(FileMgr&: *FileMgr);
353
354 // Store the list of prebuilt module files into header search options. This
355 // will prevent the implicit build to create duplicate modules and will
356 // force reuse of the existing prebuilt module files instead.
357 PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
358 if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
359 if (visitPrebuiltModule(
360 PrebuiltModuleFilename: ScanInstance.getPreprocessorOpts().ImplicitPCHInclude,
361 CI&: ScanInstance,
362 ModuleFiles&: ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
363 PrebuiltModuleVFSMap, Diags&: ScanInstance.getDiagnostics()))
364 return false;
365
366 // Use the dependency scanning optimized file system if requested to do so.
367 if (DepFS)
368 ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
369 [LocalDepFS = DepFS](FileEntryRef File)
370 -> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
371 if (llvm::ErrorOr<EntryRef> Entry =
372 LocalDepFS->getOrCreateFileSystemEntry(Filename: File.getName()))
373 if (LocalDepFS->ensureDirectiveTokensArePopulated(Entry: *Entry))
374 return Entry->getDirectiveTokens();
375 return std::nullopt;
376 };
377
378 // Create the dependency collector that will collect the produced
379 // dependencies.
380 //
381 // This also moves the existing dependency output options from the
382 // invocation to the collector. The options in the invocation are reset,
383 // which ensures that the compiler won't create new dependency collectors,
384 // and thus won't write out the extra '.d' files to disk.
385 auto Opts = std::make_unique<DependencyOutputOptions>();
386 std::swap(a&: *Opts, b&: ScanInstance.getInvocation().getDependencyOutputOpts());
387 // We need at least one -MT equivalent for the generator of make dependency
388 // files to work.
389 if (Opts->Targets.empty())
390 Opts->Targets = {
391 deduceDepTarget(OutputFile: ScanInstance.getFrontendOpts().OutputFile,
392 InputFiles: ScanInstance.getFrontendOpts().Inputs)};
393 Opts->IncludeSystemHeaders = true;
394
395 switch (Format) {
396 case ScanningOutputFormat::Make:
397 ScanInstance.addDependencyCollector(
398 Listener: std::make_shared<DependencyConsumerForwarder>(
399 args: std::move(Opts), args&: WorkingDirectory, args&: Consumer));
400 break;
401 case ScanningOutputFormat::P1689:
402 case ScanningOutputFormat::Full:
403 MDC = std::make_shared<ModuleDepCollector>(
404 args: std::move(Opts), args&: ScanInstance, args&: Consumer, args&: Controller,
405 args&: OriginalInvocation, args: std::move(PrebuiltModuleVFSMap), args&: OptimizeArgs,
406 args&: EagerLoadModules, args: Format == ScanningOutputFormat::P1689);
407 ScanInstance.addDependencyCollector(Listener: MDC);
408 break;
409 }
410
411 // Consider different header search and diagnostic options to create
412 // different modules. This avoids the unsound aliasing of module PCMs.
413 //
414 // TODO: Implement diagnostic bucketing to reduce the impact of strict
415 // context hashing.
416 ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
417 ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
418 ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;
419 ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings =
420 true;
421
422 // Avoid some checks and module map parsing when loading PCM files.
423 ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
424
425 std::unique_ptr<FrontendAction> Action;
426
427 if (ModuleName)
428 Action = std::make_unique<GetDependenciesByModuleNameAction>(args&: *ModuleName);
429 else
430 Action = std::make_unique<ReadPCHAndPreprocessAction>();
431
432 if (ScanInstance.getDiagnostics().hasErrorOccurred())
433 return false;
434
435 // Each action is responsible for calling finish.
436 DiagConsumerFinisher.release();
437 const bool Result = ScanInstance.ExecuteAction(Act&: *Action);
438
439 if (Result)
440 setLastCC1Arguments(std::move(OriginalInvocation));
441
442 return Result;
443 }
444
445 bool hasScanned() const { return Scanned; }
446
447 /// Take the cc1 arguments corresponding to the most recent invocation used
448 /// with this action. Any modifications implied by the discovered dependencies
449 /// will have already been applied.
450 std::vector<std::string> takeLastCC1Arguments() {
451 std::vector<std::string> Result;
452 std::swap(x&: Result, y&: LastCC1Arguments); // Reset LastCC1Arguments to empty.
453 return Result;
454 }
455
456private:
457 void setLastCC1Arguments(CompilerInvocation &&CI) {
458 if (MDC)
459 MDC->applyDiscoveredDependencies(CI);
460 LastCC1Arguments = CI.getCC1CommandLine();
461 }
462
463private:
464 StringRef WorkingDirectory;
465 DependencyConsumer &Consumer;
466 DependencyActionController &Controller;
467 llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
468 ScanningOutputFormat Format;
469 ScanningOptimizations OptimizeArgs;
470 bool EagerLoadModules;
471 bool DisableFree;
472 std::optional<StringRef> ModuleName;
473 std::optional<CompilerInstance> ScanInstanceStorage;
474 std::shared_ptr<ModuleDepCollector> MDC;
475 std::vector<std::string> LastCC1Arguments;
476 bool Scanned = false;
477};
478
479} // end anonymous namespace
480
481DependencyScanningWorker::DependencyScanningWorker(
482 DependencyScanningService &Service,
483 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
484 : Format(Service.getFormat()), OptimizeArgs(Service.getOptimizeArgs()),
485 EagerLoadModules(Service.shouldEagerLoadModules()) {
486 PCHContainerOps = std::make_shared<PCHContainerOperations>();
487 // We need to read object files from PCH built outside the scanner.
488 PCHContainerOps->registerReader(
489 Reader: std::make_unique<ObjectFilePCHContainerReader>());
490 // The scanner itself writes only raw ast files.
491 PCHContainerOps->registerWriter(Writer: std::make_unique<RawPCHContainerWriter>());
492
493 switch (Service.getMode()) {
494 case ScanningMode::DependencyDirectivesScan:
495 DepFS =
496 new DependencyScanningWorkerFilesystem(Service.getSharedCache(), FS);
497 BaseFS = DepFS;
498 break;
499 case ScanningMode::CanonicalPreprocessing:
500 DepFS = nullptr;
501 BaseFS = FS;
502 break;
503 }
504}
505
506llvm::Error DependencyScanningWorker::computeDependencies(
507 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
508 DependencyConsumer &Consumer, DependencyActionController &Controller,
509 std::optional<StringRef> ModuleName) {
510 std::vector<const char *> CLI;
511 for (const std::string &Arg : CommandLine)
512 CLI.push_back(x: Arg.c_str());
513 auto DiagOpts = CreateAndPopulateDiagOpts(Argv: CLI);
514 sanitizeDiagOpts(DiagOpts&: *DiagOpts);
515
516 // Capture the emitted diagnostics and report them to the client
517 // in the case of a failure.
518 std::string DiagnosticOutput;
519 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
520 TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
521
522 if (computeDependencies(WorkingDirectory, CommandLine, DepConsumer&: Consumer, Controller,
523 DiagConsumer&: DiagPrinter, ModuleName))
524 return llvm::Error::success();
525 return llvm::make_error<llvm::StringError>(Args&: DiagnosticsOS.str(),
526 Args: llvm::inconvertibleErrorCode());
527}
528
529static bool forEachDriverJob(
530 ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, FileManager &FM,
531 llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
532 SmallVector<const char *, 256> Argv;
533 Argv.reserve(N: ArgStrs.size());
534 for (const std::string &Arg : ArgStrs)
535 Argv.push_back(Elt: Arg.c_str());
536
537 llvm::vfs::FileSystem *FS = &FM.getVirtualFileSystem();
538
539 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
540 args&: Argv[0], args: llvm::sys::getDefaultTargetTriple(), args&: Diags,
541 args: "clang LLVM compiler", args&: FS);
542 Driver->setTitle("clang_based_tool");
543
544 llvm::BumpPtrAllocator Alloc;
545 bool CLMode = driver::IsClangCL(
546 DriverMode: driver::getDriverMode(ProgName: Argv[0], Args: ArrayRef(Argv).slice(N: 1)));
547
548 if (llvm::Error E = driver::expandResponseFiles(Args&: Argv, ClangCLMode: CLMode, Alloc, FS)) {
549 Diags.Report(diag::err_drv_expand_response_file)
550 << llvm::toString(std::move(E));
551 return false;
552 }
553
554 const std::unique_ptr<driver::Compilation> Compilation(
555 Driver->BuildCompilation(Args: llvm::ArrayRef(Argv)));
556 if (!Compilation)
557 return false;
558
559 if (Compilation->containsError())
560 return false;
561
562 for (const driver::Command &Job : Compilation->getJobs()) {
563 if (!Callback(Job))
564 return false;
565 }
566 return true;
567}
568
569static bool createAndRunToolInvocation(
570 std::vector<std::string> CommandLine, DependencyScanningAction &Action,
571 FileManager &FM,
572 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
573 DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {
574
575 // Save executable path before providing CommandLine to ToolInvocation
576 std::string Executable = CommandLine[0];
577 ToolInvocation Invocation(std::move(CommandLine), &Action, &FM,
578 PCHContainerOps);
579 Invocation.setDiagnosticConsumer(Diags.getClient());
580 Invocation.setDiagnosticOptions(&Diags.getDiagnosticOptions());
581 if (!Invocation.run())
582 return false;
583
584 std::vector<std::string> Args = Action.takeLastCC1Arguments();
585 Consumer.handleBuildCommand(Cmd: {.Executable: std::move(Executable), .Arguments: std::move(Args)});
586 return true;
587}
588
589bool DependencyScanningWorker::computeDependencies(
590 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
591 DependencyConsumer &Consumer, DependencyActionController &Controller,
592 DiagnosticConsumer &DC, std::optional<StringRef> ModuleName) {
593 // Reset what might have been modified in the previous worker invocation.
594 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
595
596 std::optional<std::vector<std::string>> ModifiedCommandLine;
597 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
598
599 // If we're scanning based on a module name alone, we don't expect the client
600 // to provide us with an input file. However, the driver really wants to have
601 // one. Let's just make it up to make the driver happy.
602 if (ModuleName) {
603 auto OverlayFS =
604 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(A&: BaseFS);
605 auto InMemoryFS =
606 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
607 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
608 OverlayFS->pushOverlay(FS: InMemoryFS);
609 ModifiedFS = OverlayFS;
610
611 SmallString<128> FakeInputPath;
612 // TODO: We should retry the creation if the path already exists.
613 llvm::sys::fs::createUniquePath(Model: *ModuleName + "-%%%%%%%%.input",
614 ResultPath&: FakeInputPath,
615 /*MakeAbsolute=*/false);
616 InMemoryFS->addFile(Path: FakeInputPath, ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: ""));
617
618 ModifiedCommandLine = CommandLine;
619 ModifiedCommandLine->emplace_back(args&: FakeInputPath);
620 }
621
622 const std::vector<std::string> &FinalCommandLine =
623 ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
624 auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
625
626 auto FileMgr =
627 llvm::makeIntrusiveRefCnt<FileManager>(A: FileSystemOptions{}, A&: FinalFS);
628
629 std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(), nullptr);
630 llvm::transform(Range: FinalCommandLine, d_first: FinalCCommandLine.begin(),
631 F: [](const std::string &Str) { return Str.c_str(); });
632
633 auto DiagOpts = CreateAndPopulateDiagOpts(Argv: FinalCCommandLine);
634 sanitizeDiagOpts(DiagOpts&: *DiagOpts);
635 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
636 CompilerInstance::createDiagnostics(Opts: DiagOpts.release(), Client: &DC,
637 /*ShouldOwnClient=*/false);
638
639 // Although `Diagnostics` are used only for command-line parsing, the
640 // custom `DiagConsumer` might expect a `SourceManager` to be present.
641 SourceManager SrcMgr(*Diags, *FileMgr);
642 Diags->setSourceManager(&SrcMgr);
643 // DisableFree is modified by Tooling for running
644 // in-process; preserve the original value, which is
645 // always true for a driver invocation.
646 bool DisableFree = true;
647 DependencyScanningAction Action(WorkingDirectory, Consumer, Controller, DepFS,
648 Format, OptimizeArgs, EagerLoadModules,
649 DisableFree, ModuleName);
650
651 bool Success = false;
652 if (FinalCommandLine[1] == "-cc1") {
653 Success = createAndRunToolInvocation(CommandLine: FinalCommandLine, Action, FM&: *FileMgr,
654 PCHContainerOps, Diags&: *Diags, Consumer);
655 } else {
656 Success = forEachDriverJob(
657 ArgStrs: FinalCommandLine, Diags&: *Diags, FM&: *FileMgr, Callback: [&](const driver::Command &Cmd) {
658 if (StringRef(Cmd.getCreator().getName()) != "clang") {
659 // Non-clang command. Just pass through to the dependency
660 // consumer.
661 Consumer.handleBuildCommand(
662 Cmd: {.Executable: Cmd.getExecutable(),
663 .Arguments: {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
664 return true;
665 }
666
667 // Insert -cc1 comand line options into Argv
668 std::vector<std::string> Argv;
669 Argv.push_back(x: Cmd.getExecutable());
670 Argv.insert(position: Argv.end(), first: Cmd.getArguments().begin(),
671 last: Cmd.getArguments().end());
672
673 // Create an invocation that uses the underlying file
674 // system to ensure that any file system requests that
675 // are made by the driver do not go through the
676 // dependency scanning filesystem.
677 return createAndRunToolInvocation(CommandLine: std::move(Argv), Action, FM&: *FileMgr,
678 PCHContainerOps, Diags&: *Diags, Consumer);
679 });
680 }
681
682 if (Success && !Action.hasScanned())
683 Diags->Report(diag::err_fe_expected_compiler_job)
684 << llvm::join(FinalCommandLine, " ");
685 return Success && Action.hasScanned();
686}
687
688DependencyActionController::~DependencyActionController() {}
689

source code of clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp