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