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