xref: /freebsd-src/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
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*fe6060f1SDimitry Andric std::vector<std::string> FullDependencies::getAdditionalArgs(
17*fe6060f1SDimitry Andric     std::function<StringRef(ModuleID)> LookupPCMPath,
18*fe6060f1SDimitry Andric     std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const {
19*fe6060f1SDimitry Andric   std::vector<std::string> Ret = getAdditionalArgsWithoutModulePaths();
205ffd83dbSDimitry Andric 
21*fe6060f1SDimitry Andric   std::vector<std::string> PCMPaths;
22*fe6060f1SDimitry Andric   std::vector<std::string> ModMapPaths;
23*fe6060f1SDimitry Andric   dependencies::detail::collectPCMAndModuleMapPaths(
24*fe6060f1SDimitry Andric       ClangModuleDeps, LookupPCMPath, LookupModuleDeps, PCMPaths, ModMapPaths);
25*fe6060f1SDimitry Andric   for (const std::string &PCMPath : PCMPaths)
26*fe6060f1SDimitry Andric     Ret.push_back("-fmodule-file=" + PCMPath);
27*fe6060f1SDimitry Andric   for (const std::string &ModMapPath : ModMapPaths)
28*fe6060f1SDimitry Andric     Ret.push_back("-fmodule-map-file=" + ModMapPath);
295ffd83dbSDimitry Andric 
305ffd83dbSDimitry Andric   return Ret;
315ffd83dbSDimitry Andric }
325ffd83dbSDimitry Andric 
33*fe6060f1SDimitry Andric std::vector<std::string>
34*fe6060f1SDimitry Andric FullDependencies::getAdditionalArgsWithoutModulePaths() const {
35*fe6060f1SDimitry Andric   std::vector<std::string> Args{
36*fe6060f1SDimitry Andric       "-fno-implicit-modules",
37*fe6060f1SDimitry Andric       "-fno-implicit-module-maps",
38*fe6060f1SDimitry Andric   };
39*fe6060f1SDimitry Andric 
40*fe6060f1SDimitry Andric   for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) {
41*fe6060f1SDimitry Andric     Args.push_back("-fmodule-file=" + PMD.ModuleName + "=" + PMD.PCMFile);
42*fe6060f1SDimitry Andric     Args.push_back("-fmodule-map-file=" + PMD.ModuleMapFile);
43*fe6060f1SDimitry Andric   }
44*fe6060f1SDimitry Andric 
45*fe6060f1SDimitry Andric   return Args;
46*fe6060f1SDimitry Andric }
47*fe6060f1SDimitry Andric 
48480093f4SDimitry Andric DependencyScanningTool::DependencyScanningTool(
49480093f4SDimitry Andric     DependencyScanningService &Service)
505ffd83dbSDimitry Andric     : Worker(Service) {}
51a7dea167SDimitry Andric 
52480093f4SDimitry Andric llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
53480093f4SDimitry Andric     const tooling::CompilationDatabase &Compilations, StringRef CWD) {
54a7dea167SDimitry Andric   /// Prints out all of the gathered dependencies into a string.
55480093f4SDimitry Andric   class MakeDependencyPrinterConsumer : public DependencyConsumer {
56a7dea167SDimitry Andric   public:
57*fe6060f1SDimitry Andric     void
58*fe6060f1SDimitry Andric     handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
59a7dea167SDimitry Andric       this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
60*fe6060f1SDimitry Andric     }
61*fe6060f1SDimitry Andric 
62*fe6060f1SDimitry Andric     void handleFileDependency(StringRef File) override {
635ffd83dbSDimitry Andric       Dependencies.push_back(std::string(File));
64a7dea167SDimitry Andric     }
65a7dea167SDimitry Andric 
66*fe6060f1SDimitry Andric     void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
67*fe6060f1SDimitry Andric       // Same as `handleModuleDependency`.
68*fe6060f1SDimitry Andric     }
69*fe6060f1SDimitry Andric 
70480093f4SDimitry Andric     void handleModuleDependency(ModuleDeps MD) override {
71480093f4SDimitry Andric       // These are ignored for the make format as it can't support the full
72480093f4SDimitry Andric       // set of deps, and handleFileDependency handles enough for implicitly
73480093f4SDimitry Andric       // built modules to work.
74480093f4SDimitry Andric     }
75480093f4SDimitry Andric 
76480093f4SDimitry Andric     void handleContextHash(std::string Hash) override {}
77480093f4SDimitry Andric 
78a7dea167SDimitry Andric     void printDependencies(std::string &S) {
79*fe6060f1SDimitry Andric       assert(Opts && "Handled dependency output options.");
80a7dea167SDimitry Andric 
81a7dea167SDimitry Andric       class DependencyPrinter : public DependencyFileGenerator {
82a7dea167SDimitry Andric       public:
83a7dea167SDimitry Andric         DependencyPrinter(DependencyOutputOptions &Opts,
84a7dea167SDimitry Andric                           ArrayRef<std::string> Dependencies)
85a7dea167SDimitry Andric             : DependencyFileGenerator(Opts) {
86a7dea167SDimitry Andric           for (const auto &Dep : Dependencies)
87a7dea167SDimitry Andric             addDependency(Dep);
88a7dea167SDimitry Andric         }
89a7dea167SDimitry Andric 
90a7dea167SDimitry Andric         void printDependencies(std::string &S) {
91a7dea167SDimitry Andric           llvm::raw_string_ostream OS(S);
92a7dea167SDimitry Andric           outputDependencyFile(OS);
93a7dea167SDimitry Andric         }
94a7dea167SDimitry Andric       };
95a7dea167SDimitry Andric 
96a7dea167SDimitry Andric       DependencyPrinter Generator(*Opts, Dependencies);
97a7dea167SDimitry Andric       Generator.printDependencies(S);
98a7dea167SDimitry Andric     }
99a7dea167SDimitry Andric 
100a7dea167SDimitry Andric   private:
101a7dea167SDimitry Andric     std::unique_ptr<DependencyOutputOptions> Opts;
102a7dea167SDimitry Andric     std::vector<std::string> Dependencies;
103a7dea167SDimitry Andric   };
104a7dea167SDimitry Andric 
1055ffd83dbSDimitry Andric   // We expect a single command here because if a source file occurs multiple
1065ffd83dbSDimitry Andric   // times in the original CDB, then `computeDependencies` would run the
1075ffd83dbSDimitry Andric   // `DependencyScanningAction` once for every time the input occured in the
1085ffd83dbSDimitry Andric   // CDB. Instead we split up the CDB into single command chunks to avoid this
1095ffd83dbSDimitry Andric   // behavior.
1105ffd83dbSDimitry Andric   assert(Compilations.getAllCompileCommands().size() == 1 &&
1115ffd83dbSDimitry Andric          "Expected a compilation database with a single command!");
1125ffd83dbSDimitry Andric   std::string Input = Compilations.getAllCompileCommands().front().Filename;
1135ffd83dbSDimitry Andric 
1145ffd83dbSDimitry Andric   MakeDependencyPrinterConsumer Consumer;
1155ffd83dbSDimitry Andric   auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer);
1165ffd83dbSDimitry Andric   if (Result)
1175ffd83dbSDimitry Andric     return std::move(Result);
1185ffd83dbSDimitry Andric   std::string Output;
1195ffd83dbSDimitry Andric   Consumer.printDependencies(Output);
1205ffd83dbSDimitry Andric   return Output;
1215ffd83dbSDimitry Andric }
1225ffd83dbSDimitry Andric 
1235ffd83dbSDimitry Andric llvm::Expected<FullDependenciesResult>
1245ffd83dbSDimitry Andric DependencyScanningTool::getFullDependencies(
1255ffd83dbSDimitry Andric     const tooling::CompilationDatabase &Compilations, StringRef CWD,
1265ffd83dbSDimitry Andric     const llvm::StringSet<> &AlreadySeen) {
127480093f4SDimitry Andric   class FullDependencyPrinterConsumer : public DependencyConsumer {
128480093f4SDimitry Andric   public:
1295ffd83dbSDimitry Andric     FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen)
1305ffd83dbSDimitry Andric         : AlreadySeen(AlreadySeen) {}
1315ffd83dbSDimitry Andric 
132*fe6060f1SDimitry Andric     void
133*fe6060f1SDimitry Andric     handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {}
134*fe6060f1SDimitry Andric 
135*fe6060f1SDimitry Andric     void handleFileDependency(StringRef File) override {
1365ffd83dbSDimitry Andric       Dependencies.push_back(std::string(File));
137480093f4SDimitry Andric     }
138480093f4SDimitry Andric 
139*fe6060f1SDimitry Andric     void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
140*fe6060f1SDimitry Andric       PrebuiltModuleDeps.emplace_back(std::move(PMD));
141*fe6060f1SDimitry Andric     }
142*fe6060f1SDimitry Andric 
143480093f4SDimitry Andric     void handleModuleDependency(ModuleDeps MD) override {
144*fe6060f1SDimitry Andric       ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD);
145480093f4SDimitry Andric     }
146480093f4SDimitry Andric 
147480093f4SDimitry Andric     void handleContextHash(std::string Hash) override {
148480093f4SDimitry Andric       ContextHash = std::move(Hash);
149480093f4SDimitry Andric     }
150480093f4SDimitry Andric 
1515ffd83dbSDimitry Andric     FullDependenciesResult getFullDependencies() const {
1525ffd83dbSDimitry Andric       FullDependencies FD;
153480093f4SDimitry Andric 
154*fe6060f1SDimitry Andric       FD.ID.ContextHash = std::move(ContextHash);
155480093f4SDimitry Andric 
1565ffd83dbSDimitry Andric       FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
157480093f4SDimitry Andric 
1585ffd83dbSDimitry Andric       for (auto &&M : ClangModuleDeps) {
1595ffd83dbSDimitry Andric         auto &MD = M.second;
160480093f4SDimitry Andric         if (MD.ImportedByMainFile)
161*fe6060f1SDimitry Andric           FD.ClangModuleDeps.push_back(MD.ID);
162480093f4SDimitry Andric       }
163480093f4SDimitry Andric 
164*fe6060f1SDimitry Andric       FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
165*fe6060f1SDimitry Andric 
1665ffd83dbSDimitry Andric       FullDependenciesResult FDR;
1675ffd83dbSDimitry Andric 
1685ffd83dbSDimitry Andric       for (auto &&M : ClangModuleDeps) {
1695ffd83dbSDimitry Andric         // TODO: Avoid handleModuleDependency even being called for modules
1705ffd83dbSDimitry Andric         //   we've already seen.
1715ffd83dbSDimitry Andric         if (AlreadySeen.count(M.first))
1725ffd83dbSDimitry Andric           continue;
1735ffd83dbSDimitry Andric         FDR.DiscoveredModules.push_back(std::move(M.second));
174480093f4SDimitry Andric       }
175480093f4SDimitry Andric 
1765ffd83dbSDimitry Andric       FDR.FullDeps = std::move(FD);
1775ffd83dbSDimitry Andric       return FDR;
178480093f4SDimitry Andric     }
179480093f4SDimitry Andric 
180480093f4SDimitry Andric   private:
181480093f4SDimitry Andric     std::vector<std::string> Dependencies;
182*fe6060f1SDimitry Andric     std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
183480093f4SDimitry Andric     std::unordered_map<std::string, ModuleDeps> ClangModuleDeps;
184480093f4SDimitry Andric     std::string ContextHash;
1855ffd83dbSDimitry Andric     std::vector<std::string> OutputPaths;
1865ffd83dbSDimitry Andric     const llvm::StringSet<> &AlreadySeen;
187480093f4SDimitry Andric   };
188480093f4SDimitry Andric 
189480093f4SDimitry Andric   // We expect a single command here because if a source file occurs multiple
190480093f4SDimitry Andric   // times in the original CDB, then `computeDependencies` would run the
191480093f4SDimitry Andric   // `DependencyScanningAction` once for every time the input occured in the
192480093f4SDimitry Andric   // CDB. Instead we split up the CDB into single command chunks to avoid this
193480093f4SDimitry Andric   // behavior.
194480093f4SDimitry Andric   assert(Compilations.getAllCompileCommands().size() == 1 &&
195480093f4SDimitry Andric          "Expected a compilation database with a single command!");
196480093f4SDimitry Andric   std::string Input = Compilations.getAllCompileCommands().front().Filename;
197480093f4SDimitry Andric 
1985ffd83dbSDimitry Andric   FullDependencyPrinterConsumer Consumer(AlreadySeen);
1995ffd83dbSDimitry Andric   llvm::Error Result =
200a7dea167SDimitry Andric       Worker.computeDependencies(Input, CWD, Compilations, Consumer);
201a7dea167SDimitry Andric   if (Result)
202a7dea167SDimitry Andric     return std::move(Result);
2035ffd83dbSDimitry Andric   return Consumer.getFullDependencies();
204a7dea167SDimitry Andric }
205a7dea167SDimitry Andric 
206a7dea167SDimitry Andric } // end namespace dependencies
207a7dea167SDimitry Andric } // end namespace tooling
208a7dea167SDimitry Andric } // end namespace clang
209