Coverage Report

Created: 2021-09-21 08:58

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
Line
Count
Source (jump to first uncovered line)
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/CodeGen/ObjectFilePCHContainerOperations.h"
11
#include "clang/Frontend/CompilerInstance.h"
12
#include "clang/Frontend/CompilerInvocation.h"
13
#include "clang/Frontend/FrontendActions.h"
14
#include "clang/Frontend/TextDiagnosticPrinter.h"
15
#include "clang/Frontend/Utils.h"
16
#include "clang/Lex/PreprocessorOptions.h"
17
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
18
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
19
#include "clang/Tooling/Tooling.h"
20
21
using namespace clang;
22
using namespace tooling;
23
using namespace dependencies;
24
25
namespace {
26
27
/// Forwards the gatherered dependencies to the consumer.
28
class DependencyConsumerForwarder : public DependencyFileGenerator {
29
public:
30
  DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
31
                              DependencyConsumer &C)
32
130
      : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), C(C) {}
33
34
130
  void finishedMainFile(DiagnosticsEngine &Diags) override {
35
130
    C.handleDependencyOutputOpts(*Opts);
36
130
    llvm::SmallString<256> CanonPath;
37
363
    for (const auto &File : getDependencies()) {
38
363
      CanonPath = File;
39
363
      llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
40
363
      C.handleFileDependency(CanonPath);
41
363
    }
42
130
  }
43
44
private:
45
  std::unique_ptr<DependencyOutputOptions> Opts;
46
  DependencyConsumer &C;
47
};
48
49
/// A listener that collects the imported modules and optionally the input
50
/// files.
51
class PrebuiltModuleListener : public ASTReaderListener {
52
public:
53
  PrebuiltModuleListener(llvm::StringMap<std::string> &PrebuiltModuleFiles,
54
                         llvm::StringSet<> &InputFiles, bool VisitInputFiles)
55
      : PrebuiltModuleFiles(PrebuiltModuleFiles), InputFiles(InputFiles),
56
4
        VisitInputFiles(VisitInputFiles) {}
57
58
12
  bool needsImportVisitation() const override { return true; }
59
12
  bool needsInputFileVisitation() override { return VisitInputFiles; }
60
12
  bool needsSystemInputFileVisitation() override { return VisitInputFiles; }
61
62
10
  void visitImport(StringRef ModuleName, StringRef Filename) override {
63
10
    PrebuiltModuleFiles.insert({ModuleName, Filename.str()});
64
10
  }
65
66
  bool visitInputFile(StringRef Filename, bool isSystem, bool isOverridden,
67
25
                      bool isExplicitModule) override {
68
25
    InputFiles.insert(Filename);
69
25
    return true;
70
25
  }
71
72
private:
73
  llvm::StringMap<std::string> &PrebuiltModuleFiles;
74
  llvm::StringSet<> &InputFiles;
75
  bool VisitInputFiles;
76
};
77
78
using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles);
79
80
/// Visit the given prebuilt module and collect all of the modules it
81
/// transitively imports and contributing input files.
82
static void visitPrebuiltModule(StringRef PrebuiltModuleFilename,
83
                                CompilerInstance &CI,
84
                                PrebuiltModuleFilesT &ModuleFiles,
85
                                llvm::StringSet<> &InputFiles,
86
4
                                bool VisitInputFiles) {
87
  // Maps the names of modules that weren't yet visited to their PCM path.
88
4
  llvm::StringMap<std::string> ModuleFilesWorklist;
89
  // Contains PCM paths of all visited modules.
90
4
  llvm::StringSet<> VisitedModuleFiles;
91
92
4
  PrebuiltModuleListener Listener(ModuleFilesWorklist, InputFiles,
93
4
                                  VisitInputFiles);
94
95
12
  auto GatherModuleFileInfo = [&](StringRef ASTFile) {
96
12
    ASTReader::readASTFileControlBlock(
97
12
        ASTFile, CI.getFileManager(), CI.getPCHContainerReader(),
98
12
        /*FindModuleFileExtensions=*/false, Listener,
99
12
        /*ValidateDiagnosticOptions=*/false);
100
12
  };
101
102
4
  GatherModuleFileInfo(PrebuiltModuleFilename);
103
14
  while (!ModuleFilesWorklist.empty()) {
104
10
    auto WorklistItemIt = ModuleFilesWorklist.begin();
105
106
10
    if (!VisitedModuleFiles.contains(WorklistItemIt->getValue())) {
107
8
      VisitedModuleFiles.insert(WorklistItemIt->getValue());
108
8
      GatherModuleFileInfo(WorklistItemIt->getValue());
109
8
      ModuleFiles[WorklistItemIt->getKey().str()] = WorklistItemIt->getValue();
110
8
    }
111
112
10
    ModuleFilesWorklist.erase(WorklistItemIt);
113
10
  }
114
4
}
115
116
/// Transform arbitrary file name into an object-like file name.
117
59
static std::string makeObjFileName(StringRef FileName) {
118
59
  SmallString<128> ObjFileName(FileName);
119
59
  llvm::sys::path::replace_extension(ObjFileName, "o");
120
59
  return std::string(ObjFileName.str());
121
59
}
122
123
/// Deduce the dependency target based on the output file and input files.
124
static std::string
125
deduceDepTarget(const std::string &OutputFile,
126
131
                const SmallVectorImpl<FrontendInputFile> &InputFiles) {
127
131
  if (OutputFile != "-")
128
69
    return OutputFile;
129
130
62
  if (InputFiles.empty() || 
!InputFiles.front().isFile()59
)
131
0
    return "clang-scan-deps\\ dependency";
132
133
62
  return makeObjFileName(InputFiles.front().getFile());
134
62
}
135
136
/// Sanitize diagnostic options for dependency scan.
137
326
static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
138
  // Don't print 'X warnings and Y errors generated'.
