clang 23.0.0git
DependencyScanningTool.cpp
Go to the documentation of this file.
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
14#include "clang/Driver/Driver.h"
15#include "clang/Driver/Tool.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>(Opts);
38 }
39
40 void handleFileDependency(StringRef File) override {
41 Dependencies.push_back(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(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>>
85 llvm::BumpPtrAllocator &Alloc) {
87 Argv.reserve(ArgStrs.size());
88 for (const std::string &Arg : ArgStrs)
89 Argv.push_back(Arg.c_str());
90
91 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
92 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
93 "clang LLVM compiler", FS);
94 Driver->setTitle("clang_based_tool");
95
96 bool CLMode = driver::IsClangCL(
97 driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
98
99 if (llvm::Error E =
100 driver::expandResponseFiles(Argv, CLMode, Alloc, FS.get())) {
101 Diags.Report(diag::err_drv_expand_response_file)
102 << llvm::toString(std::move(E));
103 return std::make_pair(nullptr, nullptr);
104 }
105
106 std::unique_ptr<driver::Compilation> Compilation(
107 Driver->BuildCompilation(Argv));
108 if (!Compilation)
109 return std::make_pair(nullptr, nullptr);
110
111 if (Compilation->containsError())
112 return std::make_pair(nullptr, nullptr);
113
114 if (Compilation->getJobs().empty()) {
115 Diags.Report(diag::err_fe_expected_compiler_job)
116 << llvm::join(ArgStrs, " ");
117 return std::make_pair(nullptr, nullptr);
118 }
119
120 return std::make_pair(std::move(Driver), std::move(Compilation));
121}
122
123/// Constructs the full frontend command line, including executable, for the
124/// given driver \c Cmd.
127 const auto &Args = Cmd.getArguments();
129 Out.reserve(Args.size() + 1);
130 Out.emplace_back(Cmd.getExecutable());
131 llvm::append_range(Out, Args);
132 return Out;
133}
134
136 DependencyScanningWorker &Worker, StringRef WorkingDirectory,
137 ArrayRef<std::string> CommandLine, DependencyConsumer &Consumer,
138 DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer,
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 CommandLine, *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(buildCC1CommandLine(Cmd));
162 SmallVector<ArrayRef<std::string>> FrontendCommandLinesView(
163 FrontendCommandLines.begin(), FrontendCommandLines.end());
164
165 return Worker.computeDependencies(WorkingDirectory, FrontendCommandLinesView,
166 Consumer, Controller, DiagConsumer,
167 OverlayFS);
168}
169
170static llvm::Error makeErrorFromDiagnosticsOS(
171 TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS) {
172 return llvm::make_error<llvm::StringError>(
173 DiagPrinterWithOS.DiagnosticsOS.str(), llvm::inconvertibleErrorCode());
174}
175
177 DependencyScanningWorker &Worker, StringRef WorkingDirectory,
178 ArrayRef<std::string> CommandLine, DependencyConsumer &Consumer,
179 DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer,
181 const auto IsCC1Input = (CommandLine.size() >= 2 && CommandLine[1] == "-cc1");
182 return IsCC1Input ? Worker.computeDependencies(WorkingDirectory, CommandLine,
183 Consumer, Controller,
184 DiagConsumer, OverlayFS)
186 Worker, WorkingDirectory, CommandLine, Consumer,
187 Controller, DiagConsumer, OverlayFS);
188}
189
190std::optional<std::string>
192 StringRef CWD,
193 DiagnosticConsumer &DiagConsumer) {
194 MakeDependencyPrinterConsumer DepConsumer;
195 CallbackActionController Controller(nullptr);
196 if (!computeDependencies(Worker, CWD, CommandLine, DepConsumer, Controller,
197 DiagConsumer))
198 return std::nullopt;
199 std::string Output;
200 DepConsumer.printDependencies(Output);
201 return Output;
202}
203
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, CWD, Command.CommandLine, Consumer,
253 Controller, DiagConsumer))
254 return std::nullopt;
255
256 MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
257 if (!MakeformatOutputPath.empty())
258 Consumer.printDependencies(MakeformatOutput);
259 return Rule;
260}
261
262static std::pair<IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>,
263 std::vector<std::string>>
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>(BaseFS);
273 auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
274 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
275 auto InputPath = TUBuffer.getBufferIdentifier();
276 InMemoryFS->addFile(
277 InputPath, 0, llvm::MemoryBuffer::getMemBufferCopy(TUBuffer.getBuffer()));
278 IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
279
280 OverlayFS->pushOverlay(InMemoryOverlay);
281 std::vector<std::string> ModifiedCommandLine(CommandLine);
282 ModifiedCommandLine.emplace_back(InputPath);
283
284 return std::make_pair(OverlayFS, ModifiedCommandLine);
285}
286
287static std::pair<IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>,
288 std::vector<std::string>>
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>(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(
308 InMemoryFS->addFile(FakeInputPath, 0,
309 llvm::MemoryBuffer::getMemBuffer(FakeInput));
310 IntrusiveRefCntPtr<llvm::vfs::FileSystem> InMemoryOverlay = InMemoryFS;
311 OverlayFS->pushOverlay(InMemoryOverlay);
312
313 std::vector<std::string> ModifiedCommandLine(CommandLine);
314 ModifiedCommandLine.emplace_back(FakeInputPath);
315
316 return std::make_pair(OverlayFS, ModifiedCommandLine);
317}
318
319std::optional<TranslationUnitDeps>
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.
332 std::vector<std::string> CommandLineWithTUBufferInput;
333 if (TUBuffer) {
334 std::tie(OverlayFS, CommandLineWithTUBufferInput) =
335 initVFSForTUBufferScanning(&Worker.getVFS(), CommandLine, CWD,
336 *TUBuffer);
337 CommandLine = CommandLineWithTUBufferInput;
338 }
339
340 if (!computeDependencies(Worker, CWD, CommandLine, Consumer, Controller,
341 DiagConsumer, OverlayFS))
342 return std::nullopt;
343 return Consumer.takeTranslationUnitDeps();
344}
345
348 StringRef ModuleName, ArrayRef<std::string> CommandLine, StringRef CWD,
349 const llvm::DenseSet<ModuleID> &AlreadySeen,
350 LookupModuleOutputCallback LookupModuleOutput) {
351 auto MaybeCIWithContext = CompilerInstanceWithContext::initializeOrError(
352 *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,
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(CommandLine, Diags, 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(Jobs, IsClangCmd); It != Jobs.end())
378 return buildCC1CommandLine(*It);
379 return std::nullopt;
380}
381
382std::optional<CompilerInstanceWithContext>
384 DependencyScanningTool &Tool, StringRef CWD,
385 ArrayRef<std::string> CommandLine, DependencyActionController &Controller,
386 DiagnosticConsumer &DC) {
387 auto [OverlayFS, ModifiedCommandLine] =
388 initVFSForByNameScanning(&Tool.Worker.getVFS(), CommandLine, CWD);
389 auto DiagEngineWithCmdAndOpts =
390 std::make_unique<DiagnosticsEngineWithDiagOpts>(ModifiedCommandLine,
391 OverlayFS, 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, 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 ModifiedCommandLine, *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, std::move(DiagEngineWithCmdAndOpts),
416 OverlayFS))
417 return std::nullopt;
418 return std::move(CIWithContext);
419}
420
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>(CommandLine);
432
433 auto Result = initializeFromCommandline(Tool, CWD, CommandLine, Controller,
434 DiagPrinterWithOS->DiagPrinter);
435 if (Result) {
436 Result->DiagPrinterWithOS = std::move(DiagPrinterWithOS);
437 return std::move(*Result);
438 }
439 return makeErrorFromDiagnosticsOS(*DiagPrinterWithOS);
440}
441
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);
454}
455
456bool CompilerInstanceWithContext::initialize(
457 DependencyActionController &Controller,
458 std::unique_ptr<DiagnosticsEngineWithDiagOpts> DiagEngineWithDiagOpts,
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, *DiagEngineWithCmdAndOpts->DiagEngine);
475 if (!OriginalInvocation) {
476 DiagEngineWithCmdAndOpts->DiagEngine->Report(
477 diag::err_fe_expected_compiler_job)
478 << llvm::join(CommandLine, " ");
479 return false;
480 }
481
482 if (any(Worker.Service.getOpts().OptimizeArgs &
483 ScanningOptimizations::Macros))
484 canonicalizeDefines(OriginalInvocation->getPreprocessorOpts());
485
486 // Create the CompilerInstance.
487 std::shared_ptr<ModuleCache> ModCache =
488 makeInProcessModuleCache(Worker.Service.getModuleCacheEntries());
489 CIPtr = std::make_unique<CompilerInstance>(
490 createScanCompilerInvocation(*OriginalInvocation, Worker.Service,
491 Controller),
492 Worker.PCHContainerOps, std::move(ModCache));
493 auto &CI = *CIPtr;
494
496 CI, OverlayFS, DiagEngineWithCmdAndOpts->DiagEngine->getClient(),
497 Worker.Service, Worker.DepFS);
498
499 StableDirs = getInitialStableDirs(CI);
500 auto MaybePrebuiltModulesASTMap =
501 computePrebuiltModulesASTMap(CI, StableDirs);
502 if (!MaybePrebuiltModulesASTMap)
503 return false;
504
505 PrebuiltModuleASTMap = std::move(*MaybePrebuiltModulesASTMap);
506 OutputOpts = createDependencyOutputOptions(*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
519 StringRef ModuleName, DependencyConsumer &Consumer,
520 DependencyActionController &Controller) {
521 if (SrcLocOffset >= MaxNumOfQueries)
522 llvm::report_fatal_error("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
543 CI, std::make_unique<DependencyOutputOptions>(*OutputOpts), CWD, Consumer,
544 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 *OriginalInvocation, Controller, PrebuiltModuleASTMap, StableDirs);
549
550 CompilerInvocation ModuleInvocation(*OriginalInvocation);
551 if (!Controller.initialize(CI, 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, *InputFile);
561 assert(ActionBeginSucceeded && "Action BeginSourceFile must succeed");
562 (void)ActionBeginSucceeded;
563 }
564
565 Preprocessor &PP = CI.getPreprocessor();
567 FileID MainFileID = SM.getMainFileID();
568 SourceLocation FileStart = SM.getLocForStartOfFile(MainFileID);
569 SourceLocation IDLocation = FileStart.getLocWithOffset(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(MainFileID, nullptr, 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(IDLocation);
587 CB->LexedFileChanged(MainFileID,
589 FileType, PrevFID, IDLocation);
590 }
591
592 // FIXME: Scan modules asynchronously here as well.
593
594 SrcLocOffset++;
596 IdentifierInfo *ModuleID = PP.getIdentifierInfo(ModuleName);
597 Path.emplace_back(IDLocation, ModuleID);
598 auto ModResult = CI.loadModule(IDLocation, Path, Module::Hidden, false);
599
600 assert(CB && "Must have PPCallbacks after module loading");
601 CB->moduleImport(SourceLocation(), Path, 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(ModuleInvocation);
611
612 if (!Controller.finalize(CI, ModuleInvocation))
613 return false;
614
615 Consumer.handleBuildCommand(
616 {CommandLine[0], ModuleInvocation.getCC1CommandLine()});
617
618 return true;
619}
Defines the Diagnostic-related interfaces.
static std::pair< std::unique_ptr< driver::Driver >, std::unique_ptr< driver::Compilation > > buildCompilation(ArrayRef< std::string > ArgStrs, DiagnosticsEngine &Diags, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, llvm::BumpPtrAllocator &Alloc)
static std::pair< IntrusiveRefCntPtr< llvm::vfs::OverlayFileSystem >, std::vector< std::string > > initVFSForTUBufferScanning(IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS, ArrayRef< std::string > CommandLine, StringRef WorkingDirectory, llvm::MemoryBufferRef TUBuffer)
static llvm::Error makeErrorFromDiagnosticsOS(TextDiagnosticsPrinterWithOutput &DiagPrinterWithOS)
static std::pair< IntrusiveRefCntPtr< llvm::vfs::OverlayFileSystem >, std::vector< std::string > > initVFSForByNameScanning(IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS, ArrayRef< std::string > CommandLine, StringRef WorkingDirectory)
static SmallVector< std::string, 0 > buildCC1CommandLine(const driver::Command &Cmd)
Constructs the full frontend command line, including executable, for the given driver Cmd.
static bool computeDependenciesForDriverCommandLine(DependencyScanningWorker &Worker, StringRef WorkingDirectory, ArrayRef< std::string > CommandLine, DependencyConsumer &Consumer, DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, IntrusiveRefCntPtr< llvm::vfs::OverlayFileSystem > OverlayFS)
static std::optional< SmallVector< std::string, 0 > > getFirstCC1CommandLine(ArrayRef< std::string > CommandLine, DiagnosticsEngine &Diags, llvm::IntrusiveRefCntPtr< llvm::vfs::OverlayFileSystem > OverlayFS)
#define SM(sm)
Defines the clang::Preprocessor interface.
std::vector< std::string > getCC1CommandLine() const
Generate cc1-compatible command line arguments from this instance, wrapping the result as a std::vect...
Helper class for holding the data necessary to invoke the compiler.
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:233
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
One of these records is kept for each identifier that is lexed.
@ Hidden
All of the names in this module are hidden.
Definition Module.h:548
This interface provides a way to observe the actions of the preprocessor as it does its thing.
Definition PPCallbacks.h:37
virtual void EndOfMainFile()
Callback invoked when the end of the main file is reached.
virtual void LexedFileChanged(FileID FID, LexedFileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID, SourceLocation Loc)
Callback invoked whenever the Lexer moves to a different file for lexing.
Definition PPCallbacks.h:72
virtual void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path, const Module *Imported)
Callback invoked whenever there was an explicit module-import syntax.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
bool EnterSourceFile(FileID FID, ConstSearchDirIterator Dir, SourceLocation Loc, bool IsFirstIncludeOfFile=true)
Add a source file to the top of the include stack and start lexing tokens from it instead of the curr...
IdentifierInfo * getIdentifierInfo(StringRef Name) const
Return information about the specified preprocessor identifier token.
SourceManager & getSourceManager() const
Encodes a location in the source.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
A simple dependency action controller that uses a callback.
Dependency scanner callbacks that are used during scanning to influence the behaviour of the scan - f...
virtual bool initialize(CompilerInstance &ScanInstance, CompilerInvocation &NewInvocation)
Initializes the scan instance and modifies the resulting TU invocation.
virtual bool finalize(CompilerInstance &ScanInstance, CompilerInvocation &NewInvocation)
Finalizes the scan instance and modifies the resulting TU invocation.
An individual dependency scanning worker that is able to run on its own thread.
Command - An executable path/name and argument vector to execute.
Definition Job.h:106
const llvm::opt::ArgStringList & getArguments() const
Definition Job.h:224
const char * getExecutable() const
Definition Job.h:222
static std::optional< CompilerInstanceWithContext > initializeFromCommandline(DependencyScanningTool &Tool, StringRef CWD, ArrayRef< std::string > CommandLine, dependencies::DependencyActionController &Controller, DiagnosticConsumer &DC)
Initialize the tool's compiler instance from the commandline.
static llvm::Expected< CompilerInstanceWithContext > initializeOrError(DependencyScanningTool &Tool, StringRef CWD, ArrayRef< std::string > CommandLine, dependencies::LookupModuleOutputCallback LookupModuleOutput)
Initializing the context and the compiler instance.
llvm::Expected< dependencies::TranslationUnitDeps > computeDependenciesByNameOrError(StringRef ModuleName, const llvm::DenseSet< dependencies::ModuleID > &AlreadySeen, dependencies::LookupModuleOutputCallback LookupModuleOutput)
Computes the dependeny for the module named ModuleName.
bool computeDependencies(StringRef ModuleName, dependencies::DependencyConsumer &Consumer, dependencies::DependencyActionController &Controller)
The high-level implementation of the dependency discovery tool that runs on an individual worker thre...
std::optional< dependencies::TranslationUnitDeps > getTranslationUnitDependencies(ArrayRef< std::string > CommandLine, StringRef CWD, DiagnosticConsumer &DiagConsumer, const llvm::DenseSet< dependencies::ModuleID > &AlreadySeen, dependencies::LookupModuleOutputCallback LookupModuleOutput, std::optional< llvm::MemoryBufferRef > TUBuffer=std::nullopt)
Given a Clang driver command-line for a translation unit, gather the modular dependencies and return ...
llvm::Expected< dependencies::TranslationUnitDeps > getModuleDependencies(StringRef ModuleName, ArrayRef< std::string > CommandLine, StringRef CWD, const llvm::DenseSet< dependencies::ModuleID > &AlreadySeen, dependencies::LookupModuleOutputCallback LookupModuleOutput)
Given a compilation context specified via the Clang driver command-line, gather modular dependencies ...
std::optional< P1689Rule > getP1689ModuleDependencyFile(const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput, std::string &MakeformatOutputPath, DiagnosticConsumer &DiagConsumer)
Collect the module dependency in P1689 format for C++20 named modules.
std::optional< std::string > getDependencyFile(ArrayRef< std::string > CommandLine, StringRef CWD, DiagnosticConsumer &DiagConsumer)
Print out the dependency information into a string using the dependency file format that is specified...
CharacteristicKind
Indicates whether a file or directory holds normal user code, system code, or system code which is im...
SmallVector< StringRef > getInitialStableDirs(const CompilerInstance &ScanInstance)
llvm::function_ref< std::string(const ModuleDeps &, ModuleOutputKind)> LookupModuleOutputCallback
A callback to lookup module outputs for "-fmodule-file=", "-o" etc.
@ VFS
Remove unused -ivfsoverlay arguments.
std::shared_ptr< CompilerInvocation > createScanCompilerInvocation(const CompilerInvocation &Invocation, const DependencyScanningService &Service, DependencyActionController &Controller)
Creates a CompilerInvocation suitable for the dependency scanner.
void initializeScanCompilerInstance(CompilerInstance &ScanInstance, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service, IntrusiveRefCntPtr< DependencyScanningWorkerFilesystem > DepFS)
ModuleOutputKind
An output from a module compilation, such as the path of the module file.
std::unique_ptr< CompilerInvocation > createCompilerInvocation(ArrayRef< std::string > CommandLine, DiagnosticsEngine &Diags)
void canonicalizeDefines(PreprocessorOptions &PPOpts)
Canonicalizes command-line macro defines (e.g. removing "-DX -UX").
std::unique_ptr< DependencyOutputOptions > createDependencyOutputOptions(const CompilerInvocation &Invocation)
Creates dependency output options to be reported to the dependency consumer, deducing missing informa...
std::optional< PrebuiltModulesAttrsMap > computePrebuiltModulesASTMap(CompilerInstance &ScanInstance, SmallVector< StringRef > &StableDirs)
std::shared_ptr< ModuleCache > makeInProcessModuleCache(ModuleCacheEntries &Entries)
std::shared_ptr< ModuleDepCollector > initializeScanInstanceDependencyCollector(CompilerInstance &ScanInstance, std::unique_ptr< DependencyOutputOptions > DepOutputOpts, StringRef WorkingDirectory, DependencyConsumer &Consumer, DependencyScanningService &Service, CompilerInvocation &Inv, DependencyActionController &Controller, PrebuiltModulesAttrsMap PrebuiltModulesASTMap, llvm::SmallVector< StringRef > &StableDirs)
Create the dependency collector that will collect the produced dependencies.
llvm::StringRef getDriverMode(StringRef ProgName, ArrayRef< const char * > Args)
Returns the driver mode option's value, i.e.
Definition Driver.cpp:7377
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:7394
bool IsClangCL(StringRef DriverMode)
Checks whether the value produced by getDriverMode is for CL mode.
Definition Driver.cpp:7392
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
bool computeDependencies(dependencies::DependencyScanningWorker &Worker, StringRef WorkingDirectory, ArrayRef< std::string > CommandLine, dependencies::DependencyConsumer &Consumer, dependencies::DependencyActionController &Controller, DiagnosticConsumer &DiagConsumer, llvm::IntrusiveRefCntPtr< llvm::vfs::OverlayFileSystem > OverlayFS=nullptr)
Run the dependency scanning worker for the given driver or frontend command-line, and report the disc...
std::shared_ptr< MatchComputation< T > > Generator
Definition RewriteRule.h:65
The JSON file list parser is used to communicate input to InstallAPI.
@ Worker
'worker' clause, allowed on 'loop', Combined, and 'routine' directives.
@ Result
The result type of a method or function.
Definition TypeBase.h:905
int __ovld __cnfn any(char)
Returns 1 if the most significant bit in any component of x is set; otherwise returns 0.
A command-line tool invocation that is part of building a TU.
This is used to identify a specific module.
Specifies the working directory and command of a compilation.