Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
35294 views
1
//===- DependencyScanningWorker.cpp - clang-scan-deps worker --------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
10
#include "clang/Basic/DiagnosticDriver.h"
11
#include "clang/Basic/DiagnosticFrontend.h"
12
#include "clang/Basic/DiagnosticSerialization.h"
13
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
14
#include "clang/Driver/Compilation.h"
15
#include "clang/Driver/Driver.h"
16
#include "clang/Driver/Job.h"
17
#include "clang/Driver/Tool.h"
18
#include "clang/Frontend/CompilerInstance.h"
19
#include "clang/Frontend/CompilerInvocation.h"
20
#include "clang/Frontend/FrontendActions.h"
21
#include "clang/Frontend/TextDiagnosticPrinter.h"
22
#include "clang/Frontend/Utils.h"
23
#include "clang/Lex/PreprocessorOptions.h"
24
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
25
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
26
#include "clang/Tooling/Tooling.h"
27
#include "llvm/ADT/ScopeExit.h"
28
#include "llvm/Support/Allocator.h"
29
#include "llvm/Support/Error.h"
30
#include "llvm/TargetParser/Host.h"
31
#include <optional>
32
33
using namespace clang;
34
using namespace tooling;
35
using namespace dependencies;
36
37
namespace {
38
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) {}
46
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);
55
}
56
}
57
58
private:
59
StringRef WorkingDirectory;
60
std::unique_ptr<DependencyOutputOptions> Opts;
61
DependencyConsumer &C;
62
};
63
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;
78
}
79
};
80
VFSNote(0, HSOpts.VFSOverlayFiles);
81
VFSNote(1, ExistingHSOpts.VFSOverlayFiles);
82
}
83
}
84
}
85
return false;
86
}
87
88
using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
89
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) {}
103
104
bool needsImportVisitation() const override { return true; }
105
106
void visitImport(StringRef ModuleName, StringRef Filename) override {
107
if (PrebuiltModuleFiles.insert({ModuleName.str(), Filename.str()}).second)
108
NewModuleFiles.push_back(Filename.str());
109
}
110
111
void visitModuleFile(StringRef Filename,
112
serialization::ModuleKind Kind) override {
113
CurrentFile = Filename;
114
}
115
116
bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
117
bool Complain) override {
118
std::vector<std::string> VFSOverlayFiles = HSOpts.VFSOverlayFiles;
119
PrebuiltModuleVFSMap.insert(
120
{CurrentFile, llvm::StringSet<>(VFSOverlayFiles)});
121
return checkHeaderSearchPaths(
122
HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts);
123
}
124
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;
133
};
134
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);
147
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;
156
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;
165
}
166
return false;
167
}
168
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);
174
}
175
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;
182
183
if (InputFiles.empty() || !InputFiles.front().isFile())
184
return "clang-scan-deps\\ dependency";
185
186
return makeObjFileName(InputFiles.front().getFile());
187
}
188
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);
207
});
208
}
209
210
// Clang implements -D and -U by splatting text into a predefines buffer. This
211
// allows constructs such as `-DFඞ=3 "-D F\u{0D9E} 4 3 2”` to be accepted and
212
// define the same macro, or adding C++ style comments before the macro name.
213
//
214
// This function checks that the first non-space characters in the macro
215
// obviously form an identifier that can be uniqued on without lexing. Failing
216
// to do this could lead to changing the final definition of a macro.
217
//
218
// We could set up a preprocessor and actually lex the name, but that's very
219
// heavyweight for a situation that will almost never happen in practice.
220
static std::optional<StringRef> getSimpleMacroName(StringRef Macro) {
221
StringRef Name = Macro.split("=").first.ltrim(" \t");
222
std::size_t I = 0;
223
224
auto FinishName = [&]() -> std::optional<StringRef> {
225
StringRef SimpleName = Name.slice(0, I);
226
if (SimpleName.empty())
227
return std::nullopt;
228
return SimpleName;
229
};
230
231
for (; I != Name.size(); ++I) {
232
switch (Name[I]) {
233
case '(': // Start of macro parameter list
234
case ' ': // End of macro name
235
case '\t':
236
return FinishName();
237
case '_':
238
continue;
239
default:
240
if (llvm::isAlnum(Name[I]))
241
continue;
242
return std::nullopt;
243
}
244
}
245
return FinishName();
246
}
247
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;
260
}
261
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());
268
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]));
276
}
277
std::swap(PPOpts.Macros, NewMacros);
278
}
279
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) {}
295
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());
306
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;
314
}
315
316
Scanned = true;
317
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));
322
323
// Create the compiler's actual diagnostics engine.
324
sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
325
ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
326
if (!ScanInstance.hasDiagnostics())
327
return false;
328
329
// Some DiagnosticConsumers require that finish() is called.
330
auto DiagConsumerFinisher =
331
llvm::make_scope_exit([DiagConsumer]() { DiagConsumer->finish(); });
332
333
ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath =
334
true;
335
336
ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
337
ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
338
ScanInstance.getFrontendOpts().ModulesShareFileManager = false;
339
ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw";
340
ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage =
341
any(OptimizeArgs & ScanningOptimizations::VFS);
342
343
// Support for virtual file system overlays.
344
auto FS = createVFSFromCompilerInvocation(
345
ScanInstance.getInvocation(), ScanInstance.getDiagnostics(),
346
DriverFileMgr->getVirtualFileSystemPtr());
347
348
// Create a new FileManager to match the invocation's FileSystemOptions.
349
auto *FileMgr = ScanInstance.createFileManager(FS);
350
ScanInstance.createSourceManager(*FileMgr);
351
352
// Store the list of prebuilt module files into header search options. This
353
// will prevent the implicit build to create duplicate modules and will
354
// force reuse of the existing prebuilt module files instead.
355
PrebuiltModuleVFSMapT PrebuiltModuleVFSMap;
356
if (!ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
357
if (visitPrebuiltModule(
358
ScanInstance.getPreprocessorOpts().ImplicitPCHInclude,
359
ScanInstance,
360
ScanInstance.getHeaderSearchOpts().PrebuiltModuleFiles,
361
PrebuiltModuleVFSMap, ScanInstance.getDiagnostics()))
362
return false;
363
364
// Use the dependency scanning optimized file system if requested to do so.
365
if (DepFS)
366
ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
367
[LocalDepFS = DepFS](FileEntryRef File)
368
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
369
if (llvm::ErrorOr<EntryRef> Entry =
370
LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
371
if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
372
return Entry->getDirectiveTokens();
373
return std::nullopt;
374
};
375
376
// Create the dependency collector that will collect the produced
377
// dependencies.
378
//
379
// This also moves the existing dependency output options from the
380
// invocation to the collector. The options in the invocation are reset,
381
// which ensures that the compiler won't create new dependency collectors,
382
// and thus won't write out the extra '.d' files to disk.
383
auto Opts = std::make_unique<DependencyOutputOptions>();
384
std::swap(*Opts, ScanInstance.getInvocation().getDependencyOutputOpts());
385
// We need at least one -MT equivalent for the generator of make dependency
386
// files to work.
387
if (Opts->Targets.empty())
388
Opts->Targets = {
389
deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile,
390
ScanInstance.getFrontendOpts().Inputs)};
391
Opts->IncludeSystemHeaders = true;
392
393
switch (Format) {
394
case ScanningOutputFormat::Make:
395
ScanInstance.addDependencyCollector(
396
std::make_shared<DependencyConsumerForwarder>(
397
std::move(Opts), WorkingDirectory, Consumer));
398
break;
399
case ScanningOutputFormat::P1689:
400
case ScanningOutputFormat::Full:
401
MDC = std::make_shared<ModuleDepCollector>(
402
std::move(Opts), ScanInstance, Consumer, Controller,
403
OriginalInvocation, std::move(PrebuiltModuleVFSMap), OptimizeArgs,
404
EagerLoadModules, Format == ScanningOutputFormat::P1689);
405
ScanInstance.addDependencyCollector(MDC);
406
break;
407
}
408
409
// Consider different header search and diagnostic options to create
410
// different modules. This avoids the unsound aliasing of module PCMs.
411
//
412
// TODO: Implement diagnostic bucketing to reduce the impact of strict
413
// context hashing.
414
ScanInstance.getHeaderSearchOpts().ModulesStrictContextHash = true;
415
ScanInstance.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true;
416
ScanInstance.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true;
417
ScanInstance.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings =
418
true;
419
420
// Avoid some checks and module map parsing when loading PCM files.
421
ScanInstance.getPreprocessorOpts().ModulesCheckRelocated = false;
422
423
std::unique_ptr<FrontendAction> Action;
424
425
if (ModuleName)
426
Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
427
else
428
Action = std::make_unique<ReadPCHAndPreprocessAction>();
429
430
if (ScanInstance.getDiagnostics().hasErrorOccurred())
431
return false;
432
433
// Each action is responsible for calling finish.
434
DiagConsumerFinisher.release();
435
const bool Result = ScanInstance.ExecuteAction(*Action);
436
437
if (Result)
438
setLastCC1Arguments(std::move(OriginalInvocation));
439
440
// Propagate the statistics to the parent FileManager.
441
DriverFileMgr->AddStats(ScanInstance.getFileManager());
442
443
return Result;
444
}
445
446
bool hasScanned() const { return Scanned; }
447
448
/// Take the cc1 arguments corresponding to the most recent invocation used
449
/// with this action. Any modifications implied by the discovered dependencies
450
/// will have already been applied.
451
std::vector<std::string> takeLastCC1Arguments() {
452
std::vector<std::string> Result;
453
std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty.
454
return Result;
455
}
456
457
private:
458
void setLastCC1Arguments(CompilerInvocation &&CI) {
459
if (MDC)
460
MDC->applyDiscoveredDependencies(CI);
461
LastCC1Arguments = CI.getCC1CommandLine();
462
}
463
464
private:
465
StringRef WorkingDirectory;
466
DependencyConsumer &Consumer;
467
DependencyActionController &Controller;
468
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
469
ScanningOutputFormat Format;
470
ScanningOptimizations OptimizeArgs;
471
bool EagerLoadModules;
472
bool DisableFree;
473
std::optional<StringRef> ModuleName;
474
std::optional<CompilerInstance> ScanInstanceStorage;
475
std::shared_ptr<ModuleDepCollector> MDC;
476
std::vector<std::string> LastCC1Arguments;
477
bool Scanned = false;
478
};
479
480
} // end anonymous namespace
481
482
DependencyScanningWorker::DependencyScanningWorker(
483
DependencyScanningService &Service,
484
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
485
: Format(Service.getFormat()), OptimizeArgs(Service.getOptimizeArgs()),
486
EagerLoadModules(Service.shouldEagerLoadModules()) {
487
PCHContainerOps = std::make_shared<PCHContainerOperations>();
488
// We need to read object files from PCH built outside the scanner.
489
PCHContainerOps->registerReader(
490
std::make_unique<ObjectFilePCHContainerReader>());
491
// The scanner itself writes only raw ast files.
492
PCHContainerOps->registerWriter(std::make_unique<RawPCHContainerWriter>());
493
494
switch (Service.getMode()) {
495
case ScanningMode::DependencyDirectivesScan:
496
DepFS =
497
new DependencyScanningWorkerFilesystem(Service.getSharedCache(), FS);
498
BaseFS = DepFS;
499
break;
500
case ScanningMode::CanonicalPreprocessing:
501
DepFS = nullptr;
502
BaseFS = FS;
503
break;
504
}
505
}
506
507
llvm::Error DependencyScanningWorker::computeDependencies(
508
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
509
DependencyConsumer &Consumer, DependencyActionController &Controller,
510
std::optional<StringRef> ModuleName) {
511
std::vector<const char *> CLI;
512
for (const std::string &Arg : CommandLine)
513
CLI.push_back(Arg.c_str());
514
auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
515
sanitizeDiagOpts(*DiagOpts);
516
517
// Capture the emitted diagnostics and report them to the client
518
// in the case of a failure.
519
std::string DiagnosticOutput;
520
llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
521
TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
522
523
if (computeDependencies(WorkingDirectory, CommandLine, Consumer, Controller,
524
DiagPrinter, ModuleName))
525
return llvm::Error::success();
526
return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
527
llvm::inconvertibleErrorCode());
528
}
529
530
static bool forEachDriverJob(
531
ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags, FileManager &FM,
532
llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
533
SmallVector<const char *, 256> Argv;
534
Argv.reserve(ArgStrs.size());
535
for (const std::string &Arg : ArgStrs)
536
Argv.push_back(Arg.c_str());
537
538
llvm::vfs::FileSystem *FS = &FM.getVirtualFileSystem();
539
540
std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
541
Argv[0], llvm::sys::getDefaultTargetTriple(), Diags,
542
"clang LLVM compiler", FS);
543
Driver->setTitle("clang_based_tool");
544
545
llvm::BumpPtrAllocator Alloc;
546
bool CLMode = driver::IsClangCL(
547
driver::getDriverMode(Argv[0], ArrayRef(Argv).slice(1)));
548
549
if (llvm::Error E = driver::expandResponseFiles(Argv, CLMode, Alloc, FS)) {
550
Diags.Report(diag::err_drv_expand_response_file)
551
<< llvm::toString(std::move(E));
552
return false;
553
}
554
555
const std::unique_ptr<driver::Compilation> Compilation(
556
Driver->BuildCompilation(llvm::ArrayRef(Argv)));
557
if (!Compilation)
558
return false;
559
560
if (Compilation->containsError())
561
return false;
562
563
for (const driver::Command &Job : Compilation->getJobs()) {
564
if (!Callback(Job))
565
return false;
566
}
567
return true;
568
}
569
570
static bool createAndRunToolInvocation(
571
std::vector<std::string> CommandLine, DependencyScanningAction &Action,
572
FileManager &FM,
573
std::shared_ptr<clang::PCHContainerOperations> &PCHContainerOps,
574
DiagnosticsEngine &Diags, DependencyConsumer &Consumer) {
575
576
// Save executable path before providing CommandLine to ToolInvocation
577
std::string Executable = CommandLine[0];
578
ToolInvocation Invocation(std::move(CommandLine), &Action, &FM,
579
PCHContainerOps);
580
Invocation.setDiagnosticConsumer(Diags.getClient());
581
Invocation.setDiagnosticOptions(&Diags.getDiagnosticOptions());
582
if (!Invocation.run())
583
return false;
584
585
std::vector<std::string> Args = Action.takeLastCC1Arguments();
586
Consumer.handleBuildCommand({std::move(Executable), std::move(Args)});
587
return true;
588
}
589
590
bool DependencyScanningWorker::computeDependencies(
591
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
592
DependencyConsumer &Consumer, DependencyActionController &Controller,
593
DiagnosticConsumer &DC, std::optional<StringRef> ModuleName) {
594
// Reset what might have been modified in the previous worker invocation.
595
BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
596
597
std::optional<std::vector<std::string>> ModifiedCommandLine;
598
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
599
600
// If we're scanning based on a module name alone, we don't expect the client
601
// to provide us with an input file. However, the driver really wants to have
602
// one. Let's just make it up to make the driver happy.
603
if (ModuleName) {
604
auto OverlayFS =
605
llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
606
auto InMemoryFS =
607
llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
608
InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
609
OverlayFS->pushOverlay(InMemoryFS);
610
ModifiedFS = OverlayFS;
611
612
SmallString<128> FakeInputPath;
613
// TODO: We should retry the creation if the path already exists.
614
llvm::sys::fs::createUniquePath(*ModuleName + "-%%%%%%%%.input",
615
FakeInputPath,
616
/*MakeAbsolute=*/false);
617
InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
618
619
ModifiedCommandLine = CommandLine;
620
ModifiedCommandLine->emplace_back(FakeInputPath);
621
}
622
623
const std::vector<std::string> &FinalCommandLine =
624
ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
625
auto &FinalFS = ModifiedFS ? ModifiedFS : BaseFS;
626
627
auto FileMgr =
628
llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions{}, FinalFS);
629
630
std::vector<const char *> FinalCCommandLine(FinalCommandLine.size(), nullptr);
631
llvm::transform(FinalCommandLine, FinalCCommandLine.begin(),
632
[](const std::string &Str) { return Str.c_str(); });
633
634
auto DiagOpts = CreateAndPopulateDiagOpts(FinalCCommandLine);
635
sanitizeDiagOpts(*DiagOpts);
636
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
637
CompilerInstance::createDiagnostics(DiagOpts.release(), &DC,
638
/*ShouldOwnClient=*/false);
639
640
// Although `Diagnostics` are used only for command-line parsing, the
641
// custom `DiagConsumer` might expect a `SourceManager` to be present.
642
SourceManager SrcMgr(*Diags, *FileMgr);
643
Diags->setSourceManager(&SrcMgr);
644
// DisableFree is modified by Tooling for running
645
// in-process; preserve the original value, which is
646
// always true for a driver invocation.
647
bool DisableFree = true;
648
DependencyScanningAction Action(WorkingDirectory, Consumer, Controller, DepFS,
649
Format, OptimizeArgs, EagerLoadModules,
650
DisableFree, ModuleName);
651
652
bool Success = false;
653
if (FinalCommandLine[1] == "-cc1") {
654
Success = createAndRunToolInvocation(FinalCommandLine, Action, *FileMgr,
655
PCHContainerOps, *Diags, Consumer);
656
} else {
657
Success = forEachDriverJob(
658
FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
659
if (StringRef(Cmd.getCreator().getName()) != "clang") {
660
// Non-clang command. Just pass through to the dependency
661
// consumer.
662
Consumer.handleBuildCommand(
663
{Cmd.getExecutable(),
664
{Cmd.getArguments().begin(), Cmd.getArguments().end()}});
665
return true;
666
}
667
668
// Insert -cc1 comand line options into Argv
669
std::vector<std::string> Argv;
670
Argv.push_back(Cmd.getExecutable());
671
Argv.insert(Argv.end(), Cmd.getArguments().begin(),
672
Cmd.getArguments().end());
673
674
// Create an invocation that uses the underlying file
675
// system to ensure that any file system requests that
676
// are made by the driver do not go through the
677
// dependency scanning filesystem.
678
return createAndRunToolInvocation(std::move(Argv), Action, *FileMgr,
679
PCHContainerOps, *Diags, Consumer);
680
});
681
}
682
683
if (Success && !Action.hasScanned())
684
Diags->Report(diag::err_fe_expected_compiler_job)
685
<< llvm::join(FinalCommandLine, " ");
686
return Success && Action.hasScanned();
687
}
688
689
DependencyActionController::~DependencyActionController() {}
690
691