139
326
  DiagOpts.ShowCarets = false;
140
  // Don't write out diagnostic file.
141
326
  DiagOpts.DiagnosticSerializationFile.clear();
142
  // Don't treat warnings as errors.
143
326
  DiagOpts.Warnings.push_back("no-error");
144
326
}
145
146
/// A clang tool that runs the preprocessor in a mode that's optimized for
147
/// dependency scanning for the given compiler invocation.
148
class DependencyScanningAction : public tooling::ToolAction {
149
public:
150
  DependencyScanningAction(
151
      StringRef WorkingDirectory, DependencyConsumer &Consumer,
152
      llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
153
      ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings,
154
      ScanningOutputFormat Format, llvm::Optional<StringRef> ModuleName = None)
155
      : WorkingDirectory(WorkingDirectory), Consumer(Consumer),
156
        DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings), Format(Format),
157
165
        ModuleName(ModuleName) {}
158
159
  bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
160
                     FileManager *FileMgr,
161
                     std::shared_ptr<PCHContainerOperations> PCHContainerOps,
162
163
                     DiagnosticConsumer *DiagConsumer) override {
163
    // Make a deep copy of the original Clang invocation.
164
163
    CompilerInvocation OriginalInvocation(*Invocation);
165
166
    // Create a compiler instance to handle the actual work.
167
163
    CompilerInstance Compiler(std::move(PCHContainerOps));
168
163
    Compiler.setInvocation(std::move(Invocation));
169
170
    // Create the compiler's actual diagnostics engine.
171
163
    sanitizeDiagOpts(Compiler.getDiagnosticOpts());
172
163
    Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
173
163
    if (!Compiler.hasDiagnostics())
174
0
      return false;
175
176
163
    Compiler.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath = true;
177
178
163
    FileMgr->getFileSystemOpts().WorkingDir = std::string(WorkingDirectory);
179
163
    Compiler.setFileManager(FileMgr);
180
163
    Compiler.createSourceManager(*FileMgr);
181
182
163
    llvm::StringSet<> PrebuiltModulesInputFiles;
183
    // Store the list of prebuilt module files into header search options. This
184
    // will prevent the implicit build to create duplicate modules and will
185
    // force reuse of the existing prebuilt module files instead.
186
163
    if (!Compiler.getPreprocessorOpts().ImplicitPCHInclude.empty())
187
4
      visitPrebuiltModule(
188
4
          Compiler.getPreprocessorOpts().ImplicitPCHInclude, Compiler,
189
4
          Compiler.getHeaderSearchOpts().PrebuiltModuleFiles,
190
4
          PrebuiltModulesInputFiles, /*VisitInputFiles=*/DepFS != nullptr);
191
192
    // Use the dependency scanning optimized file system if requested to do so.
193
163
    if (DepFS) {
194
123
      const CompilerInvocation &CI = Compiler.getInvocation();
195
123
      DepFS->clearIgnoredFiles();
196
      // Ignore any files that contributed to prebuilt modules. The implicit
197
      // build validates the modules by comparing the reported sizes of their
198
      // inputs to the current state of the filesystem. Minimization would throw
199
      // this mechanism off.
200
123
      for (const auto &File : PrebuiltModulesInputFiles)
201
17
        DepFS->ignoreFile(File.getKey());
202
      // Add any filenames that were explicity passed in the build settings and
203
      // that might be opened, as we want to ensure we don't run source
204
      // minimization on them.
205
123
      for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries)
206
390
        DepFS->ignoreFile(Entry.Path);
207
123
      for (const auto &Entry : CI.getHeaderSearchOpts().VFSOverlayFiles)
208
2
        DepFS->ignoreFile(Entry);
209
210
      // Support for virtual file system overlays on top of the caching
211
      // filesystem.
212
123
      FileMgr->setVirtualFileSystem(createVFSFromCompilerInvocation(
213
123
          CI, Compiler.getDiagnostics(), DepFS));
214
215
      // Pass the skip mappings which should speed up excluded conditional block
216
      // skipping in the preprocessor.
217
123
      if (PPSkipMappings)
218
118
        Compiler.getPreprocessorOpts()
219
118
            .ExcludedConditionalDirectiveSkipMappings = PPSkipMappings;
220
123
    }
