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 using namespace clang;
13 using namespace tooling;
14 using namespace dependencies;
15 
16 std::vector<std::string> FullDependencies::getCommandLine(
17     llvm::function_ref<std::string(const ModuleID &, ModuleOutputKind)>
18         LookupModuleOutput) const {
19   std::vector<std::string> Ret = getCommandLineWithoutModulePaths();
20 
21   for (ModuleID MID : ClangModuleDeps) {
22     auto PCM = LookupModuleOutput(MID, ModuleOutputKind::ModuleFile);
23     Ret.push_back("-fmodule-file=" + PCM);
24   }
25 
26   return Ret;
27 }
28 
29 std::vector<std::string>
30 FullDependencies::getCommandLineWithoutModulePaths() const {
31   std::vector<std::string> Args = OriginalCommandLine;
32 
33   Args.push_back("-fno-implicit-modules");
34   Args.push_back("-fno-implicit-module-maps");
35   for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps)
36     Args.push_back("-fmodule-file=" + PMD.PCMFile);
37 
38   // These arguments are unused in explicit compiles.
39   llvm::erase_if(Args, [](StringRef Arg) {
40     if (Arg.consume_front("-fmodules-")) {
41       return Arg.startswith("cache-path=") ||
42              Arg.startswith("prune-interval=") ||
43              Arg.startswith("prune-after=") ||
44              Arg == "validate-once-per-build-session";
45     }
46     return Arg.startswith("-fbuild-session-file=");
47   });
48 
49   return Args;
50 }
51 
52 DependencyScanningTool::DependencyScanningTool(
53     DependencyScanningService &Service,
54     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
55     : Worker(Service, std::move(FS)) {}
56 
57 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
58     const std::vector<std::string> &CommandLine, StringRef CWD,
59     llvm::Optional<StringRef> ModuleName) {
60   /// Prints out all of the gathered dependencies into a string.
61   class MakeDependencyPrinterConsumer : public DependencyConsumer {
62   public:
63     void
64     handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
65       this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
66     }
67 
68     void handleFileDependency(StringRef File) override {
69       Dependencies.push_back(std::string(File));
70     }
71 
72     void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
73       // Same as `handleModuleDependency`.
74     }
75 
76     void handleModuleDependency(ModuleDeps MD) override {
77       // These are ignored for the make format as it can't support the full
78       // set of deps, and handleFileDependency handles enough for implicitly
79       // built modules to work.
80     }
81 
82     void handleContextHash(std::string Hash) override {}
83 
84     void printDependencies(std::string &S) {
85       assert(Opts && "Handled dependency output options.");
86 
87       class DependencyPrinter : public DependencyFileGenerator {
88       public:
89         DependencyPrinter(DependencyOutputOptions &Opts,
90                           ArrayRef<std::string> Dependencies)
91             : DependencyFileGenerator(Opts) {
92           for (const auto &Dep : Dependencies)
93             addDependency(Dep);
94         }
95 
96         void printDependencies(std::string &S) {
97           llvm::raw_string_ostream OS(S);
98           outputDependencyFile(OS);
99         }
100       };
101 
102       DependencyPrinter Generator(*Opts, Dependencies);
103       Generator.printDependencies(S);
104     }
105 
106   private:
107     std::unique_ptr<DependencyOutputOptions> Opts;
108     std::vector<std::string> Dependencies;
109   };
110 
111   MakeDependencyPrinterConsumer Consumer;
112   auto Result =
113       Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
114   if (Result)
115     return std::move(Result);
116   std::string Output;
117   Consumer.printDependencies(Output);
118   return Output;
119 }
120 
121 llvm::Expected<FullDependenciesResult>
122 DependencyScanningTool::getFullDependencies(
123     const std::vector<std::string> &CommandLine, StringRef CWD,
124     const llvm::StringSet<> &AlreadySeen,
125     llvm::Optional<StringRef> ModuleName) {
126   class FullDependencyPrinterConsumer : public DependencyConsumer {
127   public:
128     FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen)
129         : AlreadySeen(AlreadySeen) {}
130 
131     void
132     handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {}
133 
134     void handleFileDependency(StringRef File) override {
135       Dependencies.push_back(std::string(File));
136     }
137 
138     void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
139       PrebuiltModuleDeps.emplace_back(std::move(PMD));
140     }
141 
142     void handleModuleDependency(ModuleDeps MD) override {
143       ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD);
144     }
145 
146     void handleContextHash(std::string Hash) override {
147       ContextHash = std::move(Hash);
148     }
149 
150     FullDependenciesResult getFullDependencies(
151         const std::vector<std::string> &OriginalCommandLine) const {
152       FullDependencies FD;
153 
154       FD.OriginalCommandLine =
155           ArrayRef<std::string>(OriginalCommandLine).slice(1);
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     llvm::MapVector<std::string, ModuleDeps, llvm::StringMap<unsigned>> ClangModuleDeps;
187     std::string ContextHash;
188     std::vector<std::string> OutputPaths;
189     const llvm::StringSet<> &AlreadySeen;
190   };
191 
192   FullDependencyPrinterConsumer Consumer(AlreadySeen);
193   llvm::Error Result =
194       Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
195   if (Result)
196     return std::move(Result);
197   return Consumer.getFullDependencies(CommandLine);
198 }
199