16
16
#include " llvm/ADT/None.h"
17
17
#include " llvm/ADT/Optional.h"
18
18
#include " llvm/ADT/STLExtras.h"
19
- #include " llvm/ADT/ScopeExit.h"
20
19
#include " llvm/ADT/SmallString.h"
21
20
#include " llvm/Support/FileSystem.h"
22
21
#include " llvm/Support/FileUtilities.h"
23
22
#include " llvm/Support/Path.h"
24
23
#include " llvm/Support/Program.h"
25
- #include < chrono>
26
24
#include < string>
27
25
#include < tuple>
28
26
#include < vector>
@@ -60,117 +58,10 @@ GlobalCompilationDatabase::getFallbackCommand(PathRef File) const {
60
58
return Cmd;
61
59
}
62
60
63
- // Loads and caches the CDB from a single directory.
64
- //
65
- // This class is threadsafe, which is to say we have independent locks for each
66
- // directory we're searching for a CDB.
67
- // Loading is deferred until first access.
68
- //
69
- // The DirectoryBasedCDB keeps a map from path => DirectoryCache.
70
- // Typical usage is to:
71
- // - 1) determine all the paths that might be searched
72
- // - 2) acquire the map lock and get-or-create all the DirectoryCache entries
73
- // - 3) release the map lock and query the caches as desired
74
- //
75
- // FIXME: this should revalidate the cache sometimes
76
- // FIXME: IO should go through a VFS
77
- class DirectoryBasedGlobalCompilationDatabase ::DirectoryCache {
78
- // Absolute canonical path that we're the cache for. (Not case-folded).
79
- const std::string Path;
80
-
81
- // True if we've looked for a CDB here and found none.
82
- // (This makes it possible for get() to return without taking a lock)
83
- // FIXME: this should have an expiry time instead of lasting forever.
84
- std::atomic<bool > FinalizedNoCDB = {false };
85
-
86
- // Guards following cache state.
87
- std::mutex Mu;
88
- // Has cache been filled from disk? FIXME: this should be an expiry time.
89
- bool CachePopulated = false ;
90
- // Whether a new CDB has been loaded but not broadcast yet.
91
- bool NeedsBroadcast = false ;
92
- // Last loaded CDB, meaningful if CachePopulated is set.
93
- // shared_ptr so we can overwrite this when callers are still using the CDB.
94
- std::shared_ptr<tooling::CompilationDatabase> CDB;
95
-
96
- public:
97
- DirectoryCache (llvm::StringRef Path) : Path(Path) {
98
- assert (llvm::sys::path::is_absolute (Path));
99
- }
100
-
101
- // Get the CDB associated with this directory.
102
- // ShouldBroadcast:
103
- // - as input, signals whether the caller is willing to broadcast a
104
- // newly-discovered CDB. (e.g. to trigger background indexing)
105
- // - as output, signals whether the caller should do so.
106
- // (If a new CDB is discovered and ShouldBroadcast is false, we mark the
107
- // CDB as needing broadcast, and broadcast it next time we can).
108
- std::shared_ptr<const tooling::CompilationDatabase>
109
- get (bool &ShouldBroadcast) {
110
- // Fast path for common case without taking lock.
111
- if (FinalizedNoCDB.load ()) {
112
- ShouldBroadcast = false ;
113
- return nullptr ;
114
- }
115
- std::lock_guard<std::mutex> Lock (Mu);
116
- auto RequestBroadcast = llvm::make_scope_exit ([&, OldCDB (CDB.get ())] {
117
- // If we loaded a new CDB, it should be broadcast at some point.
118
- if (CDB != nullptr && CDB.get () != OldCDB)
119
- NeedsBroadcast = true ;
120
- else if (CDB == nullptr ) // nothing to broadcast anymore!
121
- NeedsBroadcast = false ;
122
- // If we have something to broadcast, then do so iff allowed.
123
- if (!ShouldBroadcast)
124
- return ;
125
- ShouldBroadcast = NeedsBroadcast;
126
- NeedsBroadcast = false ;
127
- });
128
-
129
- // For now, we never actually attempt to revalidate a populated cache.
130
- if (CachePopulated)
131
- return CDB;
132
- assert (CDB == nullptr );
133
-
134
- load ();
135
- CachePopulated = true ;
136
-
137
- if (!CDB)
138
- FinalizedNoCDB.store (true );
139
- return CDB;
140
- }
141
-
142
- llvm::StringRef path () const { return Path; }
143
-
144
- private:
145
- // Updates `CDB` from disk state.
146
- void load () {
147
- std::string Error; // ignored, because it's often "didn't find anything".
148
- CDB = tooling::CompilationDatabase::loadFromDirectory (Path, Error);
149
- if (!CDB) {
150
- // Fallback: check for $src/build, the conventional CMake build root.
151
- // Probe existence first to avoid each plugin doing IO if it doesn't
152
- // exist.
153
- llvm::SmallString<256 > BuildDir (Path);
154
- llvm::sys::path::append (BuildDir, " build" );
155
- if (llvm::sys::fs::is_directory (BuildDir)) {
156
- vlog (" Found candidate build directory {0}" , BuildDir);
157
- CDB = tooling::CompilationDatabase::loadFromDirectory (BuildDir, Error);
158
- }
159
- }
160
- if (CDB) {
161
- log (" Loaded compilation database from {0}" , Path);
162
- } else {
163
- vlog (" No compilation database at {0}" , Path);
164
- }
165
- }
166
- };
167
-
168
61
DirectoryBasedGlobalCompilationDatabase::
169
62
DirectoryBasedGlobalCompilationDatabase (
170
- llvm::Optional<Path> CompileCommandsDir) {
171
- if (CompileCommandsDir)
172
- OnlyDirCache = std::make_unique<DirectoryCache>(*CompileCommandsDir);
173
- }
63
+ llvm::Optional<Path> CompileCommandsDir)
64
+ : CompileCommandsDir(std::move(CompileCommandsDir)) {}
174
65
175
66
DirectoryBasedGlobalCompilationDatabase::
176
67
~DirectoryBasedGlobalCompilationDatabase () = default ;
@@ -216,26 +107,31 @@ static bool pathEqual(PathRef A, PathRef B) {
216
107
#endif
217
108
}
218
109
219
- std::vector<DirectoryBasedGlobalCompilationDatabase::DirectoryCache *>
220
- DirectoryBasedGlobalCompilationDatabase::getDirectoryCaches (
221
- llvm::ArrayRef<llvm::StringRef> Dirs) const {
222
- std::vector<std::string> FoldedDirs;
223
- FoldedDirs.reserve (Dirs.size ());
224
- for (const auto &Dir : Dirs) {
225
- #ifndef NDEBUG
226
- if (!llvm::sys::path::is_absolute (Dir))
227
- elog (" Trying to cache CDB for relative {0}" );
228
- #endif
229
- FoldedDirs.push_back (maybeCaseFoldPath (Dir));
110
+ DirectoryBasedGlobalCompilationDatabase::CachedCDB &
111
+ DirectoryBasedGlobalCompilationDatabase::getCDBInDirLocked (PathRef Dir) const {
112
+ // FIXME(ibiryukov): Invalidate cached compilation databases on changes
113
+ auto Key = maybeCaseFoldPath (Dir);
114
+ auto R = CompilationDatabases.try_emplace (Key);
115
+ if (R.second ) { // Cache miss, try to load CDB.
116
+ CachedCDB &Entry = R.first ->second ;
117
+ std::string Error;
118
+ Entry.Path = std::string (Dir);
119
+ Entry.CDB = tooling::CompilationDatabase::loadFromDirectory (Dir, Error);
120
+ // Check for $src/build, the conventional CMake build root.
121
+ // Probe existence first to avoid each plugin doing IO if it doesn't exist.
122
+ if (!CompileCommandsDir && !Entry.CDB ) {
123
+ llvm::SmallString<256 > BuildDir = Dir;
124
+ llvm::sys::path::append (BuildDir, " build" );
125
+ if (llvm::sys::fs::is_directory (BuildDir)) {
126
+ vlog (" Found candidate build directory {0}" , BuildDir);
127
+ Entry.CDB =
128
+ tooling::CompilationDatabase::loadFromDirectory (BuildDir, Error);
129
+ }
130
+ }
131
+ if (Entry.CDB )
132
+ log (" Loaded compilation database from {0}" , Dir);
230
133
}
231
-
232
- std::vector<DirectoryCache *> Ret;
233
- Ret.reserve (Dirs.size ());
234
-
235
- std::lock_guard<std::mutex> Lock (DirCachesMutex);
236
- for (unsigned I = 0 ; I < Dirs.size (); ++I)
237
- Ret.push_back (&DirCaches.try_emplace (FoldedDirs[I], Dirs[I]).first ->second );
238
- return Ret;
134
+ return R.first ->second ;
239
135
}
240
136
241
137
llvm::Optional<DirectoryBasedGlobalCompilationDatabase::CDBLookupResult>
@@ -245,40 +141,39 @@ DirectoryBasedGlobalCompilationDatabase::lookupCDB(
245
141
" path must be absolute" );
246
142
247
143
bool ShouldBroadcast = false ;
248
- DirectoryCache *DirCache = nullptr ;
249
- std::shared_ptr<const tooling::CompilationDatabase> CDB = nullptr ;
250
- if (OnlyDirCache) {
251
- DirCache = OnlyDirCache.get ();
252
- ShouldBroadcast = Request.ShouldBroadcast ;
253
- CDB = DirCache->get (ShouldBroadcast);
254
- } else {
255
- // Traverse the canonical version to prevent false positives. i.e.:
256
- // src/build/../a.cc can detect a CDB in /src/build if not canonicalized.
257
- std::string CanonicalPath = removeDots (Request.FileName );
258
- std::vector<llvm::StringRef> SearchDirs;
259
- actOnAllParentDirectories (CanonicalPath, [&](PathRef Path) {
260
- SearchDirs.push_back (Path);
261
- return false ;
262
- });
263
- for (DirectoryCache *Candidate : getDirectoryCaches (SearchDirs)) {
264
- bool CandidateShouldBroadcast = Request.ShouldBroadcast ;
265
- if ((CDB = Candidate->get (CandidateShouldBroadcast))) {
266
- DirCache = Candidate;
267
- ShouldBroadcast = CandidateShouldBroadcast;
268
- break ;
269
- }
144
+ CDBLookupResult Result;
145
+
146
+ {
147
+ std::lock_guard<std::mutex> Lock (Mutex);
148
+ CachedCDB *Entry = nullptr ;
149
+ if (CompileCommandsDir) {
150
+ Entry = &getCDBInDirLocked (*CompileCommandsDir);
151
+ } else {
152
+ // Traverse the canonical version to prevent false positives. i.e.:
153
+ // src/build/../a.cc can detect a CDB in /src/build if not canonicalized.
154
+ // FIXME(sammccall): this loop is hot, use a union-find-like structure.
155
+ actOnAllParentDirectories (removeDots (Request.FileName ),
156
+ [&](PathRef Path) {
157
+ Entry = &getCDBInDirLocked (Path);
158
+ return Entry->CDB != nullptr ;
159
+ });
270
160
}
271
- }
272
161
273
- if (!CDB)
274
- return llvm::None;
162
+ if (!Entry || !Entry-> CDB )
163
+ return llvm::None;
275
164
276
- CDBLookupResult Result;
277
- Result.CDB = std::move (CDB);
278
- Result.PI .SourceRoot = DirCache->path ().str ();
165
+ // Mark CDB as broadcasted to make sure discovery is performed once.
166
+ if (Request.ShouldBroadcast && !Entry->SentBroadcast ) {
167
+ Entry->SentBroadcast = true ;
168
+ ShouldBroadcast = true ;
169
+ }
170
+
171
+ Result.CDB = Entry->CDB .get ();
172
+ Result.PI .SourceRoot = Entry->Path ;
173
+ }
279
174
280
- // FIXME: Maybe make the following part async, since this can block
281
- // retrieval of compile commands.
175
+ // FIXME: Maybe make the following part async, since this can block retrieval
176
+ // of compile commands.
282
177
if (ShouldBroadcast)
283
178
broadcastCDB (Result);
284
179
return Result;
@@ -291,32 +186,29 @@ void DirectoryBasedGlobalCompilationDatabase::broadcastCDB(
291
186
std::vector<std::string> AllFiles = Result.CDB ->getAllFiles ();
292
187
// We assume CDB in CompileCommandsDir owns all of its entries, since we don't
293
188
// perform any search in parent paths whenever it is set.
294
- if (OnlyDirCache ) {
295
- assert (OnlyDirCache-> path () == Result.PI .SourceRoot &&
189
+ if (CompileCommandsDir ) {
190
+ assert (*CompileCommandsDir == Result.PI .SourceRoot &&
296
191
" Trying to broadcast a CDB outside of CompileCommandsDir!" );
297
192
OnCommandChanged.broadcast (std::move (AllFiles));
298
193
return ;
299
194
}
300
195
301
- // Uniquify all parent directories of all files.
302
196
llvm::StringMap<bool > DirectoryHasCDB;
303
- std::vector<llvm::StringRef> FileAncestors;
304
- for (llvm::StringRef File : AllFiles) {
305
- actOnAllParentDirectories (File, [&](PathRef Path) {
306
- auto It = DirectoryHasCDB.try_emplace (Path);
307
- // Already seen this path, and all of its parents.
308
- if (!It.second )
309
- return true ;
310
-
311
- FileAncestors.push_back (It.first ->getKey ());
312
- return pathEqual (Path, Result.PI .SourceRoot );
313
- });
314
- }
315
- // Work out which ones have CDBs in them.
316
- for (DirectoryCache *Dir : getDirectoryCaches (FileAncestors)) {
317
- bool ShouldBroadcast = false ;
318
- if (Dir->get (ShouldBroadcast))
319
- DirectoryHasCDB.find (Dir->path ())->setValue (true );
197
+ {
198
+ std::lock_guard<std::mutex> Lock (Mutex);
199
+ // Uniquify all parent directories of all files.
200
+ for (llvm::StringRef File : AllFiles) {
201
+ actOnAllParentDirectories (File, [&](PathRef Path) {
202
+ auto It = DirectoryHasCDB.try_emplace (Path);
203
+ // Already seen this path, and all of its parents.
204
+ if (!It.second )
205
+ return true ;
206
+
207
+ CachedCDB &Entry = getCDBInDirLocked (Path);
208
+ It.first ->second = Entry.CDB != nullptr ;
209
+ return pathEqual (Path, Result.PI .SourceRoot );
210
+ });
211
+ }
320
212
}
321
213
322
214
std::vector<std::string> GovernedFiles;
0 commit comments