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