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 #include "llvm/Support/JSON.h"
12
toJSONSorted(const llvm::StringSet<> & Set)13 static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
14 std::vector<llvm::StringRef> Strings;
15 for (auto &&I : Set)
16 Strings.push_back(I.getKey());
17 std::sort(Strings.begin(), Strings.end());
18 return llvm::json::Array(Strings);
19 }
20
21 namespace clang{
22 namespace tooling{
23 namespace dependencies{
24
DependencyScanningTool(DependencyScanningService & Service)25 DependencyScanningTool::DependencyScanningTool(
26 DependencyScanningService &Service)
27 : Format(Service.getFormat()), Worker(Service) {
28 }
29
getDependencyFile(const tooling::CompilationDatabase & Compilations,StringRef CWD)30 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
31 const tooling::CompilationDatabase &Compilations, StringRef CWD) {
32 /// Prints out all of the gathered dependencies into a string.
33 class MakeDependencyPrinterConsumer : public DependencyConsumer {
34 public:
35 void handleFileDependency(const DependencyOutputOptions &Opts,
36 StringRef File) override {
37 if (!this->Opts)
38 this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
39 Dependencies.push_back(File);
40 }
41
42 void handleModuleDependency(ModuleDeps MD) override {
43 // These are ignored for the make format as it can't support the full
44 // set of deps, and handleFileDependency handles enough for implicitly
45 // built modules to work.
46 }
47
48 void handleContextHash(std::string Hash) override {}
49
50 void printDependencies(std::string &S) {
51 if (!Opts)
52 return;
53
54 class DependencyPrinter : public DependencyFileGenerator {
55 public:
56 DependencyPrinter(DependencyOutputOptions &Opts,
57 ArrayRef<std::string> Dependencies)
58 : DependencyFileGenerator(Opts) {
59 for (const auto &Dep : Dependencies)
60 addDependency(Dep);
61 }
62
63 void printDependencies(std::string &S) {
64 llvm::raw_string_ostream OS(S);
65 outputDependencyFile(OS);
66 }
67 };
68
69 DependencyPrinter Generator(*Opts, Dependencies);
70 Generator.printDependencies(S);
71 }
72
73 private:
74 std::unique_ptr<DependencyOutputOptions> Opts;
75 std::vector<std::string> Dependencies;
76 };
77
78 class FullDependencyPrinterConsumer : public DependencyConsumer {
79 public:
80 void handleFileDependency(const DependencyOutputOptions &Opts,
81 StringRef File) override {
82 Dependencies.push_back(File);
83 }
84
85 void handleModuleDependency(ModuleDeps MD) override {
86 ClangModuleDeps[MD.ContextHash + MD.ModuleName] = std::move(MD);
87 }
88
89 void handleContextHash(std::string Hash) override {
90 ContextHash = std::move(Hash);
91 }
92
93 void printDependencies(std::string &S, StringRef MainFile) {
94 // Sort the modules by name to get a deterministic order.
95 std::vector<StringRef> Modules;
96 for (auto &&Dep : ClangModuleDeps)
97 Modules.push_back(Dep.first);
98 std::sort(Modules.begin(), Modules.end());
99
100 llvm::raw_string_ostream OS(S);
101
102 using namespace llvm::json;
103
104 Array Imports;
105 for (auto &&ModName : Modules) {
106 auto &MD = ClangModuleDeps[ModName];
107 if (MD.ImportedByMainFile)
108 Imports.push_back(MD.ModuleName);
109 }
110
111 Array Mods;
112 for (auto &&ModName : Modules) {
113 auto &MD = ClangModuleDeps[ModName];
114 Object Mod{
115 {"name", MD.ModuleName},
116 {"file-deps", toJSONSorted(MD.FileDeps)},
117 {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
118 {"clang-modulemap-file", MD.ClangModuleMapFile},
119 };
120 Mods.push_back(std::move(Mod));
121 }
122
123 Object O{
124 {"input-file", MainFile},
125 {"clang-context-hash", ContextHash},
126 {"file-deps", Dependencies},
127 {"clang-module-deps", std::move(Imports)},
128 {"clang-modules", std::move(Mods)},
129 };
130
131 S = llvm::formatv("{0:2},\n", Value(std::move(O))).str();
132 return;
133 }
134
135 private:
136 std::vector<std::string> Dependencies;
137 std::unordered_map<std::string, ModuleDeps> ClangModuleDeps;
138 std::string ContextHash;
139 };
140
141
142 // We expect a single command here because if a source file occurs multiple
143 // times in the original CDB, then `computeDependencies` would run the
144 // `DependencyScanningAction` once for every time the input occured in the
145 // CDB. Instead we split up the CDB into single command chunks to avoid this
146 // behavior.
147 assert(Compilations.getAllCompileCommands().size() == 1 &&
148 "Expected a compilation database with a single command!");
149 std::string Input = Compilations.getAllCompileCommands().front().Filename;
150
151 if (Format == ScanningOutputFormat::Make) {
152 MakeDependencyPrinterConsumer Consumer;
153 auto Result =
154 Worker.computeDependencies(Input, CWD, Compilations, Consumer);
155 if (Result)
156 return std::move(Result);
157 std::string Output;
158 Consumer.printDependencies(Output);
159 return Output;
160 } else {
161 FullDependencyPrinterConsumer Consumer;
162 auto Result =
163 Worker.computeDependencies(Input, CWD, Compilations, Consumer);
164 if (Result)
165 return std::move(Result);
166 std::string Output;
167 Consumer.printDependencies(Output, Input);
168 return Output;
169 }
170 }
171
172 } // end namespace dependencies
173 } // end namespace tooling
174 } // end namespace clang
175