From afd83f201589e371ba33c7da11c8d6ad0cb0109a Mon Sep 17 00:00:00 2001 From: Alexandre Rostovtsev Date: Tue, 26 Mar 2024 16:39:56 -0400 Subject: [PATCH] [7.2.0] Respect $XDG_CACHE_HOME if available for Bazel's output root on Linux and BSD (#21817) It may well be that $HOME is on a read-only mount. In this case, it is the convention in the Linux and BSD world that $XDG_CACHE_HOME points to an appropriate writable location. Fixes https://github.com/bazelbuild/bazel/issues/16937 RELNOTES: Bazel on Linux and BSD now respects the XDG_CACHE_HOME environment variable instead of assuming that ~/.cache/bazel is writable. PiperOrigin-RevId: 614772057 Change-Id: I6377d7a90fb929843d18e82f5ed3d0adc55ac5c6 Commit 05ae91f1a04b55af94aaa7b52ef34cb3a6ce7fa4 --- site/en/remote/output-directories.md | 7 +++--- src/main/cpp/blaze_util_bsd.cc | 25 ++++++++++++------- src/main/cpp/blaze_util_linux.cc | 36 +++++++++++++++------------- src/test/cpp/startup_options_test.cc | 12 ++++++++++ 4 files changed, 51 insertions(+), 29 deletions(-) diff --git a/site/en/remote/output-directories.md b/site/en/remote/output-directories.md index 7a3965819b175e..49d42a94e4c9da 100644 --- a/site/en/remote/output-directories.md +++ b/site/en/remote/output-directories.md @@ -29,9 +29,10 @@ The solution that's currently implemented: * Bazel must be invoked from a directory containing a WORKSPACE file (the "_workspace directory_"), or a subdirectory thereof. It reports an error if it is not. -* The _outputRoot_ directory defaults to `~/.cache/bazel` on Linux, - `/private/var/tmp` on macOS, and on Windows it defaults to `%HOME%` if set, - else `%USERPROFILE%` if set, else the result of calling +* The _outputRoot_ directory defaults to `${XDG_CACHE_HOME}/bazel` (or + `~/.cache/bazel`, if the `XDG_CACHE_HOME` environment variable is not set) on + Linux, `/private/var/tmp` on macOS, and on Windows it defaults to `%HOME%` if + set, else `%USERPROFILE%` if set, else the result of calling `SHGetKnownFolderPath()` with the `FOLDERID_Profile` flag set. If the environment variable `$TEST_TMPDIR` is set, as in a test of Bazel itself, then that value overrides the default. diff --git a/src/main/cpp/blaze_util_bsd.cc b/src/main/cpp/blaze_util_bsd.cc index 8987e0c0f2c6e2..9a80750c826a79 100644 --- a/src/main/cpp/blaze_util_bsd.cc +++ b/src/main/cpp/blaze_util_bsd.cc @@ -60,17 +60,24 @@ namespace blaze { using blaze_util::GetLastErrorString; using std::string; +// ${XDG_CACHE_HOME}/bazel, a.k.a. ~/.cache/bazel by default (which is the +// fallback when XDG_CACHE_HOME is not set) string GetOutputRoot() { - char buf[2048]; - struct passwd pwbuf; - struct passwd *pw = nullptr; - int uid = getuid(); - int r = getpwuid_r(uid, &pwbuf, buf, 2048, &pw); - if (r == 0 && pw != nullptr) { - return blaze_util::JoinPath(pw->pw_dir, ".cache/bazel"); - } else { - return "/tmp"; + string xdg_cache_home = GetPathEnv("XDG_CACHE_HOME"); + if (xdg_cache_home.empty()) { + char buf[2048]; + struct passwd pwbuf; + struct passwd *pw = nullptr; + int uid = getuid(); + int r = getpwuid_r(uid, &pwbuf, buf, 2048, &pw); + if (r == 0 && pw != nullptr) { + xdg_cache_home = blaze_util::JoinPath(pw->pw_dir, ".cache"); + } else { + return "/tmp"; + } } + + return blaze_util::JoinPath(xdg_cache_home, "bazel"); } void WarnFilesystemType(const blaze_util::Path &output_base) { diff --git a/src/main/cpp/blaze_util_linux.cc b/src/main/cpp/blaze_util_linux.cc index 0f6c61777f2830..51423d3cb3e6d5 100644 --- a/src/main/cpp/blaze_util_linux.cc +++ b/src/main/cpp/blaze_util_linux.cc @@ -43,27 +43,29 @@ using blaze_util::GetLastErrorString; using std::string; using std::vector; +// ${XDG_CACHE_HOME}/bazel, a.k.a. ~/.cache/bazel by default (which is the +// fallback when XDG_CACHE_HOME is not set) string GetOutputRoot() { - string base; - string home = GetHomeDir(); - if (!home.empty()) { - base = home; - } else { - char buf[2048]; - struct passwd pwbuf; - struct passwd *pw = nullptr; - int uid = getuid(); - int r = getpwuid_r(uid, &pwbuf, buf, 2048, &pw); - if (r == 0 && pw != nullptr) { - base = pw->pw_dir; + string xdg_cache_home = GetPathEnv("XDG_CACHE_HOME"); + if (xdg_cache_home.empty()) { + string home = GetHomeDir(); // via $HOME env variable + if (home.empty()) { + // Fall back to home dir from password database + char buf[2048]; + struct passwd pwbuf; + struct passwd *pw = nullptr; + int uid = getuid(); + int r = getpwuid_r(uid, &pwbuf, buf, 2048, &pw); + if (r == 0 && pw != nullptr) { + home = pw->pw_dir; + } else { + return "/tmp"; + } } + xdg_cache_home = blaze_util::JoinPath(home, ".cache"); } - if (!base.empty()) { - return blaze_util::JoinPath(base, ".cache/bazel"); - } - - return "/tmp"; + return blaze_util::JoinPath(xdg_cache_home, "bazel"); } void WarnFilesystemType(const blaze_util::Path &output_base) { diff --git a/src/test/cpp/startup_options_test.cc b/src/test/cpp/startup_options_test.cc index 52debb7b7b9cc3..afbb786c9235c7 100644 --- a/src/test/cpp/startup_options_test.cc +++ b/src/test/cpp/startup_options_test.cc @@ -92,15 +92,27 @@ TEST_F(StartupOptionsTest, JavaLoggingOptions) { #ifdef __linux TEST_F(StartupOptionsTest, OutputRootPreferTestTmpdirIfSet) { SetEnv("HOME", "/nonexistent/home"); + SetEnv("XDG_CACHE_HOME", "/nonexistent/cache"); SetEnv("TEST_TMPDIR", "/nonexistent/tmpdir"); ReinitStartupOptions(); ASSERT_EQ("/nonexistent/tmpdir", startup_options_->output_root); } +TEST_F(StartupOptionsTest, + OutputRootPreferXdgCacheHomeIfSetAndTestTmpdirUnset) { + SetEnv("HOME", "/nonexistent/home"); + SetEnv("XDG_CACHE_HOME", "/nonexistent/cache"); + UnsetEnv("TEST_TMPDIR"); + ReinitStartupOptions(); + + ASSERT_EQ("/nonexistent/cache/bazel", startup_options_->output_root); +} + TEST_F(StartupOptionsTest, OutputRootUseHomeDirectory) { SetEnv("HOME", "/nonexistent/home"); UnsetEnv("TEST_TMPDIR"); + UnsetEnv("XDG_CACHE_HOME"); ReinitStartupOptions(); ASSERT_EQ("/nonexistent/home/.cache/bazel", startup_options_->output_root);