221
222
    // Create the dependency collector that will collect the produced
223
    // dependencies.
224
    //
225
    // This also moves the existing dependency output options from the
226
    // invocation to the collector. The options in the invocation are reset,
227
    // which ensures that the compiler won't create new dependency collectors,
228
    // and thus won't write out the extra '.d' files to disk.
229
163
    auto Opts = std::make_unique<DependencyOutputOptions>();
230
163
    std::swap(*Opts, Compiler.getInvocation().getDependencyOutputOpts());
231
    // We need at least one -MT equivalent for the generator of make dependency
232
    // files to work.
233
163
    if (Opts->Targets.empty())
234
131
      Opts->Targets = {deduceDepTarget(Compiler.getFrontendOpts().OutputFile,
235
131
                                       Compiler.getFrontendOpts().Inputs)};
236
163
    Opts->IncludeSystemHeaders = true;
237
238
163
    switch (Format) {
239
130
    case ScanningOutputFormat::Make:
240
130
      Compiler.addDependencyCollector(
241
130
          std::make_shared<DependencyConsumerForwarder>(std::move(Opts),
242
130
                                                        Consumer));
243
130
      break;
244
33
    case ScanningOutputFormat::Full:
245
33
      Compiler.addDependencyCollector(std::make_shared<ModuleDepCollector>(
246
33
          std::move(Opts), Compiler, Consumer, std::move(OriginalInvocation)));
247
33
      break;
248
163
    }
249
250
    // Consider different header search and diagnostic options to create
251
    // different modules. This avoids the unsound aliasing of module PCMs.
252
    //
253
    // TODO: Implement diagnostic bucketing and header search pruning to reduce
254
    // the impact of strict context hashing.
255
155
    Compiler.getHeaderSearchOpts().ModulesStrictContextHash = true;
256
257
155
    std::unique_ptr<FrontendAction> Action;
258
259
155
    if (ModuleName.hasValue())
260
4
      Action = std::make_unique<GetDependenciesByModuleNameAction>(*ModuleName);
261
151
    else
262
151
      Action = std::make_unique<ReadPCHAndPreprocessAction>();
263
264
155
    const bool Result = Compiler.ExecuteAction(*Action);
265
155
    if (!DepFS)
266
35
      FileMgr->clearStatCache();
267
155
    return Result;
268
163
  }
269
270
private:
271
  StringRef WorkingDirectory;
272
  DependencyConsumer &Consumer;
273
  llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
274
  ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;
275
  ScanningOutputFormat Format;
276
  llvm::Optional<StringRef> ModuleName;
277
};
278
279
} // end anonymous namespace
280
281
DependencyScanningWorker::DependencyScanningWorker(
282
    DependencyScanningService &Service)
