1//===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===//
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/DependencyScanningTool.h"
10#include "clang/Basic/Diagnostic.h"
11#include "clang/Basic/DiagnosticFrontend.h"
12#include "clang/DependencyScanning/DependencyScannerImpl.h"
13#include "clang/Driver/Compilation.h"
14#include "clang/Driver/Driver.h"
15#include "clang/Driver/Tool.h"
16#include "clang/Frontend/FrontendActions.h"
17#include "clang/Frontend/Utils.h"
18#include "clang/Lex/Preprocessor.h"
19#include "llvm/ADT/ScopeExit.h"
20#include "llvm/ADT/SmallVectorExtras.h"
21#include "llvm/ADT/iterator.h"
22#include "llvm/TargetParser/Host.h"
23#include <optional>
24
25using namespace clang;
26using namespace tooling;
27using namespace dependencies;
28
29namespace {
30/// Prints out all of the gathered dependencies into a string.
31class MakeDependencyPrinterConsumer : public DependencyConsumer {
32public:
33 void handleBuildCommand(Command) override {}
34
35 void
36 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
37 this->Opts = std::make_unique<DependencyOutputOptions>(args: Opts);
38 }
39
40 void handleFileDependency(StringRef File) override {
41 Dependencies.push_back(x: std::string(File));
42 }
43
44 // These are ignored for the make format as it can't support the full
45 // set of deps, and handleFileDependency handles enough for implicitly
46 // built modules to work.
47 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {}
48 void handleModuleDependency(ModuleDeps MD) override {}
49 void handleDirectModuleDependency(ModuleID ID) override {}
50 void handleVisibleModule(std::string ModuleName) override {}
51 void handleContextHash(std::string Hash) override {}
52
53 void printDependencies(std::string &S) {
54 assert(Opts && "Handled dependency output options.");
55
56 class DependencyPrinter : public DependencyFileGenerator {
57 public:
58 DependencyPrinter(DependencyOutputOptions &Opts,
59 ArrayRef<std::string> Dependencies)
60 : DependencyFileGenerator(Opts) {
61 for (const auto &Dep : Dependencies)
62 addDependency(Filename: Dep);
63 }
64
65 void printDependencies(std::string &S) {
66 llvm::raw_string_ostream OS(S);
67 outputDependencyFile(OS);
68 }
69 };
70
71 DependencyPrinter Generator(*Opts, Dependencies);
72 Generator.printDependencies(S);
73 }
74
75protected:
76 std::unique_ptr<DependencyOutputOptions> Opts;
77 std::vector<std::string> Dependencies;
78};
79} // anonymous namespace
80
81static std::pair<std::unique_ptr<driver::Driver>,
82 std::unique_ptr<driver::Compilation>>
83buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags,
84 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
85 llvm::BumpPtrAllocator &Alloc) {
86 SmallVector<const char *, 256> Argv;
87 Argv.reserve(N: ArgStrs.size());
88 for (const std::string &Arg : ArgStrs)
89 Argv.push_back(Elt: Arg.c_str());
90
91 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
92 args&: Argv[0], args: llvm::sys::getDefaultTargetTriple(), args&: Diags,
93 args: "clang LLVM compiler", args&: FS);
94 Driver->setTitle("clang_based_tool");
95
96 bool CLMode = driver::IsClangCL(
97 DriverMode: driver::getDriverMode(ProgName: Argv[0], Args: ArrayRef(Argv).slice(N: 1)));
98
99 if (llvm::Error E =
100 driver::expandResponseFiles(Args&: Argv, ClangCLMode: CLMode, Alloc, FS: FS.get())) {
101 Diags.Report(DiagID: diag::err_drv_expand_response_file)
102 << llvm::toString(E: std::move(E));
103 return std::make_pair(x: nullptr, y: nullptr);
104 }
105
106 std::unique_ptr<driver::Compilation> Compilation(
107 Driver->BuildCompilation(Args: Argv));
108 if (!Compilation)
109 return std::make_pair(x: nullptr, y: nullptr);
110
111 if (Compilation->containsError())
112 return std::make_pair(x: nullptr, y: nullptr);
113
114 if (Compilation->getJobs().empty()) {
115 Diags.Report(DiagID: diag::err_fe_expected_compiler_job)
116 << llvm::join(R&: ArgStrs, Separator: " ");
117 return std::make_pair(x: nullptr, y: nullptr);
118 }
119
120 return std::make_pair(x: std::move(Driver), y: std::move(Compilation));
121}
122
123/// Constructs the full frontend command line, including executable, for the
124/// given driver \c Cmd.
125static SmallVector<std::string, 0>
126buildCC1CommandLine(const driver::Command &Cmd) {
127 const auto &Args = Cmd.getArguments();
128 SmallVector<std::string, 0> Out;
129 Out.reserve(N: Args.size() + 1);
130 Out.emplace_back(Args: Cmd.getExecutable());
131 llvm::append_range(C&: Out, R: Args);
132 return Out;
133}
134
135static bool computeDependenciesForDriverCommandLine(
136 DependencyScanningWorker &Worker, StringRef WorkingDirectory,
137 ArrayRef<std::string> CommandLine, DependencyConsumer &Consumer,
138 DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer,
139 IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) {
140 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = nullptr;
141 if (OverlayFS) {
142 FS = OverlayFS;
143 } else {
144 FS = &Worker.getVFS();
145 FS->setCurrentWorkingDirectory(WorkingDirectory);
146 }
147
148 // Compilation holds a non-owning a reference to the Driver, hence we need to
149 // keep the Driver alive when we use Compilation. Arguments to commands may be
150 // owned by Alloc when expanded from response files.
151 llvm::BumpPtrAllocator Alloc;
152 auto DiagEngineWithDiagOpts =
153 DiagnosticsEngineWithDiagOpts(CommandLine, FS, DiagConsumer);
154 const auto [Driver, Compilation] = buildCompilation(
155 ArgStrs: CommandLine, Diags&: *DiagEngineWithDiagOpts.DiagEngine, FS, Alloc);
156 if (!Compilation)
157 return false;
158
159 SmallVector<SmallVector<std::string, 0>> FrontendCommandLines;
160 for (const auto &Cmd : Compilation->getJobs())
161 FrontendCommandLines.push_back(Elt: buildCC1CommandLine(Cmd));
162 SmallVector<ArrayRef<std::string>> FrontendCommandLinesView(
163 FrontendCommandLines.begin(), FrontendCommandLines.end());
164
165 return Worker.computeDependencies(WorkingDirectory, CommandLines: FrontendCommandLinesView,
166 DepConsumer&: Consumer, Controller, DiagConsumer,
167 OverlayFS);
168}
169
170static llvm::Error makeErrorFromDiagnosticsOS(
171 TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS) {
172 return llvm::make_error<llvm::StringError>(
173 Args&: DiagPrinterWithOS.DiagnosticsOS.str(), Args: llvm::inconvertibleErrorCode());
174}
175
176bool tooling::computeDependencies(
177 DependencyScanningWorker &Worker, StringRef WorkingDirectory,
178 ArrayRef<std::string> CommandLine, DependencyConsumer &Consumer,
179 DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer,
180 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) {
181 const auto IsCC1Input = (CommandLine.size() >= 2 && CommandLine[1] == "-cc1");
182 return IsCC1Input ? Worker.computeDependencies(WorkingDirectory, CommandLine,
183 DepConsumer&: Consumer, Controller,
184 DiagConsumer, OverlayFS)
185 : computeDependenciesForDriverCommandLine(
186 Worker, WorkingDirectory, CommandLine, Consumer,
187 Controller, DiagConsumer, OverlayFS);
188}
189
190std::optional<std::string>
191DependencyScanningTool::getDependencyFile(ArrayRef<std::string> CommandLine,
192 StringRef CWD,
193 DiagnosticConsumer &DiagConsumer) {
194 MakeDependencyPrinterConsumer DepConsumer;
195 CallbackActionController Controller(nullptr);
196 if (!computeDependencies(Worker, WorkingDirectory: CWD, CommandLine, Consumer&: DepConsumer, Controller,
197 DiagConsumer))
198 return std::nullopt;
199 std::string Output;
200 DepConsumer.printDependencies(S&: Output);
201 return Output;
202}
203
204std::optional<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
205 const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput,
206 std::string &MakeformatOutputPath, DiagnosticConsumer &DiagConsumer) {
207 class P1689ModuleDependencyPrinterConsumer
208 : public MakeDependencyPrinterConsumer {
209 public:
210 P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
211 const CompileCommand &Command)
212 : Filename(Command.Filename), Rule(Rule) {
213 Rule.PrimaryOutput = Command.Output;
214 }
215
216 void handleProvidedAndRequiredStdCXXModules(
217 std::optional<P1689ModuleInfo> Provided,
218 std::vector<P1689ModuleInfo> Requires) override {
219 Rule.Provides = std::move(Provided);
220 if (Rule.Provides)
221 Rule.Provides->SourcePath = Filename.str();
222 Rule.Requires = std::move(Requires);
223 }
224
225 StringRef getMakeFormatDependencyOutputPath() {
226 if (Opts->OutputFormat != DependencyOutputFormat::Make)
227 return {};
228 return Opts->OutputFile;
229 }
230
231 private:
232 StringRef Filename;
233 P1689Rule &Rule;
234 };
235
236 class P1689ActionController : public DependencyActionController {
237 public:
238 // The lookupModuleOutput is for clang modules. P1689 format don't need it.
239 std::string lookupModuleOutput(const ModuleDeps &,
240 ModuleOutputKind Kind) override {
241 return "";
242 }
243
244 std::unique_ptr<DependencyActionController> clone() const override {
245 return std::make_unique<P1689ActionController>();
246 }
247 };
248
249 P1689Rule Rule;
250 P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command);
251 P1689ActionController Controller;
252 if (!computeDependencies(Worker, WorkingDirectory: CWD, CommandLine: Command.CommandLine, Consumer,
253 Controller, DiagConsumer))
254 return std::nullopt;
255
256 MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
257 if (!MakeformatOutputPath.empty())
258 Consumer.printDependencies(S&: MakeformatOutput);
259 return Rule;
260}
261
262static std::pair<IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>,
263 std::vector<std::string>>
264initVFSForTUBufferScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
265 ArrayRef<std::string> CommandLine,
266 StringRef WorkingDirectory,
267 llvm::MemoryBufferRef TUBuffer) {
268 // Reset what might have been modified in the previous worker invocation.
269 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
270
271 auto OverlayFS =
272 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(A&: BaseFS);
273 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
274 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
275 auto InputPath = TUBuffer.getBufferIdentifier();
276 InMemoryFS->addFile(
277 Path: InputPath, ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBufferCopy(InputData: TUBuffer.getBuffer()));
278 IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
279
280 OverlayFS->pushOverlay(FS: InMemoryOverlay);
281 std::vector<std::string> ModifiedCommandLine(CommandLine);
282 ModifiedCommandLine.emplace_back(args&: InputPath);
283
284 return std::make_pair(x&: OverlayFS, y&: ModifiedCommandLine);
285}
286
287static std::pair<IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>,
288 std::vector<std::string>>
289initVFSForByNameScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
290 ArrayRef<std::string> CommandLine,
291 StringRef WorkingDirectory) {
292 // Reset what might have been modified in the previous worker invocation.
293 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
294
295 // If we're scanning based on a module name alone, we don't expect the client
296 // to provide us with an input file. However, the driver really wants to have
297 // one. Let's just make it up to make the driver happy.
298 auto OverlayFS =
299 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(A&: BaseFS);
300 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
301 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
302 StringRef FakeInputPath("module-include.input");
303 // The fake input buffer is read-only, and it is used to produce
304 // unique source locations for the diagnostics. Therefore sharing
305 // this global buffer across threads is ok.
306 static const std::string FakeInput(
307 clang::tooling::CompilerInstanceWithContext::MaxNumOfQueries, ' ');
308 InMemoryFS->addFile(Path: FakeInputPath, ModificationTime: 0,
309 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: FakeInput));
310 IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
311 OverlayFS->pushOverlay(FS: InMemoryOverlay);
312
313 std::vector<std::string> ModifiedCommandLine(CommandLine);
314 ModifiedCommandLine.emplace_back(args&: FakeInputPath);
315
316 return std::make_pair(x&: OverlayFS, y&: ModifiedCommandLine);
317}
318
319std::optional<TranslationUnitDeps>
320DependencyScanningTool::getTranslationUnitDependencies(
321 ArrayRef<std::string> CommandLine, StringRef CWD,
322 DiagnosticConsumer &DiagConsumer,
323 const llvm::DenseSet<ModuleID> &AlreadySeen,
324 LookupModuleOutputCallback LookupModuleOutput,
325 std::optional<llvm::MemoryBufferRef> TUBuffer) {
326 FullDependencyConsumer Consumer(AlreadySeen);
327 CallbackActionController Controller(LookupModuleOutput);
328
329 // If we are scanning from a TUBuffer, create an overlay filesystem with the
330 // input as an in-memory file and add it to the command line.
331 IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS = nullptr;
332 std::vector<std::string> CommandLineWithTUBufferInput;
333 if (TUBuffer) {
334 std::tie(args&: OverlayFS, args&: CommandLineWithTUBufferInput) =
335 initVFSForTUBufferScanning(BaseFS: &Worker.getVFS(), CommandLine, WorkingDirectory: CWD,
336 TUBuffer: *TUBuffer);
337 CommandLine = CommandLineWithTUBufferInput;
338 }
339
340 if (!computeDependencies(Worker, WorkingDirectory: CWD, CommandLine, Consumer, Controller,
341 DiagConsumer, OverlayFS))
342 return std::nullopt;
343 return Consumer.takeTranslationUnitDeps();
344}
345
346llvm::Expected<TranslationUnitDeps>
347DependencyScanningTool::getModuleDependencies(
348 StringRef ModuleName, ArrayRef<std::string> CommandLine, StringRef CWD,
349 const llvm::DenseSet<ModuleID> &AlreadySeen,
350 LookupModuleOutputCallback LookupModuleOutput) {
351 auto MaybeCIWithContext = CompilerInstanceWithContext::initializeOrError(
352 Tool&: *this, CWD, CommandLine, LookupModuleOutput);
353 if (auto Error = MaybeCIWithContext.takeError())
354 return Error;
355
356 return MaybeCIWithContext->computeDependenciesByNameOrError(
357 ModuleName, AlreadySeen, LookupModuleOutput);
358}
359
360static std::optional<SmallVector<std::string, 0>> getFirstCC1CommandLine(
361 ArrayRef<std::string> CommandLine, DiagnosticsEngine &Diags,
362 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) {
363 // Compilation holds a non-owning a reference to the Driver, hence we need to
364 // keep the Driver alive when we use Compilation. Arguments to commands may be
365 // owned by Alloc when expanded from response files.
366 llvm::BumpPtrAllocator Alloc;
367 const auto [Driver, Compilation] =
368 buildCompilation(ArgStrs: CommandLine, Diags, FS: OverlayFS, Alloc);
369 if (!Compilation)
370 return std::nullopt;
371
372 const auto IsClangCmd = [](const driver::Command &Cmd) {
373 return StringRef(Cmd.getCreator().getName()) == "clang";
374 };
375
376 const auto &Jobs = Compilation->getJobs();
377 if (const auto It = llvm::find_if(Range: Jobs, P: IsClangCmd); It != Jobs.end())
378 return buildCC1CommandLine(Cmd: *It);
379 return std::nullopt;
380}
381
382std::optional<CompilerInstanceWithContext>
383CompilerInstanceWithContext::initializeFromCommandline(
384 DependencyScanningTool &Tool, StringRef CWD,
385 ArrayRef<std::string> CommandLine, DependencyActionController &Controller,
386 DiagnosticConsumer &DC) {
387 auto [OverlayFS, ModifiedCommandLine] =
388 initVFSForByNameScanning(BaseFS: &Tool.Worker.getVFS(), CommandLine, WorkingDirectory: CWD);
389 auto DiagEngineWithCmdAndOpts =
390 std::make_unique<DiagnosticsEngineWithDiagOpts>(args&: ModifiedCommandLine,
391 args&: OverlayFS, args&: DC);
392
393 if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
394 // The input command line is already a -cc1 invocation; initialize the
395 // compiler instance directly from it.
396 CompilerInstanceWithContext CIWithContext(Tool.Worker, CWD, CommandLine);
397 if (!CIWithContext.initialize(
398 Controller, DiagEngineWithDiagOpts: std::move(DiagEngineWithCmdAndOpts), OverlayFS))
399 return std::nullopt;
400 return std::move(CIWithContext);
401 }
402
403 // The input command line is either a driver-style command line, or
404 // ill-formed. In this case, we will first call the Driver to build a -cc1
405 // command line for this compilation or diagnose any ill-formed input.
406 const auto MaybeFirstCC1 = getFirstCC1CommandLine(
407 CommandLine: ModifiedCommandLine, Diags&: *DiagEngineWithCmdAndOpts->DiagEngine, OverlayFS);
408 if (!MaybeFirstCC1)
409 return std::nullopt;
410
411 std::vector<std::string> CC1CommandLine(MaybeFirstCC1->begin(),
412 MaybeFirstCC1->end());
413 CompilerInstanceWithContext CIWithContext(Tool.Worker, CWD,
414 std::move(CC1CommandLine));
415 if (!CIWithContext.initialize(Controller, DiagEngineWithDiagOpts: std::move(DiagEngineWithCmdAndOpts),
416 OverlayFS))
417 return std::nullopt;
418 return std::move(CIWithContext);
419}
420
421llvm::Expected<CompilerInstanceWithContext>
422CompilerInstanceWithContext::initializeOrError(
423 DependencyScanningTool &Tool, StringRef CWD,
424 ArrayRef<std::string> CommandLine,
425 LookupModuleOutputCallback LookupModuleOutput) {
426 // It might seem wasteful to create fresh controller just for initializing the
427 // compiler instance, but repeated calls to computeDependenciesByNameOrError()
428 // do that as well, so this gets amortized.
429 CallbackActionController Controller(LookupModuleOutput);
430 auto DiagPrinterWithOS =
431 std::make_unique<TextDiagnosticsPrinterWithOutput>(args&: CommandLine);
432
433 auto Result = initializeFromCommandline(Tool, CWD, CommandLine, Controller,
434 DC&: DiagPrinterWithOS->DiagPrinter);
435 if (Result) {
436 Result->DiagPrinterWithOS = std::move(DiagPrinterWithOS);
437 return std::move(*Result);
438 }
439 return makeErrorFromDiagnosticsOS(DiagPrinterWithOS&: *DiagPrinterWithOS);
440}
441
442llvm::Expected<TranslationUnitDeps>
443CompilerInstanceWithContext::computeDependenciesByNameOrError(
444 StringRef ModuleName, const llvm::DenseSet<ModuleID> &AlreadySeen,
445 LookupModuleOutputCallback LookupModuleOutput) {
446 FullDependencyConsumer Consumer(AlreadySeen);
447 CallbackActionController Controller(LookupModuleOutput);
448 // We need to clear the DiagnosticOutput so that each by-name lookup
449 // has a clean diagnostics buffer.
450 DiagPrinterWithOS->DiagnosticOutput.clear();
451 if (computeDependencies(ModuleName, Consumer, Controller))
452 return Consumer.takeTranslationUnitDeps();
453 return makeErrorFromDiagnosticsOS(DiagPrinterWithOS&: *DiagPrinterWithOS);
454}
455
456bool CompilerInstanceWithContext::initialize(
457 DependencyActionController &Controller,
458 std::unique_ptr<DiagnosticsEngineWithDiagOpts> DiagEngineWithDiagOpts,
459 IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) {
460 assert(DiagEngineWithDiagOpts && "Valid diagnostics engine required!");
461 DiagEngineWithCmdAndOpts = std::move(DiagEngineWithDiagOpts);
462 DiagConsumer = DiagEngineWithCmdAndOpts->DiagEngine->getClient();
463
464#ifndef NDEBUG
465 assert(OverlayFS && "OverlayFS required!");
466 bool SawDepFS = false;
467 OverlayFS->visit([&](llvm::vfs::FileSystem &VFS) {
468 SawDepFS |= &VFS == Worker.DepFS.get();
469 });
470 assert(SawDepFS && "OverlayFS not based on DepFS");
471#endif
472
473 OriginalInvocation = createCompilerInvocation(
474 CommandLine, Diags&: *DiagEngineWithCmdAndOpts->DiagEngine);
475 if (!OriginalInvocation) {
476 DiagEngineWithCmdAndOpts->DiagEngine->Report(
477 DiagID: diag::err_fe_expected_compiler_job)
478 << llvm::join(R&: CommandLine, Separator: " ");
479 return false;
480 }
481
482 if (any(Val: Worker.Service.getOpts().OptimizeArgs &
483 ScanningOptimizations::Macros))
484 canonicalizeDefines(PPOpts&: OriginalInvocation->getPreprocessorOpts());
485
486 // Create the CompilerInstance.
487 std::shared_ptr<ModuleCache> ModCache =
488 makeInProcessModuleCache(Entries&: Worker.Service.getModuleCacheEntries());
489 CIPtr = std::make_unique<CompilerInstance>(
490 args: createScanCompilerInvocation(Invocation: *OriginalInvocation, Service: Worker.Service,
491 Controller),
492 args&: Worker.PCHContainerOps, args: std::move(ModCache));
493 auto &CI = *CIPtr;
494
495 initializeScanCompilerInstance(
496 ScanInstance&: CI, FS: OverlayFS, DiagConsumer: DiagEngineWithCmdAndOpts->DiagEngine->getClient(),
497 Service&: Worker.Service, DepFS: Worker.DepFS);
498
499 StableDirs = getInitialStableDirs(ScanInstance: CI);
500 auto MaybePrebuiltModulesASTMap =
501 computePrebuiltModulesASTMap(ScanInstance&: CI, StableDirs);
502 if (!MaybePrebuiltModulesASTMap)
503 return false;
504
505 PrebuiltModuleASTMap = std::move(*MaybePrebuiltModulesASTMap);
506 OutputOpts = createDependencyOutputOptions(Invocation: *OriginalInvocation);
507
508 // We do not create the target in initializeScanCompilerInstance because
509 // setting it here is unique for by-name lookups. We create the target only
510 // once here, and the information is reused for all computeDependencies calls.
511 // We do not need to call createTarget explicitly if we go through
512 // CompilerInstance::ExecuteAction to perform scanning.
513 CI.createTarget();
514
515 return true;
516}
517
518bool CompilerInstanceWithContext::computeDependencies(
519 StringRef ModuleName, DependencyConsumer &Consumer,
520 DependencyActionController &Controller) {
521 if (SrcLocOffset >= MaxNumOfQueries)
522 llvm::report_fatal_error(reason: "exceeded maximum by-name scans for worker");
523
524 assert(CIPtr && "CIPtr must be initialized before calling this method");
525 auto &CI = *CIPtr;
526
527 // We need to reset the diagnostics, so that the diagnostics issued
528 // during a previous computeDependencies call do not affect the current call.
529 // If we do not reset, we may inherit fatal errors from a previous call.
530 CI.getDiagnostics().Reset();
531
532 // We create this cleanup object because computeDependencies may exit
533 // early with errors.
534 llvm::scope_exit CleanUp([&]() {
535 CI.clearDependencyCollectors();
536 // The preprocessor may not be created at the entry of this method,
537 // but it must have been created when this method returns, whether
538 // there are errors during scanning or not.
539 CI.getPreprocessor().removePPCallbacks();
540 });
541
542 auto MDC = initializeScanInstanceDependencyCollector(
543 ScanInstance&: CI, DepOutputOpts: std::make_unique<DependencyOutputOptions>(args&: *OutputOpts), WorkingDirectory: CWD, Consumer,
544 Service&: Worker.Service,
545 /* The MDC's constructor makes a copy of the OriginalInvocation, so
546 we can pass it in without worrying that it might be changed across
547 invocations of computeDependencies. */
548 Inv&: *OriginalInvocation, Controller, PrebuiltModulesASTMap: PrebuiltModuleASTMap, StableDirs);
549
550 CompilerInvocation ModuleInvocation(*OriginalInvocation);
551 if (!Controller.initialize(ScanInstance&: CI, NewInvocation&: ModuleInvocation))
552 return false;
553
554 if (!SrcLocOffset) {
555 // When SrcLocOffset is zero, we are at the beginning of the fake source
556 // file. In this case, we call BeginSourceFile to initialize.
557 std::unique_ptr<FrontendAction> Action =
558 std::make_unique<PreprocessOnlyAction>();
559 auto *InputFile = CI.getFrontendOpts().Inputs.begin();
560 bool ActionBeginSucceeded = Action->BeginSourceFile(CI, Input: *InputFile);
561 assert(ActionBeginSucceeded && "Action BeginSourceFile must succeed");
562 (void)ActionBeginSucceeded;
563 }
564
565 Preprocessor &PP = CI.getPreprocessor();
566 SourceManager &SM = PP.getSourceManager();
567 FileID MainFileID = SM.getMainFileID();
568 SourceLocation FileStart = SM.getLocForStartOfFile(FID: MainFileID);
569 SourceLocation IDLocation = FileStart.getLocWithOffset(Offset: SrcLocOffset);
570 PPCallbacks *CB = nullptr;
571 if (!SrcLocOffset) {
572 // We need to call EnterSourceFile when SrcLocOffset is zero to initialize
573 // the preprocessor.
574 bool PPFailed = PP.EnterSourceFile(FID: MainFileID, Dir: nullptr, Loc: SourceLocation());
575 assert(!PPFailed && "Preprocess must be able to enter the main file.");
576 (void)PPFailed;
577 CB = MDC->getPPCallbacks();
578 } else {
579 // When SrcLocOffset is non-zero, the preprocessor has already been
580 // initialized through a previous call of computeDependencies. We want to
581 // preserve the PP's state, hence we do not call EnterSourceFile again.
582 MDC->attachToPreprocessor(PP);
583 CB = MDC->getPPCallbacks();
584
585 FileID PrevFID;
586 SrcMgr::CharacteristicKind FileType = SM.getFileCharacteristic(Loc: IDLocation);
587 CB->LexedFileChanged(FID: MainFileID,
588 Reason: PPChainedCallbacks::LexedFileChangeReason::EnterFile,
589 FileType, PrevFID, Loc: IDLocation);
590 }
591
592 // FIXME: Scan modules asynchronously here as well.
593
594 SrcLocOffset++;
595 SmallVector<IdentifierLoc, 2> Path;
596 IdentifierInfo *ModuleID = PP.getIdentifierInfo(Name: ModuleName);
597 Path.emplace_back(Args&: IDLocation, Args&: ModuleID);
598 auto ModResult = CI.loadModule(ImportLoc: IDLocation, Path, Visibility: Module::Hidden, IsInclusionDirective: false);
599
600 assert(CB && "Must have PPCallbacks after module loading");
601 CB->moduleImport(ImportLoc: SourceLocation(), Path, Imported: ModResult);
602 // Note that we are calling the CB's EndOfMainFile function, which
603 // forwards the results to the dependency consumer.
604 // It does not indicate the end of processing the fake file.
605 CB->EndOfMainFile();
606
607 if (!ModResult)
608 return false;
609
610 MDC->applyDiscoveredDependencies(CI&: ModuleInvocation);
611
612 if (!Controller.finalize(ScanInstance&: CI, NewInvocation&: ModuleInvocation))
613 return false;
614
615 Consumer.handleBuildCommand(
616 Cmd: {.Executable: CommandLine[0], .Arguments: ModuleInvocation.getCC1CommandLine()});
617
618 return true;
619}
620