forked from OSchip/llvm-project
153 lines
5.4 KiB
C++
153 lines
5.4 KiB
C++
//== BackgroundIndexStorage.cpp - Provide caching support to BackgroundIndex ==/
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "GlobalCompilationDatabase.h"
|
|
#include "index/Background.h"
|
|
#include "support/Logger.h"
|
|
#include "support/Path.h"
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/ScopeExit.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/FileUtilities.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include <functional>
|
|
|
|
namespace clang {
|
|
namespace clangd {
|
|
namespace {
|
|
|
|
std::string getShardPathFromFilePath(llvm::StringRef ShardRoot,
|
|
llvm::StringRef FilePath) {
|
|
llvm::SmallString<128> ShardRootSS(ShardRoot);
|
|
llvm::sys::path::append(ShardRootSS, llvm::sys::path::filename(FilePath) +
|
|
"." + llvm::toHex(digest(FilePath)) +
|
|
".idx");
|
|
return std::string(ShardRootSS.str());
|
|
}
|
|
|
|
// Uses disk as a storage for index shards.
|
|
class DiskBackedIndexStorage : public BackgroundIndexStorage {
|
|
std::string DiskShardRoot;
|
|
|
|
public:
|
|
// Creates `DiskShardRoot` and any parents during construction.
|
|
DiskBackedIndexStorage(llvm::StringRef Directory) : DiskShardRoot(Directory) {
|
|
std::error_code OK;
|
|
std::error_code EC = llvm::sys::fs::create_directories(DiskShardRoot);
|
|
if (EC != OK) {
|
|
elog("Failed to create directory {0} for index storage: {1}",
|
|
DiskShardRoot, EC.message());
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<IndexFileIn>
|
|
loadShard(llvm::StringRef ShardIdentifier) const override {
|
|
const std::string ShardPath =
|
|
getShardPathFromFilePath(DiskShardRoot, ShardIdentifier);
|
|
auto Buffer = llvm::MemoryBuffer::getFile(ShardPath);
|
|
if (!Buffer)
|
|
return nullptr;
|
|
if (auto I = readIndexFile(Buffer->get()->getBuffer()))
|
|
return std::make_unique<IndexFileIn>(std::move(*I));
|
|
else
|
|
elog("Error while reading shard {0}: {1}", ShardIdentifier,
|
|
I.takeError());
|
|
return nullptr;
|
|
}
|
|
|
|
llvm::Error storeShard(llvm::StringRef ShardIdentifier,
|
|
IndexFileOut Shard) const override {
|
|
auto ShardPath = getShardPathFromFilePath(DiskShardRoot, ShardIdentifier);
|
|
return llvm::writeFileAtomically(ShardPath + ".tmp.%%%%%%%%", ShardPath,
|
|
[&Shard](llvm::raw_ostream &OS) {
|
|
OS << Shard;
|
|
return llvm::Error::success();
|
|
});
|
|
}
|
|
};
|
|
|
|
// Doesn't persist index shards anywhere (used when the CDB dir is unknown).
|
|
// We could consider indexing into ~/.clangd/ or so instead.
|
|
class NullStorage : public BackgroundIndexStorage {
|
|
public:
|
|
std::unique_ptr<IndexFileIn>
|
|
loadShard(llvm::StringRef ShardIdentifier) const override {
|
|
return nullptr;
|
|
}
|
|
|
|
llvm::Error storeShard(llvm::StringRef ShardIdentifier,
|
|
IndexFileOut Shard) const override {
|
|
vlog("Couldn't find project for {0}, indexing in-memory only",
|
|
ShardIdentifier);
|
|
return llvm::Error::success();
|
|
}
|
|
};
|
|
|
|
// Creates and owns IndexStorages for multiple CDBs.
|
|
// When a CDB root is found, shards are stored in $ROOT/.cache/clangd/index/.
|
|
// When no root is found, the fallback path is ~/.cache/clangd/index/.
|
|
class DiskBackedIndexStorageManager {
|
|
public:
|
|
DiskBackedIndexStorageManager(
|
|
std::function<llvm::Optional<ProjectInfo>(PathRef)> GetProjectInfo)
|
|
: IndexStorageMapMu(std::make_unique<std::mutex>()),
|
|
GetProjectInfo(std::move(GetProjectInfo)) {
|
|
llvm::SmallString<128> FallbackDir;
|
|
if (llvm::sys::path::cache_directory(FallbackDir))
|
|
llvm::sys::path::append(FallbackDir, "clangd", "index");
|
|
this->FallbackDir = FallbackDir.str().str();
|
|
}
|
|
|
|
// Creates or fetches to storage from cache for the specified project.
|
|
BackgroundIndexStorage *operator()(PathRef File) {
|
|
std::lock_guard<std::mutex> Lock(*IndexStorageMapMu);
|
|
llvm::SmallString<128> StorageDir(FallbackDir);
|
|
if (auto PI = GetProjectInfo(File)) {
|
|
StorageDir = PI->SourceRoot;
|
|
llvm::sys::path::append(StorageDir, ".cache", "clangd", "index");
|
|
}
|
|
auto &IndexStorage = IndexStorageMap[StorageDir];
|
|
if (!IndexStorage)
|
|
IndexStorage = create(StorageDir);
|
|
return IndexStorage.get();
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<BackgroundIndexStorage> create(PathRef CDBDirectory) {
|
|
if (CDBDirectory.empty()) {
|
|
elog("Tried to create storage for empty directory!");
|
|
return std::make_unique<NullStorage>();
|
|
}
|
|
return std::make_unique<DiskBackedIndexStorage>(CDBDirectory);
|
|
}
|
|
|
|
Path FallbackDir;
|
|
|
|
llvm::StringMap<std::unique_ptr<BackgroundIndexStorage>> IndexStorageMap;
|
|
std::unique_ptr<std::mutex> IndexStorageMapMu;
|
|
|
|
std::function<llvm::Optional<ProjectInfo>(PathRef)> GetProjectInfo;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
BackgroundIndexStorage::Factory
|
|
BackgroundIndexStorage::createDiskBackedStorageFactory(
|
|
std::function<llvm::Optional<ProjectInfo>(PathRef)> GetProjectInfo) {
|
|
return DiskBackedIndexStorageManager(std::move(GetProjectInfo));
|
|
}
|
|
|
|
} // namespace clangd
|
|
} // namespace clang
|