xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
1 //===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===//
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/DependencyScanningTool.h"
10 #include "clang/Frontend/Utils.h"
11 
12 namespace clang{
13 namespace tooling{
14 namespace dependencies{
15 
getAdditionalArgs(std::function<StringRef (ModuleID)> LookupPCMPath,std::function<const ModuleDeps & (ModuleID)> LookupModuleDeps) const16 std::vector<std::string> FullDependencies::getAdditionalArgs(
17     std::function<StringRef(ModuleID)> LookupPCMPath,
18     std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const {
19   std::vector<std::string> Ret{
20       "-fno-implicit-modules",
21       "-fno-implicit-module-maps",
22   };
23 
24   std::vector<std::string> PCMPaths;
25   std::vector<std::string> ModMapPaths;
26   dependencies::detail::collectPCMAndModuleMapPaths(
27       ClangModuleDeps, LookupPCMPath, LookupModuleDeps, PCMPaths, ModMapPaths);
28   for (const std::string &PCMPath : PCMPaths)
29     Ret.push_back("-fmodule-file=" + PCMPath);
30   for (const std::string &ModMapPath : ModMapPaths)
31     Ret.push_back("-fmodule-map-file=" + ModMapPath);
32 
33   return Ret;
34 }
35 
36 std::vector<std::string>
getAdditionalArgsWithoutModulePaths() const37 FullDependencies::getAdditionalArgsWithoutModulePaths() const {
38   return {
39       "-fno-implicit-modules",
40       "-fno-implicit-module-maps",
41   };
42 }
43 
DependencyScanningTool(DependencyScanningService & Service)44 DependencyScanningTool::DependencyScanningTool(
45     DependencyScanningService &Service)
46     : Worker(Service) {}
47 
getDependencyFile(const tooling::CompilationDatabase & Compilations,StringRef CWD)48 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
49     const tooling::CompilationDatabase &Compilations, StringRef CWD) {
50   /// Prints out all of the gathered dependencies into a string.
51   class MakeDependencyPrinterConsumer : public DependencyConsumer {
52   public:
53     void handleFileDependency(const DependencyOutputOptions &Opts,
54                               StringRef File) override {
55       if (!this->Opts)
56         this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
57       Dependencies.push_back(std::string(File));
58     }
59 
60     void handleModuleDependency(ModuleDeps MD) override {
61       // These are ignored for the make format as it can't support the full
62       // set of deps, and handleFileDependency handles enough for implicitly
63       // built modules to work.
64     }
65 
66     void handleContextHash(std::string Hash) override {}
67 
68     void printDependencies(std::string &S) {
69       if (!Opts)
70         return;
71 
72       class DependencyPrinter : public DependencyFileGenerator {
73       public:
74         DependencyPrinter(DependencyOutputOptions &Opts,
75                           ArrayRef<std::string> Dependencies)
76             : DependencyFileGenerator(Opts) {
77           for (const auto &Dep : Dependencies)
78             addDependency(Dep);
79         }
80 
81         void printDependencies(std::string &S) {
82           llvm::raw_string_ostream OS(S);
83           outputDependencyFile(OS);
84         }
85       };
86 
87       DependencyPrinter Generator(*Opts, Dependencies);
88       Generator.printDependencies(S);
89     }
90 
91   private:
92     std::unique_ptr<DependencyOutputOptions> Opts;
93     std::vector<std::string> Dependencies;
94   };
95 
96   // We expect a single command here because if a source file occurs multiple
97   // times in the original CDB, then `computeDependencies` would run the
98   // `DependencyScanningAction` once for every time the input occured in the
99   // CDB. Instead we split up the CDB into single command chunks to avoid this
100   // behavior.
101   assert(Compilations.getAllCompileCommands().size() == 1 &&
102          "Expected a compilation database with a single command!");
103   std::string Input = Compilations.getAllCompileCommands().front().Filename;
104 
105   MakeDependencyPrinterConsumer Consumer;
106   auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer);
107   if (Result)
108     return std::move(Result);
109   std::string Output;
110   Consumer.printDependencies(Output);
111   return Output;
112 }
113 
114 llvm::Expected<FullDependenciesResult>
getFullDependencies(const tooling::CompilationDatabase & Compilations,StringRef CWD,const llvm::StringSet<> & AlreadySeen)115 DependencyScanningTool::getFullDependencies(
116     const tooling::CompilationDatabase &Compilations, StringRef CWD,
117     const llvm::StringSet<> &AlreadySeen) {
118   class FullDependencyPrinterConsumer : public DependencyConsumer {
119   public:
120     FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen)
121         : AlreadySeen(AlreadySeen) {}
122 
123     void handleFileDependency(const DependencyOutputOptions &Opts,
124                               StringRef File) override {
125       Dependencies.push_back(std::string(File));
126     }
127 
128     void handleModuleDependency(ModuleDeps MD) override {
129       ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD);
130     }
131 
132     void handleContextHash(std::string Hash) override {
133       ContextHash = std::move(Hash);
134     }
135 
136     FullDependenciesResult getFullDependencies() const {
137       FullDependencies FD;
138 
139       FD.ID.ContextHash = std::move(ContextHash);
140 
141       FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
142 
143       for (auto &&M : ClangModuleDeps) {
144         auto &MD = M.second;
145         if (MD.ImportedByMainFile)
146           FD.ClangModuleDeps.push_back(MD.ID);
147       }
148 
149       FullDependenciesResult FDR;
150 
151       for (auto &&M : ClangModuleDeps) {
152         // TODO: Avoid handleModuleDependency even being called for modules
153         //   we've already seen.
154         if (AlreadySeen.count(M.first))
155           continue;
156         FDR.DiscoveredModules.push_back(std::move(M.second));
157       }
158 
159       FDR.FullDeps = std::move(FD);
160       return FDR;
161     }
162 
163   private:
164     std::vector<std::string> Dependencies;
165     std::unordered_map<std::string, ModuleDeps> ClangModuleDeps;
166     std::string ContextHash;
167     std::vector<std::string> OutputPaths;
168     const llvm::StringSet<> &AlreadySeen;
169   };
170 
171   // We expect a single command here because if a source file occurs multiple
172   // times in the original CDB, then `computeDependencies` would run the
173   // `DependencyScanningAction` once for every time the input occured in the
174   // CDB. Instead we split up the CDB into single command chunks to avoid this
175   // behavior.
176   assert(Compilations.getAllCompileCommands().size() == 1 &&
177          "Expected a compilation database with a single command!");
178   std::string Input = Compilations.getAllCompileCommands().front().Filename;
179 
180   FullDependencyPrinterConsumer Consumer(AlreadySeen);
181   llvm::Error Result =
182       Worker.computeDependencies(Input, CWD, Compilations, Consumer);
183   if (Result)
184     return std::move(Result);
185   return Consumer.getFullDependencies();
186 }
187 
188 } // end namespace dependencies
189 } // end namespace tooling
190 } // end namespace clang
191