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