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