From 7df9526d6a747c1a709380dd70f9a116e591994e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Harboe?= Date: Fri, 17 Apr 2026 17:20:06 +0200 Subject: [PATCH 1/2] fix: unsetenv RUNFILES_* in tcl_library_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When OpenROAD is invoked by a build system that previously ran another Bazel binary (e.g. a Python wrapper in a Bazel action), the inherited RUNFILES_DIR / RUNFILES_MANIFEST_FILE env vars point to the *other* binary's runfiles tree. Runfiles::Create() checks those env vars first, so it resolves paths in the wrong tree. Unsetenv the three RUNFILES_* variables before Runfiles::Create() so it falls back to the exe_path derived from /proc/self/exe. Signed-off-by: Øyvind Harboe --- bazel/tcl_library_init.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bazel/tcl_library_init.cc b/bazel/tcl_library_init.cc index 08e0cc4e024..145f0dfde2c 100644 --- a/bazel/tcl_library_init.cc +++ b/bazel/tcl_library_init.cc @@ -78,6 +78,17 @@ static std::optional TclLibraryMountPoint(Tcl_Interp* interp) return Tcl_GetStringResult(interp); #else using rules_cc::cc::runfiles::Runfiles; + + // Clear inherited RUNFILES_* env vars: when OpenROAD is invoked by + // a build system that previously ran another Bazel binary (e.g. a + // Python wrapper), those variables point to the *other* binary's + // runfiles tree. Runfiles::Create() checks env vars first, so it + // would resolve paths in the wrong tree. Unsetting forces it to + // fall back to the exe_path derived from /proc/self/exe. + unsetenv("RUNFILES_DIR"); + unsetenv("RUNFILES_MANIFEST_FILE"); + unsetenv("RUNFILES_MANIFEST_ONLY"); + std::string error; std::unique_ptr runfiles( Runfiles::Create(GetProgramLocation(), BAZEL_CURRENT_REPOSITORY, &error)); From f0f28aba14505564b8ac3ee18b62886574afbb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Harboe?= Date: Sat, 18 Apr 2026 10:05:27 +0200 Subject: [PATCH 2/2] fix: try inherited RUNFILES_* before unsetting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous change unconditionally unset the RUNFILES_* env vars and relied on the /proc/self/exe fallback to find OpenROAD's runfiles. That breaks tests where OpenROAD is invoked via a wrapper (e.g. sh_test/whittle.py) whose runfiles tree already contains OpenROAD's tcl_resources_dir, because in remote-cache CI runs the real binary in execroot doesn't have an openroad.runfiles/ next to it, so the exe_path fallback fails. Probe the inherited RUNFILES_* tree for tcl_resources_dir first; only if it isn't there (the Python-wrapper case the previous commit fixed) unset the env vars and retry via exe_path. Fixes //test/orfs/gcd:gcd_whittle_test in CI. Signed-off-by: Øyvind Harboe --- bazel/tcl_library_init.cc | 50 +++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/bazel/tcl_library_init.cc b/bazel/tcl_library_init.cc index 145f0dfde2c..5345f0daad7 100644 --- a/bazel/tcl_library_init.cc +++ b/bazel/tcl_library_init.cc @@ -79,31 +79,51 @@ static std::optional TclLibraryMountPoint(Tcl_Interp* interp) #else using rules_cc::cc::runfiles::Runfiles; - // Clear inherited RUNFILES_* env vars: when OpenROAD is invoked by - // a build system that previously ran another Bazel binary (e.g. a - // Python wrapper), those variables point to the *other* binary's - // runfiles tree. Runfiles::Create() checks env vars first, so it - // would resolve paths in the wrong tree. Unsetting forces it to - // fall back to the exe_path derived from /proc/self/exe. + auto find_tcl_resources + = [](const Runfiles* runfiles) -> std::optional { + std::error_code ec; + for (const std::string loc : {"openroad", "opensta", "_main"}) { + const std::string check_loc = loc + "/bazel/tcl_resources_dir"; + const std::string path = runfiles->Rlocation(check_loc); + if (!path.empty() && std::filesystem::exists(path, ec)) { + return path; + } + } + return std::nullopt; + }; + + // First try with the inherited RUNFILES_* env vars. When OpenROAD + // is the direct Bazel target (or invoked from an sh_test whose + // runfiles tree also contains OpenROAD's data), those env vars + // already point at the correct tree. + std::string error; + std::unique_ptr runfiles( + Runfiles::Create(GetProgramLocation(), BAZEL_CURRENT_REPOSITORY, &error)); + if (runfiles) { + if (auto path = find_tcl_resources(runfiles.get())) { + return path; + } + } + + // Fallback: when OpenROAD is invoked by a build system that + // previously ran another Bazel binary (e.g. a Python wrapper), the + // inherited RUNFILES_* variables point to the *other* binary's + // runfiles tree and won't contain OpenROAD's tcl_resources_dir. + // Unset them and retry so Runfiles::Create falls back to the + // exe_path derived from /proc/self/exe. unsetenv("RUNFILES_DIR"); unsetenv("RUNFILES_MANIFEST_FILE"); unsetenv("RUNFILES_MANIFEST_ONLY"); - std::string error; - std::unique_ptr runfiles( + runfiles.reset( Runfiles::Create(GetProgramLocation(), BAZEL_CURRENT_REPOSITORY, &error)); if (!runfiles) { std::cerr << "[Warning] Failed to create bazel runfiles: " << error << "\n"; return std::nullopt; } - std::error_code ec; - for (const std::string loc : {"openroad", "opensta", "_main"}) { - const std::string check_loc = loc + "/bazel/tcl_resources_dir"; - const std::string path = runfiles->Rlocation(check_loc); - if (!path.empty() && std::filesystem::exists(path, ec)) { - return path; - } + if (auto path = find_tcl_resources(runfiles.get())) { + return path; } return std::nullopt; #endif