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