Coverage Report

Created: 2022-07-16 07:03

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