diff --git a/llvm/include/llvm/Support/Path.h b/llvm/include/llvm/Support/Path.h --- a/llvm/include/llvm/Support/Path.h +++ b/llvm/include/llvm/Support/Path.h @@ -371,6 +371,13 @@ /// @result True if a home directory is set, false otherwise. bool home_directory(SmallVectorImpl &result); +/// Get the directory where packages should read user-specific configurations. +/// e.g. $XDG_CONFIG_HOME. +/// +/// @param result Holds the resulting path name. +/// @result True if the appropriate path was determined, it need not exist. +bool user_config_directory(SmallVectorImpl &result); + /// Get the directory where installed packages should put their /// machine-local cache, e.g. $XDG_CACHE_HOME. /// diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -1158,6 +1158,30 @@ return false; } +bool user_config_directory(SmallVectorImpl &result) { +#ifdef __APPLE__ + // Mac: ~/Library/Preferences/ + if (home_directory(result)) { + append("Library", "Preferences"); + return true; + } +#else + // XDG_CONFIG_HOME as defined in the XDG Base Directory Specification: + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + if (const char *RequestedDir = getenv("XDG_CONFIG_HOME")) { + result.clear(); + result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); + return true; + } +#endif + // Fallback: ~/.config + if (!home_directory(result)) { + return false; + } + append(result, ".config"); + return true; +} + bool cache_directory(SmallVectorImpl &result) { #ifdef __APPLE__ if (getDarwinConfDir(false/*tempDir*/, result)) { diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -1372,6 +1372,12 @@ return getKnownFolderPath(FOLDERID_Profile, result); } +bool user_config_directory(SmallVectorImpl &result) { + // Either local or roaming appdata may be suitable in some cases, depending + // on the data. Local is more conservative, Roaming may not always be correct. + return getKnownFolderPath(FOLDERID_LocalAppData, result); +} + bool cache_directory(SmallVectorImpl &result) { return getKnownFolderPath(FOLDERID_LocalAppData, result); } diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp --- a/llvm/unittests/Support/Path.cpp +++ b/llvm/unittests/Support/Path.cpp @@ -439,6 +439,26 @@ EXPECT_EQ(PwDir, HomeDir); } +TEST(Support, ConfigDirectoryWithEnv) { + WithEnv Env("XDG_CONFIG_HOME", "/xdg/config"); + + SmallString<128> ConfigDir; + EXPECT_TRUE(path::user_config_directory(ConfigDir)); + EXPECT_EQ("/xdg/config", ConfigDir); +} + +TEST(Support, ConfigDirectoryNoEnv) { + WithEnv Env("XDG_CONFIG_HOME", nullptr); + + SmallString<128> Fallback; + ASSERT_TRUE(path::home_directory(Fallback)); + path::append(Fallback, ".config"); + + SmallString<128> CacheDir; + EXPECT_TRUE(path::user_config_directory(CacheDir)); + EXPECT_EQ(Fallback, CacheDir); +} + TEST(Support, CacheDirectoryWithEnv) { WithEnv Env("XDG_CACHE_HOME", "/xdg/cache"); @@ -460,7 +480,29 @@ } #endif +#ifdef __APPLE__ +TEST(Support, ConfigDirectory) { + SmallString<128> Fallback; + ASSERT_TRUE(path::home_directory(Fallback)); + path::append(Fallback, "Library/Preferences"); + + SmallString<128> ConfigDir; + EXPECT_TRUE(path::user_config_directory(ConfigDir)); + EXPECT_EQ(Fallback, ConfigDir); +} +#endif + #ifdef _WIN32 +TEST(Support, ConfigDirectory) { + 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::user_config_directory(CacheDir)); + EXPECT_EQ(Expected, CacheDir); + } +} + TEST(Support, CacheDirectory) { std::string Expected = getEnvWin(L"LOCALAPPDATA"); // Do not try to test it if we don't know what to expect.