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