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