Skip to content

Commit

Permalink
Reland "Add a facility to get system cache directory and use it in cl…
Browse files Browse the repository at this point in the history
…angd"

This reverts commit faf2dce.
  • Loading branch information
sam-mccall committed Apr 28, 2020
1 parent faf2dce commit 4e769e9
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 36 deletions.
34 changes: 17 additions & 17 deletions clang-tools-extra/clangd/index/BackgroundIndexStorage.cpp
Expand Up @@ -36,18 +36,13 @@ std::string getShardPathFromFilePath(llvm::StringRef ShardRoot,
return std::string(ShardRootSS.str());
}

// Uses disk as a storage for index shards. Creates a directory called
// ".clangd/index/" under the path provided during construction.
// Uses disk as a storage for index shards.
class DiskBackedIndexStorage : public BackgroundIndexStorage {
std::string DiskShardRoot;

public:
// Sets DiskShardRoot to (Directory + ".clangd/index/") which is the base
// directory for all shard files.
DiskBackedIndexStorage(llvm::StringRef Directory) {
llvm::SmallString<128> CDBDirectory(Directory);
llvm::sys::path::append(CDBDirectory, ".clangd", "index");
DiskShardRoot = std::string(CDBDirectory.str());
// 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) {
Expand Down Expand Up @@ -100,26 +95,31 @@ class NullStorage : public BackgroundIndexStorage {
};

// Creates and owns IndexStorages for multiple CDBs.
// When a CDB root is found, shards are stored in $ROOT/.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> HomeDir;
llvm::sys::path::home_directory(HomeDir);
this->HomeDir = HomeDir.str().str();
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);
Path CDBDirectory = HomeDir;
if (auto PI = GetProjectInfo(File))
CDBDirectory = PI->SourceRoot;
auto &IndexStorage = IndexStorageMap[CDBDirectory];
llvm::SmallString<128> StorageDir(FallbackDir);
if (auto PI = GetProjectInfo(File)) {
StorageDir = PI->SourceRoot;
llvm::sys::path::append(StorageDir, ".clangd", "index");
}
auto &IndexStorage = IndexStorageMap[StorageDir];
if (!IndexStorage)
IndexStorage = create(CDBDirectory);
IndexStorage = create(StorageDir);
return IndexStorage.get();
}

Expand All @@ -132,7 +132,7 @@ class DiskBackedIndexStorageManager {
return std::make_unique<DiskBackedIndexStorage>(CDBDirectory);
}

Path HomeDir;
Path FallbackDir;

llvm::StringMap<std::unique_ptr<BackgroundIndexStorage>> IndexStorageMap;
std::unique_ptr<std::mutex> IndexStorageMapMu;
Expand Down
7 changes: 7 additions & 0 deletions llvm/include/llvm/Support/Path.h
Expand Up @@ -368,6 +368,13 @@ void system_temp_directory(bool erasedOnReboot, SmallVectorImpl<char> &result);
/// @result True if a home directory is set, false otherwise.
bool home_directory(SmallVectorImpl<char> &result);

/// Get the directory where installed packages should put their
/// machine-local cache, e.g. $XDG_CACHE_HOME.
///
/// @param result Holds the resulting path name.
/// @result True if the appropriate path was determined, it need not exist.
bool cache_directory(SmallVectorImpl<char> &result);

/// Has root name?
///
/// root_name != ""
Expand Down
13 changes: 13 additions & 0 deletions llvm/lib/Support/Unix/Path.inc
Expand Up @@ -1138,6 +1138,19 @@ bool home_directory(SmallVectorImpl<char> &result) {
return true;
}

bool cache_directory(SmallVectorImpl<char> &result) {
if (const char *RequestedDir = getenv("XDG_CACHE_HOME")) {
result.clear();
result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
return true;
}
if (!home_directory(result)) {
return false;
}
append(result, ".cache");
return true;
}

