1 | //===--- GlobalCompilationDatabase.h -----------------------------*- C++-*-===// |
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 | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H |
10 | #define |
11 | |
12 | #include "support/Function.h" |
13 | #include "support/Path.h" |
14 | #include "support/Threading.h" |
15 | #include "support/ThreadsafeFS.h" |
16 | #include "clang/Tooling/ArgumentsAdjusters.h" |
17 | #include "clang/Tooling/CompilationDatabase.h" |
18 | #include "llvm/ADT/FunctionExtras.h" |
19 | #include "llvm/ADT/StringMap.h" |
20 | #include <memory> |
21 | #include <mutex> |
22 | #include <optional> |
23 | #include <vector> |
24 | |
25 | namespace clang { |
26 | namespace clangd { |
27 | |
28 | struct ProjectInfo { |
29 | // The directory in which the compilation database was discovered. |
30 | // Empty if directory-based compilation database discovery was not used. |
31 | std::string SourceRoot; |
32 | }; |
33 | |
34 | /// Provides compilation arguments used for parsing C and C++ files. |
35 | class GlobalCompilationDatabase { |
36 | public: |
37 | virtual ~GlobalCompilationDatabase() = default; |
38 | |
39 | /// If there are any known-good commands for building this file, returns one. |
40 | virtual std::optional<tooling::CompileCommand> |
41 | getCompileCommand(PathRef File) const = 0; |
42 | |
43 | /// Finds the closest project to \p File. |
44 | virtual std::optional<ProjectInfo> getProjectInfo(PathRef File) const { |
45 | return std::nullopt; |
46 | } |
47 | |
48 | /// Makes a guess at how to build a file. |
49 | /// The default implementation just runs clang on the file. |
50 | /// Clangd should treat the results as unreliable. |
51 | virtual tooling::CompileCommand getFallbackCommand(PathRef File) const; |
52 | |
53 | /// If the CDB does any asynchronous work, wait for it to complete. |
54 | /// For use in tests. |
55 | virtual bool blockUntilIdle(Deadline D) const { return true; } |
56 | |
57 | using CommandChanged = Event<std::vector<std::string>>; |
58 | /// The callback is notified when files may have new compile commands. |
59 | /// The argument is a list of full file paths. |
60 | CommandChanged::Subscription watch(CommandChanged::Listener L) const { |
61 | return OnCommandChanged.observe(L: std::move(L)); |
62 | } |
63 | |
64 | protected: |
65 | mutable CommandChanged OnCommandChanged; |
66 | }; |
67 | |
68 | // Helper class for implementing GlobalCompilationDatabases that wrap others. |
69 | class DelegatingCDB : public GlobalCompilationDatabase { |
70 | public: |
71 | DelegatingCDB(const GlobalCompilationDatabase *Base); |
72 | DelegatingCDB(std::unique_ptr<GlobalCompilationDatabase> Base); |
73 | |
74 | std::optional<tooling::CompileCommand> |
75 | getCompileCommand(PathRef File) const override; |
76 | |
77 | std::optional<ProjectInfo> getProjectInfo(PathRef File) const override; |
78 | |
79 | tooling::CompileCommand getFallbackCommand(PathRef File) const override; |
80 | |
81 | bool blockUntilIdle(Deadline D) const override; |
82 | |
83 | private: |
84 | const GlobalCompilationDatabase *Base; |
85 | std::unique_ptr<GlobalCompilationDatabase> BaseOwner; |
86 | CommandChanged::Subscription BaseChanged; |
87 | }; |
88 | |
89 | /// Gets compile args from tooling::CompilationDatabases built for parent |
90 | /// directories. |
91 | class DirectoryBasedGlobalCompilationDatabase |
92 | : public GlobalCompilationDatabase { |
93 | public: |
94 | struct Options { |
95 | Options(const ThreadsafeFS &TFS) : TFS(TFS) {} |
96 | |
97 | const ThreadsafeFS &TFS; |
98 | // Frequency to check whether e.g. compile_commands.json has changed. |
99 | std::chrono::steady_clock::duration RevalidateAfter = |
100 | std::chrono::seconds(5); |
101 | // Frequency to check whether e.g. compile_commands.json has been created. |
102 | // (This is more expensive to check frequently, as we check many locations). |
103 | std::chrono::steady_clock::duration RevalidateMissingAfter = |
104 | std::chrono::seconds(30); |
105 | // Used to provide per-file configuration. |
106 | std::function<Context(llvm::StringRef)> ContextProvider; |
107 | // Only look for a compilation database in this one fixed directory. |
108 | // FIXME: fold this into config/context mechanism. |
109 | std::optional<Path> CompileCommandsDir; |
110 | }; |
111 | |
112 | DirectoryBasedGlobalCompilationDatabase(const Options &Opts); |
113 | ~DirectoryBasedGlobalCompilationDatabase() override; |
114 | |
115 | /// Scans File's parents looking for compilation databases. |
116 | /// Any extra flags will be added. |
117 | /// Might trigger OnCommandChanged, if CDB wasn't broadcasted yet. |
118 | std::optional<tooling::CompileCommand> |
119 | getCompileCommand(PathRef File) const override; |
120 | |
121 | /// Returns the path to first directory containing a compilation database in |
122 | /// \p File's parents. |
123 | std::optional<ProjectInfo> getProjectInfo(PathRef File) const override; |
124 | |
125 | bool blockUntilIdle(Deadline Timeout) const override; |
126 | |
127 | private: |
128 | Options Opts; |
129 | |
130 | class DirectoryCache; |
131 | // Keyed by possibly-case-folded directory path. |
132 | // We can hand out pointers as they're stable and entries are never removed. |
133 | mutable llvm::StringMap<DirectoryCache> DirCaches; |
134 | mutable std::mutex DirCachesMutex; |
135 | |
136 | std::vector<DirectoryCache *> |
137 | getDirectoryCaches(llvm::ArrayRef<llvm::StringRef> Dirs) const; |
138 | |
139 | struct CDBLookupRequest { |
140 | PathRef FileName; |
141 | // Whether this lookup should trigger discovery of the CDB found. |
142 | bool ShouldBroadcast = false; |
143 | // Cached results newer than this are considered fresh and not checked |
144 | // against disk. |
145 | std::chrono::steady_clock::time_point FreshTime; |
146 | std::chrono::steady_clock::time_point FreshTimeMissing; |
147 | }; |
148 | struct CDBLookupResult { |
149 | std::shared_ptr<const tooling::CompilationDatabase> CDB; |
150 | ProjectInfo PI; |
151 | }; |
152 | std::optional<CDBLookupResult> lookupCDB(CDBLookupRequest Request) const; |
153 | |
154 | class BroadcastThread; |
155 | std::unique_ptr<BroadcastThread> Broadcaster; |
156 | |
157 | // Performs broadcast on governed files. |
158 | void broadcastCDB(CDBLookupResult Res) const; |
159 | |
160 | // cache test calls lookupCDB directly to ensure valid/invalid times. |
161 | friend class DirectoryBasedGlobalCompilationDatabaseCacheTest; |
162 | }; |
163 | |
164 | /// Extracts system include search path from drivers matching QueryDriverGlobs |
165 | /// and adds them to the compile flags. |
166 | /// Returns null when \p QueryDriverGlobs is empty. |
167 | using = llvm::unique_function<void( |
168 | tooling::CompileCommand &, llvm::StringRef) const>; |
169 | SystemIncludeExtractorFn |
170 | (llvm::ArrayRef<std::string> QueryDriverGlobs); |
171 | |
172 | /// Wraps another compilation database, and supports overriding the commands |
173 | /// using an in-memory mapping. |
174 | class OverlayCDB : public DelegatingCDB { |
175 | public: |
176 | // Makes adjustments to a tooling::CompileCommand which will be used to |
177 | // process a file (possibly different from the one in the command). |
178 | using CommandMangler = llvm::unique_function<void(tooling::CompileCommand &, |
179 | StringRef File) const>; |
180 | |
181 | // Base may be null, in which case no entries are inherited. |
182 | // FallbackFlags are added to the fallback compile command. |
183 | // Adjuster is applied to all commands, fallback or not. |
184 | OverlayCDB(const GlobalCompilationDatabase *Base, |
185 | std::vector<std::string> FallbackFlags = {}, |
186 | CommandMangler Mangler = nullptr); |
187 | |
188 | std::optional<tooling::CompileCommand> |
189 | getCompileCommand(PathRef File) const override; |
190 | tooling::CompileCommand getFallbackCommand(PathRef File) const override; |
191 | |
192 | /// Sets or clears the compilation command for a particular file. |
193 | void |
194 | setCompileCommand(PathRef File, |
195 | std::optional<tooling::CompileCommand> CompilationCommand); |
196 | |
197 | private: |
198 | mutable std::mutex Mutex; |
199 | llvm::StringMap<tooling::CompileCommand> Commands; /* GUARDED_BY(Mut) */ |
200 | CommandMangler Mangler; |
201 | std::vector<std::string> FallbackFlags; |
202 | }; |
203 | |
204 | } // namespace clangd |
205 | } // namespace clang |
206 | |
207 | #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H |
208 | |