1e5dd7070Spatrick //===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick
9e5dd7070Spatrick #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
10e5dd7070Spatrick #include "clang/Frontend/Utils.h"
11*12c85518Srobert #include <optional>
12e5dd7070Spatrick
13*12c85518Srobert using namespace clang;
14*12c85518Srobert using namespace tooling;
15*12c85518Srobert using namespace dependencies;
16e5dd7070Spatrick
17*12c85518Srobert static std::vector<std::string>
makeTUCommandLineWithoutPaths(ArrayRef<std::string> OriginalCommandLine)18*12c85518Srobert makeTUCommandLineWithoutPaths(ArrayRef<std::string> OriginalCommandLine) {
19*12c85518Srobert std::vector<std::string> Args = OriginalCommandLine;
20ec727ea7Spatrick
21*12c85518Srobert Args.push_back("-fno-implicit-modules");
22*12c85518Srobert Args.push_back("-fno-implicit-module-maps");
23ec727ea7Spatrick
24*12c85518Srobert // These arguments are unused in explicit compiles.
25*12c85518Srobert llvm::erase_if(Args, [](StringRef Arg) {
26*12c85518Srobert if (Arg.consume_front("-fmodules-")) {
27*12c85518Srobert return Arg.startswith("cache-path=") ||
28*12c85518Srobert Arg.startswith("prune-interval=") ||
29*12c85518Srobert Arg.startswith("prune-after=") ||
30*12c85518Srobert Arg == "validate-once-per-build-session";
31ec727ea7Spatrick }
32*12c85518Srobert return Arg.startswith("-fbuild-session-file=");
33*12c85518Srobert });
34a9ac8606Spatrick
35a9ac8606Spatrick return Args;
36a9ac8606Spatrick }
37a9ac8606Spatrick
DependencyScanningTool(DependencyScanningService & Service,llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)38e5dd7070Spatrick DependencyScanningTool::DependencyScanningTool(
39*12c85518Srobert DependencyScanningService &Service,
40*12c85518Srobert llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
41*12c85518Srobert : Worker(Service, std::move(FS)) {}
42e5dd7070Spatrick
43*12c85518Srobert namespace {
44e5dd7070Spatrick /// Prints out all of the gathered dependencies into a string.
45e5dd7070Spatrick class MakeDependencyPrinterConsumer : public DependencyConsumer {
46e5dd7070Spatrick public:
handleBuildCommand(Command)47*12c85518Srobert void handleBuildCommand(Command) override {}
48*12c85518Srobert
49a9ac8606Spatrick void
handleDependencyOutputOpts(const DependencyOutputOptions & Opts)50a9ac8606Spatrick handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
51e5dd7070Spatrick this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
52a9ac8606Spatrick }
53a9ac8606Spatrick
handleFileDependency(StringRef File)54a9ac8606Spatrick void handleFileDependency(StringRef File) override {
55ec727ea7Spatrick Dependencies.push_back(std::string(File));
56e5dd7070Spatrick }
57e5dd7070Spatrick
handlePrebuiltModuleDependency(PrebuiltModuleDep PMD)58a9ac8606Spatrick void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
59a9ac8606Spatrick // Same as `handleModuleDependency`.
60a9ac8606Spatrick }
61a9ac8606Spatrick
handleModuleDependency(ModuleDeps MD)62e5dd7070Spatrick void handleModuleDependency(ModuleDeps MD) override {
63e5dd7070Spatrick // These are ignored for the make format as it can't support the full
64e5dd7070Spatrick // set of deps, and handleFileDependency handles enough for implicitly
65e5dd7070Spatrick // built modules to work.
66e5dd7070Spatrick }
67e5dd7070Spatrick
handleContextHash(std::string Hash)68e5dd7070Spatrick void handleContextHash(std::string Hash) override {}
69e5dd7070Spatrick
lookupModuleOutput(const ModuleID & ID,ModuleOutputKind Kind)70*12c85518Srobert std::string lookupModuleOutput(const ModuleID &ID,
71*12c85518Srobert ModuleOutputKind Kind) override {
72*12c85518Srobert llvm::report_fatal_error("unexpected call to lookupModuleOutput");
73*12c85518Srobert }
74*12c85518Srobert
printDependencies(std::string & S)75e5dd7070Spatrick void printDependencies(std::string &S) {
76a9ac8606Spatrick assert(Opts && "Handled dependency output options.");
77e5dd7070Spatrick
78e5dd7070Spatrick class DependencyPrinter : public DependencyFileGenerator {
79e5dd7070Spatrick public:
80e5dd7070Spatrick DependencyPrinter(DependencyOutputOptions &Opts,
81e5dd7070Spatrick ArrayRef<std::string> Dependencies)
82e5dd7070Spatrick : DependencyFileGenerator(Opts) {
83e5dd7070Spatrick for (const auto &Dep : Dependencies)
84e5dd7070Spatrick addDependency(Dep);
85e5dd7070Spatrick }
86e5dd7070Spatrick
87e5dd7070Spatrick void printDependencies(std::string &S) {
88e5dd7070Spatrick llvm::raw_string_ostream OS(S);
89e5dd7070Spatrick outputDependencyFile(OS);
90e5dd7070Spatrick }
91e5dd7070Spatrick };
92e5dd7070Spatrick
93e5dd7070Spatrick DependencyPrinter Generator(*Opts, Dependencies);
94e5dd7070Spatrick Generator.printDependencies(S);
95e5dd7070Spatrick }
96e5dd7070Spatrick
97*12c85518Srobert protected:
98e5dd7070Spatrick std::unique_ptr<DependencyOutputOptions> Opts;
99e5dd7070Spatrick std::vector<std::string> Dependencies;
100e5dd7070Spatrick };
101*12c85518Srobert } // anonymous namespace
102e5dd7070Spatrick
getDependencyFile(const std::vector<std::string> & CommandLine,StringRef CWD,std::optional<StringRef> ModuleName)103*12c85518Srobert llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
104*12c85518Srobert const std::vector<std::string> &CommandLine, StringRef CWD,
105*12c85518Srobert std::optional<StringRef> ModuleName) {
106ec727ea7Spatrick MakeDependencyPrinterConsumer Consumer;
107*12c85518Srobert auto Result =
108*12c85518Srobert Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
109ec727ea7Spatrick if (Result)
110ec727ea7Spatrick return std::move(Result);
111ec727ea7Spatrick std::string Output;
112ec727ea7Spatrick Consumer.printDependencies(Output);
113ec727ea7Spatrick return Output;
114ec727ea7Spatrick }
115ec727ea7Spatrick
getP1689ModuleDependencyFile(const CompileCommand & Command,StringRef CWD,std::string & MakeformatOutput,std::string & MakeformatOutputPath)116*12c85518Srobert llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
117*12c85518Srobert const CompileCommand &Command, StringRef CWD,
118*12c85518Srobert std::string &MakeformatOutput, std::string &MakeformatOutputPath) {
119*12c85518Srobert class P1689ModuleDependencyPrinterConsumer
120*12c85518Srobert : public MakeDependencyPrinterConsumer {
121*12c85518Srobert public:
122*12c85518Srobert P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
123*12c85518Srobert const CompileCommand &Command)
124*12c85518Srobert : Filename(Command.Filename), Rule(Rule) {
125*12c85518Srobert Rule.PrimaryOutput = Command.Output;
126*12c85518Srobert }
127*12c85518Srobert
128*12c85518Srobert void handleProvidedAndRequiredStdCXXModules(
129*12c85518Srobert std::optional<P1689ModuleInfo> Provided,
130*12c85518Srobert std::vector<P1689ModuleInfo> Requires) override {
131*12c85518Srobert Rule.Provides = Provided;
132*12c85518Srobert if (Rule.Provides)
133*12c85518Srobert Rule.Provides->SourcePath = Filename.str();
134*12c85518Srobert Rule.Requires = Requires;
135*12c85518Srobert }
136*12c85518Srobert
137*12c85518Srobert StringRef getMakeFormatDependencyOutputPath() {
138*12c85518Srobert if (Opts->OutputFormat != DependencyOutputFormat::Make)
139*12c85518Srobert return {};
140*12c85518Srobert return Opts->OutputFile;
141*12c85518Srobert }
142*12c85518Srobert
143*12c85518Srobert private:
144*12c85518Srobert StringRef Filename;
145*12c85518Srobert P1689Rule &Rule;
146*12c85518Srobert };
147*12c85518Srobert
148*12c85518Srobert P1689Rule Rule;
149*12c85518Srobert P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command);
150*12c85518Srobert auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer);
151*12c85518Srobert if (Result)
152*12c85518Srobert return std::move(Result);
153*12c85518Srobert
154*12c85518Srobert MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
155*12c85518Srobert if (!MakeformatOutputPath.empty())
156*12c85518Srobert Consumer.printDependencies(MakeformatOutput);
157*12c85518Srobert return Rule;
158*12c85518Srobert }
159*12c85518Srobert
160ec727ea7Spatrick llvm::Expected<FullDependenciesResult>
getFullDependencies(const std::vector<std::string> & CommandLine,StringRef CWD,const llvm::StringSet<> & AlreadySeen,LookupModuleOutputCallback LookupModuleOutput,std::optional<StringRef> ModuleName)161ec727ea7Spatrick DependencyScanningTool::getFullDependencies(
162*12c85518Srobert const std::vector<std::string> &CommandLine, StringRef CWD,
163*12c85518Srobert const llvm::StringSet<> &AlreadySeen,
164*12c85518Srobert LookupModuleOutputCallback LookupModuleOutput,
165*12c85518Srobert std::optional<StringRef> ModuleName) {
166*12c85518Srobert FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput,
167*12c85518Srobert Worker.shouldEagerLoadModules());
168*12c85518Srobert llvm::Error Result =
169*12c85518Srobert Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
170*12c85518Srobert if (Result)
171*12c85518Srobert return std::move(Result);
172*12c85518Srobert return Consumer.takeFullDependencies();
173e5dd7070Spatrick }
174e5dd7070Spatrick
175*12c85518Srobert llvm::Expected<FullDependenciesResult>
getFullDependenciesLegacyDriverCommand(const std::vector<std::string> & CommandLine,StringRef CWD,const llvm::StringSet<> & AlreadySeen,LookupModuleOutputCallback LookupModuleOutput,std::optional<StringRef> ModuleName)176*12c85518Srobert DependencyScanningTool::getFullDependenciesLegacyDriverCommand(
177*12c85518Srobert const std::vector<std::string> &CommandLine, StringRef CWD,
178*12c85518Srobert const llvm::StringSet<> &AlreadySeen,
179*12c85518Srobert LookupModuleOutputCallback LookupModuleOutput,
180*12c85518Srobert std::optional<StringRef> ModuleName) {
181*12c85518Srobert FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput,
182*12c85518Srobert Worker.shouldEagerLoadModules());
183*12c85518Srobert llvm::Error Result =
184*12c85518Srobert Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
185*12c85518Srobert if (Result)
186*12c85518Srobert return std::move(Result);
187*12c85518Srobert return Consumer.getFullDependenciesLegacyDriverCommand(CommandLine);
188a9ac8606Spatrick }
189a9ac8606Spatrick
takeFullDependencies()190*12c85518Srobert FullDependenciesResult FullDependencyConsumer::takeFullDependencies() {
191*12c85518Srobert FullDependenciesResult FDR;
192*12c85518Srobert FullDependencies &FD = FDR.FullDeps;
193e5dd7070Spatrick
194a9ac8606Spatrick FD.ID.ContextHash = std::move(ContextHash);
195*12c85518Srobert FD.FileDeps = std::move(Dependencies);
196*12c85518Srobert FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
197*12c85518Srobert FD.Commands = std::move(Commands);
198e5dd7070Spatrick
199ec727ea7Spatrick for (auto &&M : ClangModuleDeps) {
200ec727ea7Spatrick auto &MD = M.second;
201e5dd7070Spatrick if (MD.ImportedByMainFile)
202a9ac8606Spatrick FD.ClangModuleDeps.push_back(MD.ID);
203*12c85518Srobert // TODO: Avoid handleModuleDependency even being called for modules
204*12c85518Srobert // we've already seen.
205*12c85518Srobert if (AlreadySeen.count(M.first))
206*12c85518Srobert continue;
207*12c85518Srobert FDR.DiscoveredModules.push_back(std::move(MD));
208*12c85518Srobert }
209*12c85518Srobert
210*12c85518Srobert return FDR;
211*12c85518Srobert }
212*12c85518Srobert
213*12c85518Srobert FullDependenciesResult
getFullDependenciesLegacyDriverCommand(const std::vector<std::string> & OriginalCommandLine) const214*12c85518Srobert FullDependencyConsumer::getFullDependenciesLegacyDriverCommand(
215*12c85518Srobert const std::vector<std::string> &OriginalCommandLine) const {
216*12c85518Srobert FullDependencies FD;
217*12c85518Srobert
218*12c85518Srobert FD.DriverCommandLine = makeTUCommandLineWithoutPaths(
219*12c85518Srobert ArrayRef<std::string>(OriginalCommandLine).slice(1));
220*12c85518Srobert
221*12c85518Srobert FD.ID.ContextHash = std::move(ContextHash);
222*12c85518Srobert
223*12c85518Srobert FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
224*12c85518Srobert
225*12c85518Srobert for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps)
226*12c85518Srobert FD.DriverCommandLine.push_back("-fmodule-file=" + PMD.PCMFile);
227*12c85518Srobert
228*12c85518Srobert for (auto &&M : ClangModuleDeps) {
229*12c85518Srobert auto &MD = M.second;
230*12c85518Srobert if (MD.ImportedByMainFile) {
231*12c85518Srobert FD.ClangModuleDeps.push_back(MD.ID);
232*12c85518Srobert auto PCMPath = LookupModuleOutput(MD.ID, ModuleOutputKind::ModuleFile);
233*12c85518Srobert if (EagerLoadModules) {
234*12c85518Srobert FD.DriverCommandLine.push_back("-fmodule-file=" + PCMPath);
235*12c85518Srobert } else {
236*12c85518Srobert FD.DriverCommandLine.push_back("-fmodule-map-file=" +
237*12c85518Srobert MD.ClangModuleMapFile);
238*12c85518Srobert FD.DriverCommandLine.push_back("-fmodule-file=" + MD.ID.ModuleName +
239*12c85518Srobert "=" + PCMPath);
240*12c85518Srobert }
241*12c85518Srobert }
242e5dd7070Spatrick }
243e5dd7070Spatrick
244a9ac8606Spatrick FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
245a9ac8606Spatrick
246ec727ea7Spatrick FullDependenciesResult FDR;
247ec727ea7Spatrick
248ec727ea7Spatrick for (auto &&M : ClangModuleDeps) {
249ec727ea7Spatrick // TODO: Avoid handleModuleDependency even being called for modules
250ec727ea7Spatrick // we've already seen.
251ec727ea7Spatrick if (AlreadySeen.count(M.first))
252ec727ea7Spatrick continue;
253ec727ea7Spatrick FDR.DiscoveredModules.push_back(std::move(M.second));
254e5dd7070Spatrick }
255e5dd7070Spatrick
256ec727ea7Spatrick FDR.FullDeps = std::move(FD);
257ec727ea7Spatrick return FDR;
258e5dd7070Spatrick }
259