xref: /freebsd-src/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
1480093f4SDimitry Andric //===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===//
2a7dea167SDimitry Andric //
3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a7dea167SDimitry Andric //
7a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
8a7dea167SDimitry Andric 
9a7dea167SDimitry Andric #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
10a7dea167SDimitry Andric #include "clang/Frontend/Utils.h"
11a7dea167SDimitry Andric 
12a7dea167SDimitry Andric namespace clang{
13a7dea167SDimitry Andric namespace tooling{
14a7dea167SDimitry Andric namespace dependencies{
15a7dea167SDimitry Andric 
16*5ffd83dbSDimitry Andric std::vector<std::string> FullDependencies::getAdditionalCommandLine(
17*5ffd83dbSDimitry Andric     std::function<StringRef(ClangModuleDep)> LookupPCMPath,
18*5ffd83dbSDimitry Andric     std::function<const ModuleDeps &(ClangModuleDep)> LookupModuleDeps) const {
19*5ffd83dbSDimitry Andric   std::vector<std::string> Ret = AdditionalNonPathCommandLine;
20*5ffd83dbSDimitry Andric 
21*5ffd83dbSDimitry Andric   dependencies::detail::appendCommonModuleArguments(
22*5ffd83dbSDimitry Andric       ClangModuleDeps, LookupPCMPath, LookupModuleDeps, Ret);
23*5ffd83dbSDimitry Andric 
24*5ffd83dbSDimitry Andric   return Ret;
25*5ffd83dbSDimitry Andric }
26*5ffd83dbSDimitry Andric 
27480093f4SDimitry Andric DependencyScanningTool::DependencyScanningTool(
28480093f4SDimitry Andric     DependencyScanningService &Service)
29*5ffd83dbSDimitry Andric     : Worker(Service) {}
30a7dea167SDimitry Andric 
31480093f4SDimitry Andric llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
32480093f4SDimitry Andric     const tooling::CompilationDatabase &Compilations, StringRef CWD) {
33a7dea167SDimitry Andric   /// Prints out all of the gathered dependencies into a string.
34480093f4SDimitry Andric   class MakeDependencyPrinterConsumer : public DependencyConsumer {
35a7dea167SDimitry Andric   public:
36a7dea167SDimitry Andric     void handleFileDependency(const DependencyOutputOptions &Opts,
37a7dea167SDimitry Andric                               StringRef File) override {
38a7dea167SDimitry Andric       if (!this->Opts)
39a7dea167SDimitry Andric         this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
40*5ffd83dbSDimitry Andric       Dependencies.push_back(std::string(File));
41a7dea167SDimitry Andric     }
42a7dea167SDimitry Andric 
43480093f4SDimitry Andric     void handleModuleDependency(ModuleDeps MD) override {
44480093f4SDimitry Andric       // These are ignored for the make format as it can't support the full
45480093f4SDimitry Andric       // set of deps, and handleFileDependency handles enough for implicitly
46480093f4SDimitry Andric       // built modules to work.
47480093f4SDimitry Andric     }
48480093f4SDimitry Andric 
49480093f4SDimitry Andric     void handleContextHash(std::string Hash) override {}
50480093f4SDimitry Andric 
51a7dea167SDimitry Andric     void printDependencies(std::string &S) {
52a7dea167SDimitry Andric       if (!Opts)
53a7dea167SDimitry Andric         return;
54a7dea167SDimitry Andric 
55a7dea167SDimitry Andric       class DependencyPrinter : public DependencyFileGenerator {
56a7dea167SDimitry Andric       public:
57a7dea167SDimitry Andric         DependencyPrinter(DependencyOutputOptions &Opts,
58a7dea167SDimitry Andric                           ArrayRef<std::string> Dependencies)
59a7dea167SDimitry Andric             : DependencyFileGenerator(Opts) {
60a7dea167SDimitry Andric           for (const auto &Dep : Dependencies)
61a7dea167SDimitry Andric             addDependency(Dep);
62a7dea167SDimitry Andric         }
63a7dea167SDimitry Andric 
64a7dea167SDimitry Andric         void printDependencies(std::string &S) {
65a7dea167SDimitry Andric           llvm::raw_string_ostream OS(S);
66a7dea167SDimitry Andric           outputDependencyFile(OS);
67a7dea167SDimitry Andric         }
68a7dea167SDimitry Andric       };
69a7dea167SDimitry Andric 
70a7dea167SDimitry Andric       DependencyPrinter Generator(*Opts, Dependencies);
71a7dea167SDimitry Andric       Generator.printDependencies(S);
72a7dea167SDimitry Andric     }
73a7dea167SDimitry Andric 
74a7dea167SDimitry Andric   private:
75a7dea167SDimitry Andric     std::unique_ptr<DependencyOutputOptions> Opts;
76a7dea167SDimitry Andric     std::vector<std::string> Dependencies;
77a7dea167SDimitry Andric   };
78a7dea167SDimitry Andric 
79*5ffd83dbSDimitry Andric   // We expect a single command here because if a source file occurs multiple
80*5ffd83dbSDimitry Andric   // times in the original CDB, then `computeDependencies` would run the
81*5ffd83dbSDimitry Andric   // `DependencyScanningAction` once for every time the input occured in the
82*5ffd83dbSDimitry Andric   // CDB. Instead we split up the CDB into single command chunks to avoid this
83*5ffd83dbSDimitry Andric   // behavior.
84*5ffd83dbSDimitry Andric   assert(Compilations.getAllCompileCommands().size() == 1 &&
85*5ffd83dbSDimitry Andric          "Expected a compilation database with a single command!");
86*5ffd83dbSDimitry Andric   std::string Input = Compilations.getAllCompileCommands().front().Filename;
87*5ffd83dbSDimitry Andric 
88*5ffd83dbSDimitry Andric   MakeDependencyPrinterConsumer Consumer;
89*5ffd83dbSDimitry Andric   auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer);
90*5ffd83dbSDimitry Andric   if (Result)
91*5ffd83dbSDimitry Andric     return std::move(Result);
92*5ffd83dbSDimitry Andric   std::string Output;
93*5ffd83dbSDimitry Andric   Consumer.printDependencies(Output);
94*5ffd83dbSDimitry Andric   return Output;
95*5ffd83dbSDimitry Andric }
96*5ffd83dbSDimitry Andric 
97*5ffd83dbSDimitry Andric llvm::Expected<FullDependenciesResult>
98*5ffd83dbSDimitry Andric DependencyScanningTool::getFullDependencies(
99*5ffd83dbSDimitry Andric     const tooling::CompilationDatabase &Compilations, StringRef CWD,
100*5ffd83dbSDimitry Andric     const llvm::StringSet<> &AlreadySeen) {
101480093f4SDimitry Andric   class FullDependencyPrinterConsumer : public DependencyConsumer {
102480093f4SDimitry Andric   public:
103*5ffd83dbSDimitry Andric     FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen)
104*5ffd83dbSDimitry Andric         : AlreadySeen(AlreadySeen) {}
105*5ffd83dbSDimitry Andric 
106480093f4SDimitry Andric     void handleFileDependency(const DependencyOutputOptions &Opts,
107480093f4SDimitry Andric                               StringRef File) override {
108*5ffd83dbSDimitry Andric       Dependencies.push_back(std::string(File));
109480093f4SDimitry Andric     }
110480093f4SDimitry Andric 
111480093f4SDimitry Andric     void handleModuleDependency(ModuleDeps MD) override {
112480093f4SDimitry Andric       ClangModuleDeps[MD.ContextHash + MD.ModuleName] = std::move(MD);
113480093f4SDimitry Andric     }
114480093f4SDimitry Andric 
115480093f4SDimitry Andric     void handleContextHash(std::string Hash) override {
116480093f4SDimitry Andric       ContextHash = std::move(Hash);
117480093f4SDimitry Andric     }
118480093f4SDimitry Andric 
119*5ffd83dbSDimitry Andric     FullDependenciesResult getFullDependencies() const {
120*5ffd83dbSDimitry Andric       FullDependencies FD;
121480093f4SDimitry Andric 
122*5ffd83dbSDimitry Andric       FD.ContextHash = std::move(ContextHash);
123480093f4SDimitry Andric 
124*5ffd83dbSDimitry Andric       FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
125480093f4SDimitry Andric 
126*5ffd83dbSDimitry Andric       for (auto &&M : ClangModuleDeps) {
127*5ffd83dbSDimitry Andric         auto &MD = M.second;
128480093f4SDimitry Andric         if (MD.ImportedByMainFile)
129*5ffd83dbSDimitry Andric           FD.ClangModuleDeps.push_back({MD.ModuleName, ContextHash});
130480093f4SDimitry Andric       }
131480093f4SDimitry Andric 
132*5ffd83dbSDimitry Andric       FullDependenciesResult FDR;
133*5ffd83dbSDimitry Andric 
134*5ffd83dbSDimitry Andric       for (auto &&M : ClangModuleDeps) {
135*5ffd83dbSDimitry Andric         // TODO: Avoid handleModuleDependency even being called for modules
136*5ffd83dbSDimitry Andric         //   we've already seen.
137*5ffd83dbSDimitry Andric         if (AlreadySeen.count(M.first))
138*5ffd83dbSDimitry Andric           continue;
139*5ffd83dbSDimitry Andric         FDR.DiscoveredModules.push_back(std::move(M.second));
140480093f4SDimitry Andric       }
141480093f4SDimitry Andric 
142*5ffd83dbSDimitry Andric       FDR.FullDeps = std::move(FD);
143*5ffd83dbSDimitry Andric       return FDR;
144480093f4SDimitry Andric     }
145480093f4SDimitry Andric 
146480093f4SDimitry Andric   private:
147480093f4SDimitry Andric     std::vector<std::string> Dependencies;
148480093f4SDimitry Andric     std::unordered_map<std::string, ModuleDeps> ClangModuleDeps;
149480093f4SDimitry Andric     std::string ContextHash;
150*5ffd83dbSDimitry Andric     std::vector<std::string> OutputPaths;
151*5ffd83dbSDimitry Andric     const llvm::StringSet<> &AlreadySeen;
152480093f4SDimitry Andric   };
153480093f4SDimitry Andric 
154480093f4SDimitry Andric   // We expect a single command here because if a source file occurs multiple
155480093f4SDimitry Andric   // times in the original CDB, then `computeDependencies` would run the
156480093f4SDimitry Andric   // `DependencyScanningAction` once for every time the input occured in the
157480093f4SDimitry Andric   // CDB. Instead we split up the CDB into single command chunks to avoid this
158480093f4SDimitry Andric   // behavior.
159480093f4SDimitry Andric   assert(Compilations.getAllCompileCommands().size() == 1 &&
160480093f4SDimitry Andric          "Expected a compilation database with a single command!");
161480093f4SDimitry Andric   std::string Input = Compilations.getAllCompileCommands().front().Filename;
162480093f4SDimitry Andric 
163*5ffd83dbSDimitry Andric   FullDependencyPrinterConsumer Consumer(AlreadySeen);
164*5ffd83dbSDimitry Andric   llvm::Error Result =
165a7dea167SDimitry Andric       Worker.computeDependencies(Input, CWD, Compilations, Consumer);
166a7dea167SDimitry Andric   if (Result)
167a7dea167SDimitry Andric     return std::move(Result);
168*5ffd83dbSDimitry Andric   return Consumer.getFullDependencies();
169a7dea167SDimitry Andric }
170a7dea167SDimitry Andric 
171a7dea167SDimitry Andric } // end namespace dependencies
172a7dea167SDimitry Andric } // end namespace tooling
173a7dea167SDimitry Andric } // end namespace clang
174