Index: include/llvm/Support/Path.h =================================================================== --- include/llvm/Support/Path.h +++ include/llvm/Support/Path.h @@ -325,6 +325,17 @@ /// @result True if a home directory is set, false otherwise. bool home_directory(SmallVectorImpl &result); +/// @brief Get the typical OS-specific temporary directory. +/// +/// Expect the resulting path to be a directory shared with other +/// applications/services used by the user and/or the system. Params \p Path1 to +/// \p Path3 can be used to append additional directory names to the resulting +/// path. Recommended pattern is //. +/// +/// @param Result Holds the resulting path name. +void temp_directory(SmallVectorImpl &Result, const Twine &Path1, + const Twine &Path2 = "", const Twine &Path3 = ""); + /// @brief Get the user's cache directory. /// /// Expect the resulting path to be a directory shared with other Index: lib/Support/Path.cpp =================================================================== --- lib/Support/Path.cpp +++ lib/Support/Path.cpp @@ -1134,6 +1134,12 @@ namespace sys { namespace path { +void temp_directory(SmallVectorImpl &Result, const Twine &Path1, + const Twine &Path2, const Twine &Path3) { + getTempDir(Result); + append(Result, Path1, Path2, Path3); +} + bool user_cache_directory(SmallVectorImpl &Result, const Twine &Path1, const Twine &Path2, const Twine &Path3) { if (getUserCacheDir(Result)) { Index: lib/Support/Unix/Path.inc =================================================================== --- lib/Support/Unix/Path.inc +++ lib/Support/Unix/Path.inc @@ -560,12 +560,14 @@ return false; } -static bool getDarwinConfDir(bool TempDir, SmallVectorImpl &Result) { - #if defined(_CS_DARWIN_USER_TEMP_DIR) && defined(_CS_DARWIN_USER_CACHE_DIR) +enum class DarwinConfDir : bool { Temp, Cache }; + +static bool getDarwinConfDir(DarwinConfDir Dir, SmallVectorImpl &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. // macros defined in on darwin >= 9 - int ConfName = TempDir ? _CS_DARWIN_USER_TEMP_DIR - : _CS_DARWIN_USER_CACHE_DIR; + int ConfName = (Dir == DarwinConfDir::Temp) ? _CS_DARWIN_USER_TEMP_DIR + : _CS_DARWIN_USER_CACHE_DIR; size_t ConfLen = confstr(ConfName, nullptr, 0); if (ConfLen > 0) { do { @@ -581,7 +583,7 @@ Result.clear(); } - #endif +#endif return false; } @@ -596,7 +598,7 @@ } // Try Darwin configuration query - if (getDarwinConfDir(false, Result)) + if (getDarwinConfDir(DarwinConfDir::Cache, Result)) return true; // Use "$HOME/.cache" if $HOME is available @@ -620,32 +622,39 @@ return nullptr; } -static const char *getDefaultTempDir(bool ErasedOnReboot) { +static const char *getDefaultTempDir() { #ifdef P_tmpdir if ((bool)P_tmpdir) return P_tmpdir; #endif - if (ErasedOnReboot) - return "/tmp"; - return "/var/tmp"; + return "/tmp"; } -void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl &Result) { +static void getTempDir(SmallVectorImpl &Result) { Result.clear(); - if (ErasedOnReboot) { - // There is no env variable for the cache directory. - if (const char *RequestedDir = getEnvTempDir()) { - Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); - return; - } + if (const char *RequestedDir = getEnvTempDir()) { + Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); + return; } - if (getDarwinConfDir(ErasedOnReboot, Result)) + if (getDarwinConfDir(DarwinConfDir::Temp , Result)) return; - const char *RequestedDir = getDefaultTempDir(ErasedOnReboot); + const char *RequestedDir = getDefaultTempDir(); + Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); +} + +void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl &Result) { + if (ErasedOnReboot) + return getTempDir(Result); + + if (user_cache_directory(Result, "")) + return; + + const char *RequestedDir = "/var/tmp"; + Result.clear(); Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); } Index: lib/Support/Windows/Path.inc =================================================================== --- lib/Support/Windows/Path.inc +++ lib/Support/Windows/Path.inc @@ -798,8 +798,7 @@ return false; } -void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl &Result) { - (void)ErasedOnReboot; +static void getTempDir(SmallVectorImpl &Result) { Result.clear(); // Check whether the temporary directory is specified by an environment var. @@ -817,6 +816,13 @@ const char *DefaultResult = "C:\\Temp"; Result.append(DefaultResult, DefaultResult + strlen(DefaultResult)); } + +void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl &Result) { + if (!ErasedOnReboot && user_cache_directory(Result, "")) + return; + + temp_directory(Result, ""); +} } // end namespace path namespace windows { Index: unittests/Support/Path.cpp =================================================================== --- unittests/Support/Path.cpp +++ unittests/Support/Path.cpp @@ -322,6 +322,46 @@ } } +static std::string path2regex(std::string Path) { + size_t Pos = 0; + while ((Pos = Path.find('\\', Pos)) != std::string::npos) { + Path.replace(Pos, 1, "\\\\"); + Pos += 2; + } + return Path; +} + +/// Helper for running known dir test that depends on enviroment variables in +/// separated process. This allows checking different values of enviroment +/// variables without messing up the environment of the current process. +#define EXPECT_DIR(test, expected) \ + EXPECT_EXIT( \ + { \ + SmallString<300> Dir; \ + test; \ + raw_os_ostream(std::cerr) << Dir; \ + std::exit(0); \ + }, \ + ::testing::ExitedWithCode(0), path2regex(expected)) + +/// Helper for running home dir test in separated process, @see EXPECT_DIR. +#define EXPECT_HOME_DIR(prepare, expected) \ + EXPECT_DIR( \ + { \ + prepare; \ + path::home_directory(Dir); \ + }, \ + expected) + +/// Helper for running temp dir test in separated process, @see EXPECT_DIR. +#define EXPECT_TEMP_DIR(prepare, expected) \ + EXPECT_DIR( \ + { \ + prepare; \ + path::temp_directory(Dir, ""); \ + }, \ + expected) + TEST(Support, UserCacheDirectory) { SmallString<13> CacheDir; SmallString<20> CacheDir2; @@ -358,30 +398,41 @@ TempDir.clear(); path::system_temp_directory(true, TempDir); EXPECT_TRUE(!TempDir.empty()); -} -#ifdef LLVM_ON_WIN32 -static std::string path2regex(std::string Path) { - size_t Pos = 0; - while ((Pos = Path.find('\\', Pos)) != std::string::npos) { - Path.replace(Pos, 1, "\\\\"); - Pos += 2; - } - return Path; + StringRef TestToken{""}; + SmallString<32> TempDir2{TestToken}; + path::temp_directory(TempDir2, ""); + EXPECT_FALSE(TempDir2.empty()); + EXPECT_FALSE(TempDir2.startswith(TestToken)); + + path::temp_directory(TempDir, ""); + EXPECT_EQ(TempDir2, TempDir); + + path::temp_directory(TempDir, "TempInc", "TempApp", "file.tmp"); + auto It = path::rbegin(TempDir); + EXPECT_EQ("file.tmp", *It); + EXPECT_EQ("TempApp", *++It); + EXPECT_EQ("TempInc", *++It); + auto ParentDir = *++It; + + // Test Unicode: "/(pi)r^2/aleth.0" + path::temp_directory(TempDir2, "\xCF\x80r\xC2\xB2", "\xE2\x84\xB5.0"); + auto It2 = path::rbegin(TempDir2); + EXPECT_EQ("\xE2\x84\xB5.0", *It2); + EXPECT_EQ("\xCF\x80r\xC2\xB2", *++It2); + auto ParentDir2 = *++It2; + + EXPECT_EQ(ParentDir, ParentDir2); } -/// Helper for running temp dir test in separated process. See below. -#define EXPECT_TEMP_DIR(prepare, expected) \ - EXPECT_EXIT( \ - { \ - prepare; \ - SmallString<300> TempDir; \ - path::system_temp_directory(true, TempDir); \ - raw_os_ostream(std::cerr) << TempDir; \ - std::exit(0); \ - }, \ - ::testing::ExitedWithCode(0), path2regex(expected)) +#ifdef LLVM_ON_UNIX +TEST(SupportDeathTest, TempDirectoryOnUnix) { + EXPECT_TEMP_DIR(setenv("TMPDIR", "/home/LLVMUser/.tmp", true), + "/home/LLVMUser/.tmp"); +} +#endif +#ifdef LLVM_ON_WIN32 TEST(SupportDeathTest, TempDirectoryOnWindows) { // In this test we want to check how system_temp_directory responds to // different values of specific env vars. To prevent corrupting env vars of @@ -405,12 +456,12 @@ // All related env vars empty EXPECT_TEMP_DIR( - { - _wputenv_s(L"TMP", L""); - _wputenv_s(L"TEMP", L""); - _wputenv_s(L"USERPROFILE", L""); - }, - "C:\\Temp"); + { + _wputenv_s(L"TMP", L""); + _wputenv_s(L"TEMP", L""); + _wputenv_s(L"USERPROFILE", L""); + }, + "C:\\Temp"); // Test evn var / path with 260 chars. SmallString<270> Expected{"C:\\Temp\\AB\\123456789"};