From 6f0c2e74322778fd3775adad1fa71ada703764af Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Thu, 19 Jun 2025 10:06:20 -0700 Subject: [PATCH 1/2] fix(toolchains): use posix-compatible exec -a alternative --- .../runtime_env_toolchain_interpreter.sh | 13 ++++++----- tests/runtime_env_toolchain/BUILD.bazel | 23 +++++++++++++++++++ .../toolchain_runs_test.py | 6 +++++ tests/support/sh_py_run_test.bzl | 6 +++++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/python/private/runtime_env_toolchain_interpreter.sh b/python/private/runtime_env_toolchain_interpreter.sh index 7b3ec598b2..dd4d648d12 100755 --- a/python/private/runtime_env_toolchain_interpreter.sh +++ b/python/private/runtime_env_toolchain_interpreter.sh @@ -71,14 +71,15 @@ if [ -e "$self_dir/pyvenv.cfg" ] || [ -e "$self_dir/../pyvenv.cfg" ]; then if [ ! -e "$PYTHON_BIN" ]; then die "ERROR: Python interpreter does not exist: $PYTHON_BIN" fi - # PYTHONEXECUTABLE is also used because `exec -a` doesn't fully trick the - # pyenv wrappers. + # PYTHONEXECUTABLE is also used because switching argv0 doesn't fully trick + # the pyenv wrappers. # NOTE: The PYTHONEXECUTABLE envvar only works for non-Mac starting in Python 3.11 export PYTHONEXECUTABLE="$venv_bin" - # Python looks at argv[0] to determine sys.executable, so use exec -a - # to make it think it's the venv's binary, not the actual one invoked. - # NOTE: exec -a isn't strictly posix-compatible, but very widespread - exec -a "$venv_bin" "$PYTHON_BIN" "$@" + # Python looks at argv[0] to determine sys.executable, so set that to the venv + # binary, not the actual one invoked. + # NOTE: exec -a would be simpler, but isn't posix-compatible, and dash shell + # (Ubuntu/debian default) doesn't support it; see #3009. + exec sh -c "$PYTHON_BIN \$@" "$venv_bin" "$@" else exec "$PYTHON_BIN" "$@" fi diff --git a/tests/runtime_env_toolchain/BUILD.bazel b/tests/runtime_env_toolchain/BUILD.bazel index 2f82d204ff..f1bda251f9 100644 --- a/tests/runtime_env_toolchain/BUILD.bazel +++ b/tests/runtime_env_toolchain/BUILD.bazel @@ -40,3 +40,26 @@ py_reconfig_test( tags = ["no-remote-exec"], deps = ["//python/runfiles"], ) + +py_reconfig_test( + name = "bootstrap_script_test", + srcs = ["toolchain_runs_test.py"], + bootstrap_impl = "script", + data = [ + "//tests/support:current_build_settings", + ], + extra_toolchains = [ + "//python/runtime_env_toolchains:all", + # Necessary for RBE CI + CC_TOOLCHAIN, + ], + main = "toolchain_runs_test.py", + # With bootstrap=script, the build version must match the runtime version + # because the venv has the version in the lib/site-packages dir name. + python_version = PYTHON_VERSION, + # Our RBE has Python 3.6, which is too old for the language features + # we use now. Using the runtime-env toolchain on RBE is pretty + # questionable anyways. + tags = ["no-remote-exec"], + deps = ["//python/runfiles"], +) diff --git a/tests/runtime_env_toolchain/toolchain_runs_test.py b/tests/runtime_env_toolchain/toolchain_runs_test.py index 7be2472e8b..57b792aa6b 100644 --- a/tests/runtime_env_toolchain/toolchain_runs_test.py +++ b/tests/runtime_env_toolchain/toolchain_runs_test.py @@ -1,6 +1,7 @@ import json import pathlib import platform +import sys import unittest from python.runfiles import runfiles @@ -23,6 +24,11 @@ def test_ran(self): settings["interpreter"]["short_path"], ) + if settings["bootstrap_impl"] == "script": + # Verify we're running in a venv + self.assertNotEqual(sys.prefix, sys.base_prefix) + self.assertIn(".venv/", sys.executable) + if __name__ == "__main__": unittest.main() diff --git a/tests/support/sh_py_run_test.bzl b/tests/support/sh_py_run_test.bzl index 69141fe8a4..49445ed304 100644 --- a/tests/support/sh_py_run_test.bzl +++ b/tests/support/sh_py_run_test.bzl @@ -135,6 +135,7 @@ def _current_build_settings_impl(ctx): ctx.actions.write( output = info, content = json.encode({ + "bootstrap_impl": ctx.attr._bootstrap_impl_flag[config_common.FeatureFlagInfo].value, "interpreter": { "short_path": runtime.interpreter.short_path if runtime.interpreter else None, }, @@ -153,6 +154,11 @@ Writes information about the current build config to JSON for testing. This is so tests can verify information about the build config used for them. """, implementation = _current_build_settings_impl, + attrs = { + "_bootstrap_impl_flag": attr.label( + default = "//python/config_settings:bootstrap_impl", + ), + }, toolchains = [ TARGET_TOOLCHAIN_TYPE, ], From a91a86be4dfd3f97dbd5c602702b3cf370a8cf87 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Thu, 19 Jun 2025 23:04:08 -0700 Subject: [PATCH 2/2] make runtime env test pass for runtime created venv --- tests/runtime_env_toolchain/toolchain_runs_test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/runtime_env_toolchain/toolchain_runs_test.py b/tests/runtime_env_toolchain/toolchain_runs_test.py index 57b792aa6b..c66b0bbd8a 100644 --- a/tests/runtime_env_toolchain/toolchain_runs_test.py +++ b/tests/runtime_env_toolchain/toolchain_runs_test.py @@ -27,7 +27,10 @@ def test_ran(self): if settings["bootstrap_impl"] == "script": # Verify we're running in a venv self.assertNotEqual(sys.prefix, sys.base_prefix) - self.assertIn(".venv/", sys.executable) + # .venv/ occurs for a build-time venv. + # For a runtime created venv, it goes into a temp dir, so + # look for the /bin/ dir as an indicator. + self.assertRegex(sys.executable, r"[.]venv/|/bin/") if __name__ == "__main__":