static bool getDarwinConfDir(bool TempDir, SmallVectorImpl<char> &Result) {
#if defined(_CS_DARWIN_USER_TEMP_DIR) && defined(_CS_DARWIN_USER_CACHE_DIR)
// On Darwin, use DARWIN_USER_TEMP_DIR or DARWIN_USER_CACHE_DIR.
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Support/Windows/Path.inc
Expand Up @@ -1372,6 +1372,10 @@ bool home_directory(SmallVectorImpl<char> &result) {
return getKnownFolderPath(FOLDERID_Profile, result);
}

bool cache_directory(SmallVectorImpl<char> &result) {
return getKnownFolderPath(FOLDERID_LocalAppData, result);
}

static bool getTempDirEnvVar(const wchar_t *Var, SmallVectorImpl<char> &Res) {
SmallVector<wchar_t, 1024> Buf;
size_t Size = 1024;
Expand Down
86 changes: 67 additions & 19 deletions llvm/unittests/Support/Path.cpp
Expand Up @@ -13,6 +13,7 @@
#include "llvm/ADT/Triple.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
Expand Down Expand Up @@ -305,15 +306,45 @@ TEST(Support, AbsolutePathIteratorEnd) {
}
}

TEST(Support, HomeDirectory) {
std::string expected;
#ifdef _WIN32
if (wchar_t const *path = ::_wgetenv(L"USERPROFILE")) {
std::string getEnvWin(const wchar_t *Var) {
std::string expected;
if (wchar_t const *path = ::_wgetenv(Var)) {
auto pathLen = ::wcslen(path);
ArrayRef<char> ref{reinterpret_cast<char const *>(path),
pathLen * sizeof(wchar_t)};
convertUTF16ToUTF8String(ref, expected);
}
return expected;
}
#else
// RAII helper to set and restore an environment variable.
class WithEnv {
const char *Var;
llvm::Optional<std::string> OriginalValue;

public:
WithEnv(const char *Var, const char *Value) : Var(Var) {
if (const char *V = ::getenv(Var))
OriginalValue.emplace(V);
if (Value)
::setenv(Var, Value, 1);
else
::unsetenv(Var);
}
~WithEnv() {
if (OriginalValue)
::setenv(Var, OriginalValue->c_str(), 1);
else
::unsetenv(Var);
}
};
#endif

TEST(Support, HomeDirectory) {
std::string expected;
#ifdef _WIN32
expected = getEnvWin(L"USERPROFILE");
#else
if (char const *path = ::getenv("HOME"))
expected = path;
Expand All @@ -330,31 +361,48 @@ TEST(Support, HomeDirectory) {

#ifdef LLVM_ON_UNIX
TEST(Support, HomeDirectoryWithNoEnv) {
std::string OriginalStorage;
char const *OriginalEnv = ::getenv("HOME");
if (OriginalEnv) {
// We're going to unset it, so make a copy and save a pointer to the copy
// so that we can reset it at the end of the test.
OriginalStorage = OriginalEnv;
OriginalEnv = OriginalStorage.c_str();
}
WithEnv Env("HOME", nullptr);

// Don't run the test if we have nothing to compare against.
struct passwd *pw = getpwuid(getuid());
if (!pw || !pw->pw_dir) return;

::unsetenv("HOME");
EXPECT_EQ(nullptr, ::getenv("HOME"));
std::string PwDir = pw->pw_dir;

SmallString<128> HomeDir;
auto status = path::home_directory(HomeDir);
EXPECT_TRUE(status);
EXPECT_TRUE(path::home_directory(HomeDir));
EXPECT_EQ(PwDir, HomeDir);
}

TEST(Support, CacheDirectoryWithEnv) {
WithEnv Env("XDG_CACHE_HOME", "/xdg/cache");

SmallString<128> CacheDir;
EXPECT_TRUE(path::cache_directory(CacheDir));
EXPECT_EQ("/xdg/cache", CacheDir);
}

TEST(Support, CacheDirectoryNoEnv) {
WithEnv Env("XDG_CACHE_HOME", nullptr);

// Now put the environment back to its original state (meaning that if it was
// unset before, we don't reset it).
if (OriginalEnv) ::setenv("HOME", OriginalEnv, 1);
SmallString<128> Fallback;
ASSERT_TRUE(path::home_directory(Fallback));
path::append(Fallback, ".cache");

SmallString<128> CacheDir;
EXPECT_TRUE(path::cache_directory(CacheDir));
EXPECT_EQ(Fallback, CacheDir);
}
#endif

#ifdef _WIN32
TEST(Support, CacheDirectory) {
std::string Expected = getEnvWin(L"LOCALAPPDATA");
// Do not try to test it if we don't know what to expect.
if (!Expected.empty()) {
SmallString<128> CacheDir;
EXPECT_TRUE(path::cache_directory(CacheDir));
EXPECT_EQ(Expected, CacheDir);
}
}
#endif

Expand Down

0 comments on commit 4e769e9

Please sign in to comment.