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