Coverage Report

Created: 2021-01-26 06:56

/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/Frontend/CompilerInstance.h"
11
#include "clang/Frontend/CompilerInvocation.h"
12
#include "clang/Frontend/FrontendActions.h"
13
#include "clang/Frontend/TextDiagnosticPrinter.h"
14
#include "clang/Frontend/Utils.h"
15
#include "clang/Lex/PreprocessorOptions.h"
16
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
17
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
18
#include "clang/Tooling/Tooling.h"
19
20
using namespace clang;
21
using namespace tooling;
22
using namespace dependencies;
23
24
namespace {
25
26
/// Forwards the gatherered dependencies to the consumer.
27
class DependencyConsumerForwarder : public DependencyFileGenerator {
28
public:
29
  DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
30
                              DependencyConsumer &C)
31
68
      : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), C(C) {}
32
33
69
  void finishedMainFile(DiagnosticsEngine &Diags) override {
34
69
    llvm::SmallString<256> CanonPath;
35
194
    for (const auto &File : getDependencies()) {
36
194
      CanonPath = File;
37
194
      llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
38
194
      C.handleFileDependency(*Opts, CanonPath);
39
194
    }
40
69
  }
41
42
private:
43
  std::unique_ptr<DependencyOutputOptions> Opts;
44
  DependencyConsumer &C;
45
};
46
47
/// A clang tool that runs the preprocessor in a mode that's optimized for
48
/// dependency scanning for the given compiler invocation.
49
class DependencyScanningAction : public tooling::ToolAction {
50
public:
51
  DependencyScanningAction(
52
      StringRef WorkingDirectory, DependencyConsumer &Consumer,
53
      llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
54
      ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings,
55
      ScanningOutputFormat Format)
56
      : WorkingDirectory(WorkingDirectory), Consumer(Consumer),
57
        DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings),
58
75
        Format(Format) {}
59
60
  bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
61
                     FileManager *FileMgr,
62
                     std::shared_ptr<PCHContainerOperations> PCHContainerOps,
63
74
                     DiagnosticConsumer *DiagConsumer) override {
64
    // Create a compiler instance to handle the actual work.
65
74
    CompilerInstance Compiler(std::move(PCHContainerOps));
66
74
    Compiler.setInvocation(std::move(Invocation));
67
68
    // Don't print 'X warnings and Y errors generated'.
69
74
    Compiler.getDiagnosticOpts().ShowCarets = false;
70
    // Create the compiler's actual diagnostics engine.
71
74
    Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
72
74
    if (!Compiler.hasDiagnostics())
73
0
      return false;
74
75
    // Use the dependency scanning optimized file system if we can.
76
74
    if (DepFS) {
77
55
      const CompilerInvocation &CI = Compiler.getInvocation();
78
      // Add any filenames that were explicity passed in the build settings and
79
      // that might be opened, as we want to ensure we don't run source
80
      // minimization on them.
81
55
      DepFS->IgnoredFiles.clear();
82
55
      for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries)
83
219
        DepFS->IgnoredFiles.insert(Entry.Path);
84
55
      for (const auto &Entry : CI.getHeaderSearchOpts().VFSOverlayFiles)
85
1
        DepFS->IgnoredFiles.insert(Entry);
86
87
      // Support for virtual file system overlays on top of the caching
88
      // filesystem.
89
55
      FileMgr->setVirtualFileSystem(createVFSFromCompilerInvocation(
90
55
          CI, Compiler.getDiagnostics(), DepFS));
91
92
      // Pass the skip mappings which should speed up excluded conditional block
93
      // skipping in the preprocessor.
94
55
      if (PPSkipMappings)
95
52
        Compiler.getPreprocessorOpts()
96
52
            .ExcludedConditionalDirectiveSkipMappings = PPSkipMappings;
97
55
    }
98
99
74
    FileMgr->getFileSystemOpts().WorkingDir = std::string(WorkingDirectory);
100
74
    Compiler.setFileManager(FileMgr);
101
74
    Compiler.createSourceManager(*FileMgr);
102
103
    // Create the dependency collector that will collect the produced
104
    // dependencies.
105
    //
106
    // This also moves the existing dependency output options from the
107
    // invocation to the collector. The options in the invocation are reset,
108
    // which ensures that the compiler won't create new dependency collectors,
109
    // and thus won't write out the extra '.d' files to disk.
