1 | //===- DependencyScanningFilesystem.cpp - clang-scan-deps fs --------------===// |
---|---|
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/DependencyScanningFilesystem.h" |
10 | #include "llvm/Support/MemoryBuffer.h" |
11 | #include "llvm/Support/Threading.h" |
12 | #include <optional> |
13 | |
14 | using namespace clang; |
15 | using namespace tooling; |
16 | using namespace dependencies; |
17 | |
18 | llvm::ErrorOr<DependencyScanningWorkerFilesystem::TentativeEntry> |
19 | DependencyScanningWorkerFilesystem::readFile(StringRef Filename) { |
20 | // Load the file and its content from the file system. |
21 | auto MaybeFile = getUnderlyingFS().openFileForRead(Path: Filename); |
22 | if (!MaybeFile) |
23 | return MaybeFile.getError(); |
24 | auto File = std::move(*MaybeFile); |
25 | |
26 | auto MaybeStat = File->status(); |
27 | if (!MaybeStat) |
28 | return MaybeStat.getError(); |
29 | auto Stat = std::move(*MaybeStat); |
30 | |
31 | auto MaybeBuffer = File->getBuffer(Name: Stat.getName()); |
32 | if (!MaybeBuffer) |
33 | return MaybeBuffer.getError(); |
34 | auto Buffer = std::move(*MaybeBuffer); |
35 | |
36 | // If the file size changed between read and stat, pretend it didn't. |
37 | if (Stat.getSize() != Buffer->getBufferSize()) |
38 | Stat = llvm::vfs::Status::copyWithNewSize(In: Stat, NewSize: Buffer->getBufferSize()); |
39 | |
40 | return TentativeEntry(Stat, std::move(Buffer)); |
41 | } |
42 | |
43 | bool DependencyScanningWorkerFilesystem::ensureDirectiveTokensArePopulated( |
44 | EntryRef Ref) { |
45 | auto &Entry = Ref.Entry; |
46 | |
47 | if (Entry.isError() || Entry.isDirectory()) |
48 | return false; |
49 | |
50 | CachedFileContents *Contents = Entry.getCachedContents(); |
51 | assert(Contents && "contents not initialized"); |
52 | |
53 | // Double-checked locking. |
54 | if (Contents->DepDirectives.load()) |
55 | return true; |
56 | |
57 | std::lock_guard<std::mutex> GuardLock(Contents->ValueLock); |
58 | |
59 | // Double-checked locking. |
60 | if (Contents->DepDirectives.load()) |
61 | return true; |
62 | |
63 | SmallVector<dependency_directives_scan::Directive, 64> Directives; |
64 | // Scan the file for preprocessor directives that might affect the |
65 | // dependencies. |
66 | if (scanSourceForDependencyDirectives(Input: Contents->Original->getBuffer(), |
67 | Tokens&: Contents->DepDirectiveTokens, |
68 | Directives)) { |
69 | Contents->DepDirectiveTokens.clear(); |
70 | // FIXME: Propagate the diagnostic if desired by the client. |
71 | Contents->DepDirectives.store(p: new std::optional<DependencyDirectivesTy>()); |
72 | return false; |
73 | } |
74 | |
75 | // This function performed double-checked locking using `DepDirectives`. |
76 | // Assigning it must be the last thing this function does, otherwise other |
77 | // threads may skip the critical section (`DepDirectives != nullptr`), leading |
78 | // to a data race. |
79 | Contents->DepDirectives.store( |
80 | p: new std::optional<DependencyDirectivesTy>(std::move(Directives))); |
81 | return true; |
82 | } |
83 | |
84 | DependencyScanningFilesystemSharedCache:: |
85 | DependencyScanningFilesystemSharedCache() { |
86 | // This heuristic was chosen using a empirical testing on a |
87 | // reasonably high core machine (iMacPro 18 cores / 36 threads). The cache |
88 | // sharding gives a performance edge by reducing the lock contention. |
89 | // FIXME: A better heuristic might also consider the OS to account for |
90 | // the different cost of lock contention on different OSes. |
91 | NumShards = |
92 | std::max(a: 2u, b: llvm::hardware_concurrency().compute_thread_count() / 4); |
93 | CacheShards = std::make_unique<CacheShard[]>(num: NumShards); |
94 | } |
95 | |
96 | DependencyScanningFilesystemSharedCache::CacheShard & |
97 | DependencyScanningFilesystemSharedCache::getShardForFilename( |
98 | StringRef Filename) const { |
99 | assert(llvm::sys::path::is_absolute_gnu(Filename)); |
100 | return CacheShards[llvm::hash_value(S: Filename) % NumShards]; |
101 | } |
102 | |
103 | DependencyScanningFilesystemSharedCache::CacheShard & |
104 | DependencyScanningFilesystemSharedCache::getShardForUID( |
105 | llvm::sys::fs::UniqueID UID) const { |
106 | auto Hash = llvm::hash_combine(args: UID.getDevice(), args: UID.getFile()); |
107 | return CacheShards[Hash % NumShards]; |
108 | } |
109 | |
110 | std::vector<StringRef> |
111 | DependencyScanningFilesystemSharedCache::getInvalidNegativeStatCachedPaths( |
112 | llvm::vfs::FileSystem &UnderlyingFS) const { |
113 | // Iterate through all shards and look for cached stat errors. |
114 | std::vector<StringRef> InvalidPaths; |
115 | for (unsigned i = 0; i < NumShards; i++) { |
116 | const CacheShard &Shard = CacheShards[i]; |
117 | std::lock_guard<std::mutex> LockGuard(Shard.CacheLock); |
118 | for (const auto &[Path, CachedPair] : Shard.CacheByFilename) { |
119 | const CachedFileSystemEntry *Entry = CachedPair.first; |
120 | |
121 | if (Entry->getError()) { |
122 | // Only examine cached errors. |
123 | llvm::ErrorOr<llvm::vfs::Status> Stat = UnderlyingFS.status(Path); |
124 | if (Stat) { |
125 | // This is the case where we have cached the non-existence |
126 | // of the file at Path first, and a a file at the path is created |
127 | // later. The cache entry is not invalidated (as we have no good |
128 | // way to do it now), which may lead to missing file build errors. |
129 | InvalidPaths.push_back(x: Path); |
130 | } |
131 | } |
132 | } |
133 | } |
134 | return InvalidPaths; |
135 | } |
136 | |
137 | const CachedFileSystemEntry * |
138 | DependencyScanningFilesystemSharedCache::CacheShard::findEntryByFilename( |
139 | StringRef Filename) const { |
140 | assert(llvm::sys::path::is_absolute_gnu(Filename)); |
141 | std::lock_guard<std::mutex> LockGuard(CacheLock); |
142 | auto It = CacheByFilename.find(Key: Filename); |
143 | return It == CacheByFilename.end() ? nullptr : It->getValue().first; |
144 | } |
145 | |
146 | const CachedFileSystemEntry * |
147 | DependencyScanningFilesystemSharedCache::CacheShard::findEntryByUID( |
148 | llvm::sys::fs::UniqueID UID) const { |
149 | std::lock_guard<std::mutex> LockGuard(CacheLock); |
150 | auto It = EntriesByUID.find(Val: UID); |
151 | return It == EntriesByUID.end() ? nullptr : It->getSecond(); |
152 | } |
153 | |
154 | const CachedFileSystemEntry & |
155 | DependencyScanningFilesystemSharedCache::CacheShard:: |
156 | getOrEmplaceEntryForFilename(StringRef Filename, |
157 | llvm::ErrorOr<llvm::vfs::Status> Stat) { |
158 | std::lock_guard<std::mutex> LockGuard(CacheLock); |
159 | auto [It, Inserted] = CacheByFilename.insert(KV: {Filename, {nullptr, nullptr}}); |
160 | auto &[CachedEntry, CachedRealPath] = It->getValue(); |
161 | if (!CachedEntry) { |
162 | // The entry is not present in the shared cache. Either the cache doesn't |
163 | // know about the file at all, or it only knows about its real path. |
164 | assert((Inserted || CachedRealPath) && "existing file with empty pair"); |
165 | CachedEntry = |
166 | new (EntryStorage.Allocate()) CachedFileSystemEntry(std::move(Stat)); |
167 | } |
168 | return *CachedEntry; |
169 | } |
170 | |
171 | const CachedFileSystemEntry & |
172 | DependencyScanningFilesystemSharedCache::CacheShard::getOrEmplaceEntryForUID( |
173 | llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat, |
174 | std::unique_ptr<llvm::MemoryBuffer> Contents) { |
175 | std::lock_guard<std::mutex> LockGuard(CacheLock); |
176 | auto [It, Inserted] = EntriesByUID.try_emplace(Key: UID); |
177 | auto &CachedEntry = It->getSecond(); |
178 | if (Inserted) { |
179 | CachedFileContents *StoredContents = nullptr; |
180 | if (Contents) |
181 | StoredContents = new (ContentsStorage.Allocate()) |
182 | CachedFileContents(std::move(Contents)); |
183 | CachedEntry = new (EntryStorage.Allocate()) |
184 | CachedFileSystemEntry(std::move(Stat), StoredContents); |
185 | } |
186 | return *CachedEntry; |
187 | } |
188 | |
189 | const CachedFileSystemEntry & |
190 | DependencyScanningFilesystemSharedCache::CacheShard:: |
191 | getOrInsertEntryForFilename(StringRef Filename, |
192 | const CachedFileSystemEntry &Entry) { |
193 | std::lock_guard<std::mutex> LockGuard(CacheLock); |
194 | auto [It, Inserted] = CacheByFilename.insert(KV: {Filename, {&Entry, nullptr}}); |
195 | auto &[CachedEntry, CachedRealPath] = It->getValue(); |
196 | if (!Inserted || !CachedEntry) |
197 | CachedEntry = &Entry; |
198 | return *CachedEntry; |
199 | } |
200 | |
201 | const CachedRealPath * |
202 | DependencyScanningFilesystemSharedCache::CacheShard::findRealPathByFilename( |
203 | StringRef Filename) const { |
204 | assert(llvm::sys::path::is_absolute_gnu(Filename)); |
205 | std::lock_guard<std::mutex> LockGuard(CacheLock); |
206 | auto It = CacheByFilename.find(Key: Filename); |
207 | return It == CacheByFilename.end() ? nullptr : It->getValue().second; |
208 | } |
209 | |
210 | const CachedRealPath &DependencyScanningFilesystemSharedCache::CacheShard:: |
211 | getOrEmplaceRealPathForFilename(StringRef Filename, |
212 | llvm::ErrorOr<llvm::StringRef> RealPath) { |
213 | std::lock_guard<std::mutex> LockGuard(CacheLock); |
214 | |
215 | const CachedRealPath *&StoredRealPath = CacheByFilename[Filename].second; |
216 | if (!StoredRealPath) { |
217 | auto OwnedRealPath = [&]() -> CachedRealPath { |
218 | if (!RealPath) |
219 | return RealPath.getError(); |
220 | return RealPath->str(); |
221 | }(); |
222 | |
223 | StoredRealPath = new (RealPathStorage.Allocate()) |
224 | CachedRealPath(std::move(OwnedRealPath)); |
225 | } |
226 | |
227 | return *StoredRealPath; |
228 | } |
229 | |
230 | bool DependencyScanningWorkerFilesystem::shouldBypass(StringRef Path) const { |
231 | return BypassedPathPrefix && Path.starts_with(Prefix: *BypassedPathPrefix); |
232 | } |
233 | |
234 | DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem( |
235 | DependencyScanningFilesystemSharedCache &SharedCache, |
236 | IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) |
237 | : llvm::RTTIExtends<DependencyScanningWorkerFilesystem, |
238 | llvm::vfs::ProxyFileSystem>(std::move(FS)), |
239 | SharedCache(SharedCache), |
240 | WorkingDirForCacheLookup(llvm::errc::invalid_argument) { |
241 | updateWorkingDirForCacheLookup(); |
242 | } |
243 | |
244 | const CachedFileSystemEntry & |
245 | DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID( |
246 | TentativeEntry TEntry) { |
247 | auto &Shard = SharedCache.getShardForUID(UID: TEntry.Status.getUniqueID()); |
248 | return Shard.getOrEmplaceEntryForUID(UID: TEntry.Status.getUniqueID(), |
249 | Stat: std::move(TEntry.Status), |
250 | Contents: std::move(TEntry.Contents)); |
251 | } |
252 | |
253 | const CachedFileSystemEntry * |
254 | DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough( |
255 | StringRef Filename) { |
256 | if (const auto *Entry = LocalCache.findEntryByFilename(Filename)) |
257 | return Entry; |
258 | auto &Shard = SharedCache.getShardForFilename(Filename); |
259 | if (const auto *Entry = Shard.findEntryByFilename(Filename)) |
260 | return &LocalCache.insertEntryForFilename(Filename, Entry: *Entry); |
261 | return nullptr; |
262 | } |
263 | |
264 | llvm::ErrorOr<const CachedFileSystemEntry &> |
265 | DependencyScanningWorkerFilesystem::computeAndStoreResult( |
266 | StringRef OriginalFilename, StringRef FilenameForLookup) { |
267 | llvm::ErrorOr<llvm::vfs::Status> Stat = |
268 | getUnderlyingFS().status(Path: OriginalFilename); |
269 | if (!Stat) { |
270 | const auto &Entry = |
271 | getOrEmplaceSharedEntryForFilename(Filename: FilenameForLookup, EC: Stat.getError()); |
272 | return insertLocalEntryForFilename(Filename: FilenameForLookup, Entry); |
273 | } |
274 | |
275 | if (const auto *Entry = findSharedEntryByUID(Stat: *Stat)) |
276 | return insertLocalEntryForFilename(Filename: FilenameForLookup, Entry: *Entry); |
277 | |
278 | auto TEntry = |
279 | Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(Filename: OriginalFilename); |
280 | |
281 | const CachedFileSystemEntry *SharedEntry = [&]() { |
282 | if (TEntry) { |
283 | const auto &UIDEntry = getOrEmplaceSharedEntryForUID(TEntry: std::move(*TEntry)); |
284 | return &getOrInsertSharedEntryForFilename(Filename: FilenameForLookup, Entry: UIDEntry); |
285 | } |
286 | return &getOrEmplaceSharedEntryForFilename(Filename: FilenameForLookup, |
287 | EC: TEntry.getError()); |
288 | }(); |
289 | |
290 | return insertLocalEntryForFilename(Filename: FilenameForLookup, Entry: *SharedEntry); |
291 | } |
292 | |
293 | llvm::ErrorOr<EntryRef> |
294 | DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry( |
295 | StringRef OriginalFilename) { |
296 | SmallString<256> PathBuf; |
297 | auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf); |
298 | if (!FilenameForLookup) |
299 | return FilenameForLookup.getError(); |
300 | |
301 | if (const auto *Entry = |
302 | findEntryByFilenameWithWriteThrough(Filename: *FilenameForLookup)) |
303 | return EntryRef(OriginalFilename, *Entry).unwrapError(); |
304 | auto MaybeEntry = computeAndStoreResult(OriginalFilename, FilenameForLookup: *FilenameForLookup); |
305 | if (!MaybeEntry) |
306 | return MaybeEntry.getError(); |
307 | return EntryRef(OriginalFilename, *MaybeEntry).unwrapError(); |
308 | } |
309 | |
310 | llvm::ErrorOr<llvm::vfs::Status> |
311 | DependencyScanningWorkerFilesystem::status(const Twine &Path) { |
312 | SmallString<256> OwnedFilename; |
313 | StringRef Filename = Path.toStringRef(Out&: OwnedFilename); |
314 | |
315 | if (shouldBypass(Path: Filename)) |
316 | return getUnderlyingFS().status(Path); |
317 | |
318 | llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(OriginalFilename: Filename); |
319 | if (!Result) |
320 | return Result.getError(); |
321 | return Result->getStatus(); |
322 | } |
323 | |
324 | bool DependencyScanningWorkerFilesystem::exists(const Twine &Path) { |
325 | // While some VFS overlay filesystems may implement more-efficient |
326 | // mechanisms for `exists` queries, `DependencyScanningWorkerFilesystem` |
327 | // typically wraps `RealFileSystem` which does not specialize `exists`, |
328 | // so it is not likely to benefit from such optimizations. Instead, |
329 | // it is more-valuable to have this query go through the |
330 | // cached-`status` code-path of the `DependencyScanningWorkerFilesystem`. |
331 | llvm::ErrorOr<llvm::vfs::Status> Status = status(Path); |
332 | return Status && Status->exists(); |
333 | } |
334 | |
335 | namespace { |
336 | |
337 | /// The VFS that is used by clang consumes the \c CachedFileSystemEntry using |
338 | /// this subclass. |
339 | class DepScanFile final : public llvm::vfs::File { |
340 | public: |
341 | DepScanFile(std::unique_ptr<llvm::MemoryBuffer> Buffer, |
342 | llvm::vfs::Status Stat) |
343 | : Buffer(std::move(Buffer)), Stat(std::move(Stat)) {} |
344 | |
345 | static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> create(EntryRef Entry); |
346 | |
347 | llvm::ErrorOr<llvm::vfs::Status> status() override { return Stat; } |
348 | |
349 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> |
350 | getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, |
351 | bool IsVolatile) override { |
352 | return std::move(Buffer); |
353 | } |
354 | |
355 | std::error_code close() override { return {}; } |
356 | |
357 | private: |
358 | std::unique_ptr<llvm::MemoryBuffer> Buffer; |
359 | llvm::vfs::Status Stat; |
360 | }; |
361 | |
362 | } // end anonymous namespace |
363 | |
364 | llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> |
365 | DepScanFile::create(EntryRef Entry) { |
366 | assert(!Entry.isError() && "error"); |
367 | |
368 | if (Entry.isDirectory()) |
369 | return std::make_error_code(e: std::errc::is_a_directory); |
370 | |
371 | auto Result = std::make_unique<DepScanFile>( |
372 | args: llvm::MemoryBuffer::getMemBuffer(InputData: Entry.getContents(), |
373 | BufferName: Entry.getStatus().getName(), |
374 | /*RequiresNullTerminator=*/false), |
375 | args: Entry.getStatus()); |
376 | |
377 | return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>( |
378 | std::unique_ptr<llvm::vfs::File>(std::move(Result))); |
379 | } |
380 | |
381 | llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> |
382 | DependencyScanningWorkerFilesystem::openFileForRead(const Twine &Path) { |
383 | SmallString<256> OwnedFilename; |
384 | StringRef Filename = Path.toStringRef(Out&: OwnedFilename); |
385 | |
386 | if (shouldBypass(Path: Filename)) |
387 | return getUnderlyingFS().openFileForRead(Path); |
388 | |
389 | llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(OriginalFilename: Filename); |
390 | if (!Result) |
391 | return Result.getError(); |
392 | return DepScanFile::create(Entry: Result.get()); |
393 | } |
394 | |
395 | std::error_code |
396 | DependencyScanningWorkerFilesystem::getRealPath(const Twine &Path, |
397 | SmallVectorImpl<char> &Output) { |
398 | SmallString<256> OwnedFilename; |
399 | StringRef OriginalFilename = Path.toStringRef(Out&: OwnedFilename); |
400 | |
401 | if (shouldBypass(Path: OriginalFilename)) |
402 | return getUnderlyingFS().getRealPath(Path, Output); |
403 | |
404 | SmallString<256> PathBuf; |
405 | auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf); |
406 | if (!FilenameForLookup) |
407 | return FilenameForLookup.getError(); |
408 | |
409 | auto HandleCachedRealPath = |
410 | [&Output](const CachedRealPath &RealPath) -> std::error_code { |
411 | if (!RealPath) |
412 | return RealPath.getError(); |
413 | Output.assign(in_start: RealPath->begin(), in_end: RealPath->end()); |
414 | return {}; |
415 | }; |
416 | |
417 | // If we already have the result in local cache, no work required. |
418 | if (const auto *RealPath = |
419 | LocalCache.findRealPathByFilename(Filename: *FilenameForLookup)) |
420 | return HandleCachedRealPath(*RealPath); |
421 | |
422 | // If we have the result in the shared cache, cache it locally. |
423 | auto &Shard = SharedCache.getShardForFilename(Filename: *FilenameForLookup); |
424 | if (const auto *ShardRealPath = |
425 | Shard.findRealPathByFilename(Filename: *FilenameForLookup)) { |
426 | const auto &RealPath = LocalCache.insertRealPathForFilename( |
427 | Filename: *FilenameForLookup, RealPath: *ShardRealPath); |
428 | return HandleCachedRealPath(RealPath); |
429 | } |
430 | |
431 | // If we don't know the real path, compute it... |
432 | std::error_code EC = getUnderlyingFS().getRealPath(Path: OriginalFilename, Output); |
433 | llvm::ErrorOr<llvm::StringRef> ComputedRealPath = EC; |
434 | if (!EC) |
435 | ComputedRealPath = StringRef{Output.data(), Output.size()}; |
436 | |
437 | // ...and try to write it into the shared cache. In case some other thread won |
438 | // this race and already wrote its own result there, just adopt it. Write |
439 | // whatever is in the shared cache into the local one. |
440 | const auto &RealPath = Shard.getOrEmplaceRealPathForFilename( |
441 | Filename: *FilenameForLookup, RealPath: ComputedRealPath); |
442 | return HandleCachedRealPath( |
443 | LocalCache.insertRealPathForFilename(Filename: *FilenameForLookup, RealPath)); |
444 | } |
445 | |
446 | std::error_code DependencyScanningWorkerFilesystem::setCurrentWorkingDirectory( |
447 | const Twine &Path) { |
448 | std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path); |
449 | updateWorkingDirForCacheLookup(); |
450 | return EC; |
451 | } |
452 | |
453 | void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() { |
454 | llvm::ErrorOr<std::string> CWD = |
455 | getUnderlyingFS().getCurrentWorkingDirectory(); |
456 | if (!CWD) { |
457 | WorkingDirForCacheLookup = CWD.getError(); |
458 | } else if (!llvm::sys::path::is_absolute_gnu(path: *CWD)) { |
459 | WorkingDirForCacheLookup = llvm::errc::invalid_argument; |
460 | } else { |
461 | WorkingDirForCacheLookup = *CWD; |
462 | } |
463 | assert(!WorkingDirForCacheLookup || |
464 | llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup)); |
465 | } |
466 | |
467 | llvm::ErrorOr<StringRef> |
468 | DependencyScanningWorkerFilesystem::tryGetFilenameForLookup( |
469 | StringRef OriginalFilename, llvm::SmallVectorImpl<char> &PathBuf) const { |
470 | StringRef FilenameForLookup; |
471 | if (llvm::sys::path::is_absolute_gnu(path: OriginalFilename)) { |
472 | FilenameForLookup = OriginalFilename; |
473 | } else if (!WorkingDirForCacheLookup) { |
474 | return WorkingDirForCacheLookup.getError(); |
475 | } else { |
476 | StringRef RelFilename = OriginalFilename; |
477 | RelFilename.consume_front(Prefix: "./"); |
478 | PathBuf.assign(in_start: WorkingDirForCacheLookup->begin(), |
479 | in_end: WorkingDirForCacheLookup->end()); |
480 | llvm::sys::path::append(path&: PathBuf, a: RelFilename); |
481 | FilenameForLookup = StringRef{PathBuf.begin(), PathBuf.size()}; |
482 | } |
483 | assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup)); |
484 | return FilenameForLookup; |
485 | } |
486 | |
487 | const char DependencyScanningWorkerFilesystem::ID = 0; |
488 |
Definitions
- readFile
- ensureDirectiveTokensArePopulated
- DependencyScanningFilesystemSharedCache
- getShardForFilename
- getShardForUID
- getInvalidNegativeStatCachedPaths
- findEntryByFilename
- findEntryByUID
- getOrEmplaceEntryForFilename
- getOrEmplaceEntryForUID
- getOrInsertEntryForFilename
- findRealPathByFilename
- getOrEmplaceRealPathForFilename
- shouldBypass
- DependencyScanningWorkerFilesystem
- getOrEmplaceSharedEntryForUID
- findEntryByFilenameWithWriteThrough
- computeAndStoreResult
- getOrCreateFileSystemEntry
- status
- exists
- DepScanFile
- DepScanFile
- status
- getBuffer
- close
- create
- openFileForRead
- getRealPath
- setCurrentWorkingDirectory
- updateWorkingDirForCacheLookup
- tryGetFilenameForLookup
Learn to use CMake with our Intro Training
Find out more