283
248
    : Format(Service.getFormat()) {
284
248
  PCHContainerOps = std::make_shared<PCHContainerOperations>();
285
248
  PCHContainerOps->registerReader(
286
248
      std::make_unique<ObjectFilePCHContainerReader>());
287
  // We don't need to write object files, but the current PCH implementation
288
  // requires the writer to be registered as well.
289
248
  PCHContainerOps->registerWriter(
290
248
      std::make_unique<ObjectFilePCHContainerWriter>());
291
292
248
  auto OverlayFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
293
248
      llvm::vfs::createPhysicalFileSystem());
294
248
  InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
295
248
  OverlayFS->pushOverlay(InMemoryFS);
296
248
  RealFS = OverlayFS;
297
298
248
  if (Service.canSkipExcludedPPRanges())
299
246
    PPSkipMappings =
300
246
        std::make_unique<ExcludedPreprocessorDirectiveSkipMapping>();
301
248
  if (Service.getMode() == ScanningMode::MinimizedSourcePreprocessing)
302
229
    DepFS = new DependencyScanningWorkerFilesystem(
303
229
        Service.getSharedCache(), RealFS, PPSkipMappings.get());
304
248
  if (Service.canReuseFileManager())
305
246
    Files = new FileManager(FileSystemOptions(), RealFS);
306
248
}
307
308
static llvm::Error
309
runWithDiags(DiagnosticOptions *DiagOpts,
310
             llvm::function_ref<bool(DiagnosticConsumer &, DiagnosticOptions &)>
311
163
                 BodyShouldSucceed) {
312
163
  sanitizeDiagOpts(*DiagOpts);
313
314
  // Capture the emitted diagnostics and report them to the client
315
  // in the case of a failure.
316
163
  std::string DiagnosticOutput;
317
163
  llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
318
163
  TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts);
319
320
163
  if (BodyShouldSucceed(DiagPrinter, *DiagOpts))
321
159
    return llvm::Error::success();
322
4
  return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
323
4
                                             llvm::inconvertibleErrorCode());
324
163
}
325
326
llvm::Error DependencyScanningWorker::computeDependencies(
327
    StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
328
165
    DependencyConsumer &Consumer, llvm::Optional<StringRef> ModuleName) {
329
  // Reset what might have been modified in the previous worker invocation.
330
165
  RealFS->setCurrentWorkingDirectory(WorkingDirectory);
331
165
  if (Files)
332
161
    Files->setVirtualFileSystem(RealFS);
333
334
165
  llvm::IntrusiveRefCntPtr<FileManager> CurrentFiles =
335
165
      Files ? 
Files161
:
new FileManager(FileSystemOptions(), RealFS)4
;
336
337
165
  Optional<std::vector<std::string>> ModifiedCommandLine;
338
165
  if (ModuleName.hasValue()) {
339
4
    ModifiedCommandLine = CommandLine;
340
4
    InMemoryFS->addFile(*ModuleName, 0, llvm::MemoryBuffer::getMemBuffer(""));
341
4
    ModifiedCommandLine->emplace_back(*ModuleName);
342
4
  }
343
344
165
  const std::vector<std::string> &FinalCommandLine =
345
165
      ModifiedCommandLine ? 
*ModifiedCommandLine4
:
CommandLine161
;
346
347
165
  std::vector<const char *> FinalCCommandLine(CommandLine.size(), nullptr);
348
165
  llvm::transform(CommandLine, FinalCCommandLine.begin(),
349
1.48k
                  [](const std::string &Str) { return Str.c_str(); });
350
351
165
  return runWithDiags(CreateAndPopulateDiagOpts(FinalCCommandLine).release(),
352
165
                      [&](DiagnosticConsumer &DC, DiagnosticOptions &DiagOpts) {
353
165
                        DependencyScanningAction Action(
354
165
                            WorkingDirectory, Consumer, DepFS,
355
165
                            PPSkipMappings.get(), Format, ModuleName);
356
                        // Create an invocation that uses the underlying file
357
                        // system to ensure that any file system requests that
358
                        // are made by the driver do not go through the
359
                        // dependency scanning filesystem.
360
165
                        ToolInvocation Invocation(FinalCommandLine, &Action,
361
165
                                                  CurrentFiles.get(),
362
165
                                                  PCHContainerOps);
363
165
                        Invocation.setDiagnosticConsumer(&DC);
364
165
                        Invocation.setDiagnosticOptions(&DiagOpts);
365
165
                        return Invocation.run();
366
165
                      });
367
165
}