[lld-macho] Remove unneeded functions from BPSectionOrderer. NFC
[llvm-project.git] / clang / lib / Tooling / DependencyScanning / DependencyScanningWorker.cpp
blob5a648df05e4fd33aeb32431621db0f1e9eca6f85
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 //===----------------------------------------------------------------------===//
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/Driver/Compilation.h"
14 #include "clang/Driver/Driver.h"
15 #include "clang/Driver/Job.h"
16 #include "clang/Driver/Tool.h"
17 #include "clang/Frontend/CompilerInstance.h"
18 #include "clang/Frontend/CompilerInvocation.h"
19 #include "clang/Frontend/FrontendActions.h"
20 #include "clang/Frontend/TextDiagnosticPrinter.h"
21 #include "clang/Frontend/Utils.h"
22 #include "clang/Lex/PreprocessorOptions.h"
23 #include "clang/Serialization/ObjectFilePCHContainerReader.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>
33 using namespace clang;
34 using namespace tooling;
35 using namespace dependencies;
37 namespace {
39 /// Forwards the gatherered dependencies to the consumer.
40 class DependencyConsumerForwarder : public DependencyFileGenerator {
41 public:
42 DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
43 StringRef WorkingDirectory, DependencyConsumer &C)
44 : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory),
45 Opts(std::move(Opts)), C(C) {}
47 void finishedMainFile(DiagnosticsEngine &Diags) override {
48 C.handleDependencyOutputOpts(*Opts);
49 llvm::SmallString<256> CanonPath;
50 for (const auto &File : getDependencies()) {
51 CanonPath = File;
52 llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
53 llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath);
54 C.handleFileDependency(CanonPath);
58 private:
59 StringRef WorkingDirectory;
60 std::unique_ptr<DependencyOutputOptions> Opts;
61 DependencyConsumer &C;
64 static 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(VFSOverlays, "\n");
77 Diags->Report(diag::note_pch_vfsoverlay_files) << Type << Files;
80 VFSNote(0, HSOpts.VFSOverlayFiles);
81 VFSNote(1, ExistingHSOpts.VFSOverlayFiles);
85 return false;
88 using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
90 /// A listener that collects the imported modules and optionally the input
91 /// files.
92 class PrebuiltModuleListener : public ASTReaderListener {
93 public:
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) {}
104 bool needsImportVisitation() const override { return true; }
106 void visitImport(StringRef ModuleName, StringRef Filename) override {
107 if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
108 NewModuleFiles.push_back(Filename.str());
111 void visitModuleFile(StringRef Filename,
112 serialization::ModuleKind Kind) override {
113 CurrentFile = Filename;
116 bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
117 bool Complain) override {
118 std::vector<std::string> VFSOverlayFiles = HSOpts.VFSOverlayFiles;
119 PrebuiltModuleVFSMap.insert(
120 {CurrentFile, llvm::StringSet<>(VFSOverlayFiles)});
121 return checkHeaderSearchPaths(
122 HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);
125 private:
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;
135 /// Visit the given prebuilt module and collect all of the modules it
136 /// transitively imports and contributing input files.
137 static 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);
148 Listener.visitModuleFile(PrebuiltModuleFilename,
149 serialization::MK_ExplicitModule);
150 if (ASTReader::readASTFileControlBlock(
151 PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(),
152 CI.getPCHContainerReader(),
153 /*FindModuleFileExtensions=*/false, Listener,
154 /*ValidateDiagnosticOptions=*/false, ASTReader::ARR_OutOfDate))
155 return true;
157 while (!Worklist.empty()) {
158 Listener.visitModuleFile(Worklist.back(), serialization::MK_ExplicitModule);
159 if (ASTReader::readASTFileControlBlock(
160 Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
161 CI.getPCHContainerReader(),
162 /*FindModuleFileExtensions=*/false, Listener,
163 /*ValidateDiagnosticOptions=*/false))
164 return true;
166 return false;
169 /// Transform arbitrary file name into an object-like file name.
170 static std::string makeObjFileName(StringRef FileName) {
171 SmallString<128> ObjFileName(FileName);
172 llvm::sys::path::replace_extension(ObjFileName, "o");
173 return std::string(ObjFileName);
176 /// Deduce the dependency target based on the output file and input files.
177 static std::string
178 deduceDepTarget(const std::string &OutputFile,
179 const SmallVectorImpl<FrontendInputFile> &InputFiles) {
180 if (OutputFile != "-")
181 return OutputFile;
183 if (InputFiles.empty() || !InputFiles.front().isFile())
184 return "clang-scan-deps\\ dependency";
186 return makeObjFileName(InputFiles.front().getFile());
189 /// Sanitize diagnostic options for dependency scan.
190 static 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(DiagOpts.Warnings, [](StringRef Warning) {
203 return llvm::StringSwitch<bool>(Warning)
204 .Cases("pch-vfs-diff", "error=pch-vfs-diff", false)
205 .StartsWith("no-error=", false)
206 .Default(true);
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.
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.
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.
220 static std::optional<StringRef> getSimpleMacroName(StringRef Macro) {
221 StringRef Name = Macro.split("=").first.ltrim(" \t");
222 std::size_t I = 0;
224 auto FinishName = [&]() -> std::optional<StringRef> {
225 StringRef SimpleName = Name.slice(0, I);
226 if (SimpleName.empty())
227 return std::nullopt;
228 return SimpleName;
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(Name[I]))
241 continue;
242 return std::nullopt;
245 return FinishName();
248 static void canonicalizeDefines(PreprocessorOptions &PPOpts) {
249 using MacroOpt = std::pair<StringRef, std::size_t>;
250 std::vector<MacroOpt> SimpleNames;
251 SimpleNames.reserve(PPOpts.Macros.size());
252 std::size_t Index = 0;
253 for (const auto &M : PPOpts.Macros) {
254 auto SName = getSimpleMacroName(M.first);
255 // Skip optimizing if we can't guarantee we can preserve relative order.
256 if (!SName)
257 return;
258 SimpleNames.emplace_back(*SName, Index);
259 ++Index;
262 llvm::stable_sort(SimpleNames, llvm::less_first());
263 // Keep the last instance of each macro name by going in reverse
264 auto NewEnd = std::unique(
265 SimpleNames.rbegin(), SimpleNames.rend(),
266 [](const MacroOpt &A, const MacroOpt &B) { return A.first == B.first; });
267 SimpleNames.erase(SimpleNames.begin(), NewEnd.base());
269 // Apply permutation.
270 decltype(PPOpts.Macros) NewMacros;
271 NewMacros.reserve(SimpleNames.size());
272 for (std::size_t I = 0, E = SimpleNames.size(); I != E; ++I) {
273 std::size_t OriginalIndex = SimpleNames[I].second;
274 // We still emit undefines here as they may be undefining a predefined macro
275 NewMacros.push_back(std::move(PPOpts.Macros[OriginalIndex]));
277 std::swap(PPOpts.Macros, NewMacros);
280 /// A clang tool that runs the preprocessor in a mode that's optimized for
281 /// dependency scanning for the given compiler invocation.
282 class DependencyScanningAction : public tooling::ToolAction {
283 public:
284 DependencyScanningAction(
285 StringRef WorkingDirectory, DependencyConsumer &Consumer,
286 DependencyActionController &Controller,
287 llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
288 ScanningOutputFormat Format, ScanningOptimizations OptimizeArgs,
289 bool EagerLoadModules, bool DisableFree,
290 std::optional<StringRef> ModuleName = std::nullopt)
291 : WorkingDirectory(WorkingDirectory), Consumer(Consumer),
292 Controller(Controller), DepFS(std::move(DepFS)), Format(Format),
293 OptimizeArgs(OptimizeArgs), EagerLoadModules(EagerLoadModules),
294 DisableFree(DisableFree), ModuleName(ModuleName) {}
296 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
297 FileManager *DriverFileMgr,
298 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
299 DiagnosticConsumer *DiagConsumer) override {
300 // Make a deep copy of the original Clang invocation.
301 CompilerInvocation OriginalInvocation(*Invocation);
302 // Restore the value of DisableFree, which may be modified by Tooling.
303 OriginalInvocation.getFrontendOpts().DisableFree = DisableFree;
304 if (any(OptimizeArgs & ScanningOptimizations::Macros))
305 canonicalizeDefines(OriginalInvocation.getPreprocessorOpts());
307 if (Scanned) {
308 // Scanning runs once for the first -cc1 invocation in a chain of driver
309 // jobs. For any dependent jobs, reuse the scanning result and just
310 // update the LastCC1Arguments to correspond to the new invocation.
311 // FIXME: to support multi-arch builds, each arch requires a separate scan
312 setLastCC1Arguments(std::move(OriginalInvocation));
313 return true;
316 Scanned = true;
318 // Create a compiler instance to handle the actual work.
319 ScanInstanceStorage.emplace(std::move(PCHContainerOps));
320 CompilerInstance &ScanInstance = *ScanInstanceStorage;
321 ScanInstance.setInvocation(std::move(Invocation));
323 // Create the compiler's actual diagnostics engine.
324 sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
325 ScanInstance.createDiagnostics(DriverFileMgr->getVirtualFileSystem(),
326 DiagConsumer, /*ShouldOwnClient=*/false);
327 if (!ScanInstance.hasDiagnostics())
328 return false;
330 // Some DiagnosticConsumers require that finish() is called.
331 auto DiagConsumerFinisher =
332 llvm::make_scope_exit([DiagConsumer]() { DiagConsumer->finish(); });
334 ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =
335 true;
337 ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
338 ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
339 // This will prevent us compiling individual modules asynchronously since
340 // FileManager is not thread-safe, but it does improve performance for now.
341 ScanInstance.getFrontendOpts().ModulesShareFileManager = true;
342 ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
343 ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage =
344 any(OptimizeArgs & ScanningOptimizations::VFS);
346 // Support for virtual file system overlays.
347 auto FS = createVFSFromCompilerInvocation(
348 ScanInstance.getInvocation(), ScanInstance.getDiagnostics(),
349 DriverFileMgr->getVirtualFileSystemPtr());
351 // Use the dependency scanning optimized file system if requested to do so.
352 if (DepFS) {
353 StringRef ModulesCachePath =
354 ScanInstance.getHeaderSearchOpts().ModuleCachePath;
356 DepFS->resetBypassedPathPrefix();
357 if (!ModulesCachePath.empty())
358 DepFS->setBypassedPathPrefix(ModulesCachePath);
360 ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
361 [LocalDepFS = DepFS](FileEntryRef File)
362 -> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
363 if (llvm::ErrorOr<EntryRef> Entry =
364 LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
365 if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
366 return Entry->getDirectiveTokens();
367 return std::nullopt;
371 // Create a new FileManager to match the invocation's FileSystemOptions.
372 auto *FileMgr = ScanInstance.createFileManager(FS);
373 ScanInstance.createSourceManager(*FileMgr);
375 // Store the list of prebuilt module files into header search options. This
376 // will prevent the implicit build to create duplicate modules and will
377 // force reuse of the existing prebuilt module files instead.
378 PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
379 if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
380 if (visitPrebuiltModule(
381 ScanInstance.getPreprocessorOpts().ImplicitPCHInclude,
382 ScanInstance,
383 ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
384 PrebuiltModuleVFSMap, ScanInstance.getDiagnostics()))
385 return false;
387 // Create the dependency collector that will collect the produced
388 // dependencies.
390 // This also moves the existing dependency output options from the
391 // invocation to the collector. The options in the invocation are reset,
392 // which ensures that the compiler won't create new dependency collectors,
393 // and thus won't write out the extra '.d' files to disk.
394 auto Opts = std::make_unique<DependencyOutputOptions>();
395 std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());
396 // We need at least one -MT equivalent for the generator of make dependency
397 // files to work.
398 if (Opts->Targets.empty())
399 Opts->Targets = {
400 deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile,
401 ScanInstance.getFrontendOpts().Inputs)};
402 Opts->IncludeSystemHeaders = true;
404 switch (Format) {
405 case ScanningOutputFormat::Make:
406 ScanInstance.addDependencyCollector(
407 std::make_shared<DependencyConsumerForwarder>(
408 std::move(Opts), WorkingDirectory, Consumer));
409 break;
410 case ScanningOutputFormat::P1689:
411 case ScanningOutputFormat::Full:
412 MDC = std::make_shared<ModuleDepCollector>(
413 std::move(Opts), ScanInstance, Consumer, Controller,
414 OriginalInvocation, std::move(PrebuiltModuleVFSMap), OptimizeArgs,
415 EagerLoadModules, Format == ScanningOutputFormat::P1689);
416 ScanInstance.addDependencyCollector(MDC);
417 break;
420 // Consider different header search and diagnostic options to create
421 // different modules. This avoids the unsound aliasing of module PCMs.
423 // TODO: Implement diagnostic bucketing to reduce the impact of strict
424 // context hashing.
425 ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
426 ScanInstance.getHeaderSearchOpts().ModulesSerializeOnlyPreprocessor = true;
427 ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
428 ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;
429 ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings =
430 true;
432 // Avoid some checks and module map parsing when loading PCM files.
433 ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
435 std::unique_ptr<FrontendAction> Action;
437 if (Format == ScanningOutputFormat::P1689)
438 Action = std::make_unique<PreprocessOnlyAction>();
439 else if (ModuleName)
440 Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
441 else
442 Action = std::make_unique<ReadPCHAndPreprocessAction>();
444 if (ScanInstance.getDiagnostics().hasErrorOccurred())
445 return false;
447 // Each action is responsible for calling finish.
448 DiagConsumerFinisher.release();
449 const bool Result = ScanInstance.ExecuteAction(*Action);
451 if (Result)
452 setLastCC1Arguments(std::move(OriginalInvocation));
454 // Propagate the statistics to the parent FileManager.
455 DriverFileMgr->AddStats(ScanInstance.getFileManager());
457 return Result;
460 bool hasScanned() const { return Scanned; }
462 /// Take the cc1 arguments corresponding to the most recent invocation used
463 /// with this action. Any modifications implied by the discovered dependencies
464 /// will have already been applied.
465 std::vector<std::string> takeLastCC1Arguments() {
466 std::vector<std::string> Result;
467 std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty.
468 return Result;
471 private:
472 void setLastCC1Arguments(CompilerInvocation &&CI) {
473 if (MDC)
474 MDC->applyDiscoveredDependencies(CI);
475 LastCC1Arguments = CI.getCC1CommandLine();
478 private:
479 StringRef WorkingDirectory;
480 DependencyConsumer &Consumer;
481 DependencyActionController &Controller;
482 llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
483 ScanningOutputFormat Format;
484 ScanningOptimizations OptimizeArgs;
485 bool EagerLoadModules;
486 bool DisableFree;
487 std::optional<StringRef> ModuleName;
488 std::optional<CompilerInstance> ScanInstanceStorage;
489 std::shared_ptr<ModuleDepCollector> MDC;
490 std::vector<std::string> LastCC1Arguments;
491 bool Scanned = false;
494 } // end anonymous namespace
496 DependencyScanningWorker::DependencyScanningWorker(
497 DependencyScanningService &Service,
498 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
499 : Format(Service.getFormat()), OptimizeArgs(Service.getOptimizeArgs()),
500 EagerLoadModules(Service.shouldEagerLoadModules()) {
501 PCHContainerOps = std::make_shared<PCHContainerOperations>();
502 // We need to read object files from PCH built outside the scanner.
503 PCHContainerOps->registerReader(
504 std::make_unique<ObjectFilePCHContainerReader>());
505 // The scanner itself writes only raw ast files.
506 PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
508 if (Service.shouldTraceVFS())
509 FS = llvm::makeIntrusiveRefCnt<llvm::vfs::TracingFileSystem>(std::move(FS));
511 switch (Service.getMode()) {
512 case ScanningMode::DependencyDirectivesScan:
513 DepFS =
514 new DependencyScanningWorkerFilesystem(Service.getSharedCache(), FS);
515 BaseFS = DepFS;
516 break;
517 case ScanningMode::CanonicalPreprocessing:
518 DepFS = nullptr;
519 BaseFS = FS;
520 break;
524 llvm::Error DependencyScanningWorker::computeDependencies(
525 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
526 DependencyConsumer &Consumer, DependencyActionController &Controller,
527 std::optional<StringRef> ModuleName) {
528 std::vector<const char *> CLI;
529 for (const std::string &Arg : CommandLine)
530 CLI.push_back(Arg.c_str());
531 auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
532 sanitizeDiagOpts(*DiagOpts);
534 // Capture the emitted diagnostics and report them to the client
535 // in the case of a failure.
536 std::string DiagnosticOutput;
537 llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
538 TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
540 if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
541 DiagPrinter, ModuleName))
542 return llvm::Error::success();
543 return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
544 llvm::inconvertibleErrorCode());
547 static bool forEachDriverJob(
548 ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, FileManager &FM,
549 llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
550 SmallVector<const char *, 256> Argv;
551 Argv.reserve(ArgStrs.size());
552 for (const std::string &Arg : ArgStrs)
553 Argv.push_back(Arg.c_str());
555 llvm::vfs::FileSystem *FS = &FM.getVirtualFileSystem();
557 std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
558 Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
559 "clang LLVM compiler", FS);
560 Driver->setTitle("clang_based_tool");
562 llvm::BumpPtrAllocator Alloc;
563 bool CLMode = driver::IsClangCL(
564 driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
566 if (llvm::Error E = driver::expandResponseFiles(Argv, CLMode, Alloc, FS)) {
567 Diags.Report(diag::err_drv_expand_response_file)
568 << llvm::toString(std::move(E));
569 return false;
572 const std::unique_ptr<driver::Compilation> Compilation(
573 Driver->BuildCompilation(llvm::ArrayRef(Argv)));
574 if (!Compilation)
575 return false;
577 if (Compilation->containsError())
578 return false;
580 for (const driver::Command &Job : Compilation->getJobs()) {
581 if (!Callback(Job))
582 return false;
584 return true;
587 static bool createAndRunToolInvocation(
588 std::vector<std::string> CommandLine, DependencyScanningAction &Action,
589 FileManager &FM,
590 std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
591 DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {
593 // Save executable path before providing CommandLine to ToolInvocation
594 std::string Executable = CommandLine[0];
595 ToolInvocation Invocation(std::move(CommandLine), &Action, &FM,
596 PCHContainerOps);
597 Invocation.setDiagnosticConsumer(Diags.getClient());
598 Invocation.setDiagnosticOptions(&Diags.getDiagnosticOptions());
599 if (!Invocation.run())
600 return false;
602 std::vector<std::string> Args = Action.takeLastCC1Arguments();
603 Consumer.handleBuildCommand({std::move(Executable), std::move(Args)});
604 return true;
607 bool DependencyScanningWorker::computeDependencies(
608 StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
609 DependencyConsumer &Consumer, DependencyActionController &Controller,
610 DiagnosticConsumer &DC, std::optional<StringRef> ModuleName) {
611 // Reset what might have been modified in the previous worker invocation.
612 BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
614 std::optional<std::vector<std::string>> ModifiedCommandLine;
615 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
617 // If we're scanning based on a module name alone, we don't expect the client
618 // to provide us with an input file. However, the driver really wants to have
619 // one. Let's just make it up to make the driver happy.
620 if (ModuleName) {
621 auto OverlayFS =
622 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
623 auto InMemoryFS =
624 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
625 InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
626 OverlayFS->pushOverlay(InMemoryFS);
627 ModifiedFS = OverlayFS;
629 SmallString<128> FakeInputPath;
630 // TODO: We should retry the creation if the path already exists.
631 llvm::sys::fs::createUniquePath(*ModuleName + "-%%%%%%%%.input",
632 FakeInputPath,
633 /*MakeAbsolute=*/false);
634 InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
636 ModifiedCommandLine = CommandLine;
637 ModifiedCommandLine->emplace_back(FakeInputPath);
640 const std::vector<std::string> &FinalCommandLine =
641 ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
642 auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
644 auto FileMgr =
645 llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions{}, FinalFS);
647 std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(), nullptr);
648 llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),
649 [](const std::string &Str) { return Str.c_str(); });
651 auto DiagOpts = CreateAndPopulateDiagOpts(FinalCCommandLine);
652 sanitizeDiagOpts(*DiagOpts);
653 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
654 CompilerInstance::createDiagnostics(*FinalFS, DiagOpts.release(), &DC,
655 /*ShouldOwnClient=*/false);
657 // Although `Diagnostics` are used only for command-line parsing, the
658 // custom `DiagConsumer` might expect a `SourceManager` to be present.
659 SourceManager SrcMgr(*Diags, *FileMgr);
660 Diags->setSourceManager(&SrcMgr);
661 // DisableFree is modified by Tooling for running
662 // in-process; preserve the original value, which is
663 // always true for a driver invocation.
664 bool DisableFree = true;
665 DependencyScanningAction Action(WorkingDirectory, Consumer, Controller, DepFS,
666 Format, OptimizeArgs, EagerLoadModules,
667 DisableFree, ModuleName);
669 bool Success = false;
670 if (FinalCommandLine[1] == "-cc1") {
671 Success = createAndRunToolInvocation(FinalCommandLine, Action, *FileMgr,
672 PCHContainerOps, *Diags, Consumer);
673 } else {
674 Success = forEachDriverJob(
675 FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
676 if (StringRef(Cmd.getCreator().getName()) != "clang") {
677 // Non-clang command. Just pass through to the dependency
678 // consumer.
679 Consumer.handleBuildCommand(
680 {Cmd.getExecutable(),
681 {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
682 return true;
685 // Insert -cc1 comand line options into Argv
686 std::vector<std::string> Argv;
687 Argv.push_back(Cmd.getExecutable());
688 Argv.insert(Argv.end(), Cmd.getArguments().begin(),
689 Cmd.getArguments().end());
691 // Create an invocation that uses the underlying file
692 // system to ensure that any file system requests that
693 // are made by the driver do not go through the
694 // dependency scanning filesystem.
695 return createAndRunToolInvocation(std::move(Argv), Action, *FileMgr,
696 PCHContainerOps, *Diags, Consumer);
700 if (Success && !Action.hasScanned())
701 Diags->Report(diag::err_fe_expected_compiler_job)
702 << llvm::join(FinalCommandLine, " ");
703 return Success && Action.hasScanned();
706 DependencyActionController::~DependencyActionController() {}