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