Coverage Report

Created: 2020-02-25 14:32

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