]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm-project / clang / lib / Tooling / DependencyScanning / DependencyScanningWorker.cpp
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/FrontendActions.h"
12 #include "clang/Frontend/TextDiagnosticPrinter.h"
13 #include "clang/Frontend/Utils.h"
14 #include "clang/Tooling/Tooling.h"
15
16 using namespace clang;
17 using namespace tooling;
18 using namespace dependencies;
19
20 namespace {
21
22 /// Prints out all of the gathered dependencies into a string.
23 class DependencyPrinter : public DependencyFileGenerator {
24 public:
25   DependencyPrinter(std::unique_ptr<DependencyOutputOptions> Opts,
26                     std::string &S)
27       : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), S(S) {}
28
29   void finishedMainFile(DiagnosticsEngine &Diags) override {
30     llvm::raw_string_ostream OS(S);
31     outputDependencyFile(OS);
32   }
33
34 private:
35   std::unique_ptr<DependencyOutputOptions> Opts;
36   std::string &S;
37 };
38
39 /// A proxy file system that doesn't call `chdir` when changing the working
40 /// directory of a clang tool.
41 class ProxyFileSystemWithoutChdir : public llvm::vfs::ProxyFileSystem {
42 public:
43   ProxyFileSystemWithoutChdir(
44       llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
45       : ProxyFileSystem(std::move(FS)) {}
46
47   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
48     assert(!CWD.empty() && "empty CWD");
49     return CWD;
50   }
51
52   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
53     CWD = Path.str();
54     return {};
55   }
56
57 private:
58   std::string CWD;
59 };
60
61 /// A clang tool that runs the preprocessor in a mode that's optimized for
62 /// dependency scanning for the given compiler invocation.
63 class DependencyScanningAction : public tooling::ToolAction {
64 public:
65   DependencyScanningAction(StringRef WorkingDirectory,
66                            std::string &DependencyFileContents)
67       : WorkingDirectory(WorkingDirectory),
68         DependencyFileContents(DependencyFileContents) {}
69
70   bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
71                      FileManager *FileMgr,
72                      std::shared_ptr<PCHContainerOperations> PCHContainerOps,
73                      DiagnosticConsumer *DiagConsumer) override {
74     // Create a compiler instance to handle the actual work.
75     CompilerInstance Compiler(std::move(PCHContainerOps));
76     Compiler.setInvocation(std::move(Invocation));
77     FileMgr->getFileSystemOpts().WorkingDir = WorkingDirectory;
78     Compiler.setFileManager(FileMgr);
79
80     // Don't print 'X warnings and Y errors generated'.
81     Compiler.getDiagnosticOpts().ShowCarets = false;
82     // Create the compiler's actual diagnostics engine.
83     Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
84     if (!Compiler.hasDiagnostics())
85       return false;
86
87     Compiler.createSourceManager(*FileMgr);
88
89     // Create the dependency collector that will collect the produced
90     // dependencies.
91     //
92     // This also moves the existing dependency output options from the
93     // invocation to the collector. The options in the invocation are reset,
94     // which ensures that the compiler won't create new dependency collectors,
95     // and thus won't write out the extra '.d' files to disk.
96     auto Opts = llvm::make_unique<DependencyOutputOptions>(
97         std::move(Compiler.getInvocation().getDependencyOutputOpts()));
98     // We need at least one -MT equivalent for the generator to work.
99     if (Opts->Targets.empty())
100       Opts->Targets = {"clang-scan-deps dependency"};
101     Compiler.addDependencyCollector(std::make_shared<DependencyPrinter>(
102         std::move(Opts), DependencyFileContents));
103
104     auto Action = llvm::make_unique<PreprocessOnlyAction>();
105     const bool Result = Compiler.ExecuteAction(*Action);
106     FileMgr->clearStatCache();
107     return Result;
108   }
109
110 private:
111   StringRef WorkingDirectory;
112   /// The dependency file will be written to this string.
113   std::string &DependencyFileContents;
114 };
115
116 } // end anonymous namespace
117
118 DependencyScanningWorker::DependencyScanningWorker() {
119   DiagOpts = new DiagnosticOptions();
120   PCHContainerOps = std::make_shared<PCHContainerOperations>();
121   /// FIXME: Use the shared file system from the service for fast scanning
122   /// mode.
123   WorkerFS = new ProxyFileSystemWithoutChdir(llvm::vfs::getRealFileSystem());
124 }
125
126 llvm::Expected<std::string>
127 DependencyScanningWorker::getDependencyFile(const std::string &Input,
128                                             StringRef WorkingDirectory,
129                                             const CompilationDatabase &CDB) {
130   // Capture the emitted diagnostics and report them to the client
131   // in the case of a failure.
132   std::string DiagnosticOutput;
133   llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
134   TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.get());
135
136   WorkerFS->setCurrentWorkingDirectory(WorkingDirectory);
137   tooling::ClangTool Tool(CDB, Input, PCHContainerOps, WorkerFS);
138   Tool.clearArgumentsAdjusters();
139   Tool.setRestoreWorkingDir(false);
140   Tool.setPrintErrorMessage(false);
141   Tool.setDiagnosticConsumer(&DiagPrinter);
142   std::string Output;
143   DependencyScanningAction Action(WorkingDirectory, Output);
144   if (Tool.run(&Action)) {
145     return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
146                                                llvm::inconvertibleErrorCode());
147   }
148   return Output;
149 }