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