110
74
    auto Opts = std::make_unique<DependencyOutputOptions>(
111
74
        std::move(Compiler.getInvocation().getDependencyOutputOpts()));
112
    // We need at least one -MT equivalent for the generator to work.
113
74
    if (Opts->Targets.empty())
114
0
      Opts->Targets = {"clang-scan-deps dependency"};
115
116
74
    switch (Format) {
117
65
    case ScanningOutputFormat::Make:
118
65
      Compiler.addDependencyCollector(
119
65
          std::make_shared<DependencyConsumerForwarder>(std::move(Opts),
120
65
                                                        Consumer));
121
65
      break;
122
3
    case ScanningOutputFormat::Full:
123
3
      Compiler.addDependencyCollector(std::make_shared<ModuleDepCollector>(
124
3
          std::move(Opts), Compiler, Consumer));
125
3
      break;
126
62
    }
127
128
    // Consider different header search and diagnostic options to create
129
    // different modules. This avoids the unsound aliasing of module PCMs.
130
    //
131
    // TODO: Implement diagnostic bucketing and header search pruning to reduce
132
    // the impact of strict context hashing.
133
62
    Compiler.getHeaderSearchOpts().ModulesStrictContextHash = true;
134
135
62
    auto Action = std::make_unique<PreprocessOnlyAction>();
136
62
    const bool Result = Compiler.ExecuteAction(*Action);
137
62
    if (!DepFS)
138
18
      FileMgr->clearStatCache();
139
62
    return Result;
140
62
  }
141
142
private:
143
  StringRef WorkingDirectory;
144
  DependencyConsumer &Consumer;
145
  llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
146
  ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;
147
  ScanningOutputFormat Format;
148
};
149
150
} // end anonymous namespace
151
152
DependencyScanningWorker::DependencyScanningWorker(
153
    DependencyScanningService &Service)
154
59
    : Format(Service.getFormat()) {
155
59
  DiagOpts = new DiagnosticOptions();
156
59
  PCHContainerOps = std::make_shared<PCHContainerOperations>();
157
59
  RealFS = llvm::vfs::createPhysicalFileSystem();
158
59
  if (Service.canSkipExcludedPPRanges())
159
58
    PPSkipMappings =
160
58
        std::make_unique<ExcludedPreprocessorDirectiveSkipMapping>();
161
59
  if (Service.getMode() == ScanningMode::MinimizedSourcePreprocessing)
162
49
    DepFS = new DependencyScanningWorkerFilesystem(
163
49
        Service.getSharedCache(), RealFS, PPSkipMappings.get());
164
59
  if (Service.canReuseFileManager())
165
57
    Files = new FileManager(FileSystemOptions(), RealFS);
166
59
}
167
168
static llvm::Error runWithDiags(
169
    DiagnosticOptions *DiagOpts,
170
75
    llvm::function_ref<bool(DiagnosticConsumer &DC)> BodyShouldSucceed) {
171
  // Capture the emitted diagnostics and report them to the client
172
  // in the case of a failure.
173
75
  std::string DiagnosticOutput;
174
75
  llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
175
75
  TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts);
176
177
75
  if (BodyShouldSucceed(DiagPrinter))
178
72
    return llvm::Error::success();
179
3
  return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
180
3
                                             llvm::inconvertibleErrorCode());
181
3
}
182
183
llvm::Error DependencyScanningWorker::computeDependencies(
184
    const std::string &Input, StringRef WorkingDirectory,
185
75
    const CompilationDatabase &CDB, DependencyConsumer &Consumer) {
186
75
  RealFS->setCurrentWorkingDirectory(WorkingDirectory);
187
75
  return runWithDiags(DiagOpts.get(), [&](DiagnosticConsumer &DC) {
188
    /// Create the tool that uses the underlying file system to ensure that any
189
    /// file system requests that are made by the driver do not go through the
190
    /// dependency scanning filesystem.
191
75
    tooling::ClangTool Tool(CDB, Input, PCHContainerOps, RealFS, Files);
192
75
    Tool.clearArgumentsAdjusters();
193
75
    Tool.setRestoreWorkingDir(false);
194
75
    Tool.setPrintErrorMessage(false);
195
75
    Tool.setDiagnosticConsumer(&DC);
196
75
    DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS,
197
75
                                    PPSkipMappings.get(), Format);
198
75
    return !Tool.run(&Action);
199
75
  });
200
75
}