Skip to content

feat: default to bootstrap script for non-windows #2858

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
May 6, 2025
Merged
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ END_UNRELEASED_TEMPLATE
{#v0-0-0-changed}
### Changed

* If using the (deprecated) autodetecting/runtime_env toolchain, then the Python
version specified at build-time *must* match the Python version used at
runtime (the {obj}`--@rules_python//python/config_settings:python_version`
flag and the {attr}`python_version` attribute control the build-time version
for a target). If they don't match, dependencies won't be importable. (Such a
misconfiguration was unlikely to work to begin with; this is called out as an
FYI).
* (rules) {obj}`--bootstrap_impl=script` is the default for non-Windows.
* (rules) On Windows, {obj}`--bootstrap_impl=system_python` is forced. This
allows setting `--bootstrap_impl=script` in bazelrc for mixed-platform
environments.
Expand Down
7 changes: 6 additions & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,12 @@ internal_dev_deps = use_extension(
"internal_dev_deps",
dev_dependency = True,
)
use_repo(internal_dev_deps, "buildkite_config", "wheel_for_testing")
use_repo(
internal_dev_deps,
"buildkite_config",
"rules_python_runtime_env_tc_info",
"wheel_for_testing",
)

# Add gazelle plugin so that we can run the gazelle example as an e2e integration
# test and include the distribution files.
Expand Down
9 changes: 9 additions & 0 deletions docs/api/rules_python/python/config_settings/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ Values:
::::{bzl:flag} bootstrap_impl
Determine how programs implement their startup process.

The default for this depends on the platform:
* Windows: `system_python` (**always** used)
* Other: `script`

Values:
* `system_python`: Use a bootstrap that requires a system Python available
in order to start programs. This requires
Expand All @@ -269,6 +273,11 @@ instead.
:::{versionadded} 0.33.0
:::

:::{versionchanged} VERSION_NEXT_FEATURE
* The default for non-Windows changed from `system_python` to `script`.
* On Windows, the value is forced to `system_python`.
:::

::::

::::{bzl:flag} current_config
Expand Down
3 changes: 3 additions & 0 deletions internal_dev_setup.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ load("@rules_shell//shell:repositories.bzl", "rules_shell_dependencies", "rules_
load("//:version.bzl", "SUPPORTED_BAZEL_VERSIONS")
load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS")
load("//python/private:pythons_hub.bzl", "hub_repo") # buildifier: disable=bzl-visibility
load("//python/private:runtime_env_repo.bzl", "runtime_env_repo") # buildifier: disable=bzl-visibility
load("//python/private/pypi:deps.bzl", "pypi_deps") # buildifier: disable=bzl-visibility

def rules_python_internal_setup():
Expand All @@ -40,6 +41,8 @@ def rules_python_internal_setup():
python_versions = sorted(TOOL_VERSIONS.keys()),
)

runtime_env_repo(name = "rules_python_runtime_env_tc_info")

pypi_deps()

bazel_skylib_workspace()
Expand Down
2 changes: 1 addition & 1 deletion python/config_settings/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ string_flag(

rp_string_flag(
name = "bootstrap_impl",
build_setting_default = BootstrapImplFlag.SYSTEM_PYTHON,
build_setting_default = BootstrapImplFlag.SCRIPT,
override = select({
# Windows doesn't yet support bootstrap=script, so force disable it
":_is_windows": BootstrapImplFlag.SYSTEM_PYTHON,
Expand Down
17 changes: 13 additions & 4 deletions python/private/config_settings.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,19 @@ def is_python_version_at_least(name, **kwargs):
)

def _python_version_at_least_impl(ctx):
at_least = tuple(ctx.attr.at_least.split("."))
current = tuple(
ctx.attr._major_minor[config_common.FeatureFlagInfo].value.split("."),
)
flag_value = ctx.attr._major_minor[config_common.FeatureFlagInfo].value

# CI is, somehow, getting an empty string for the current flag value.
# How isn't clear.
if not flag_value:
return [config_common.FeatureFlagInfo(value = "no")]

current = tuple([
int(x)
for x in flag_value.split(".")
])
at_least = tuple([int(x) for x in ctx.attr.at_least.split(".")])

value = "yes" if current >= at_least else "no"
return [config_common.FeatureFlagInfo(value = value)]

Expand Down
2 changes: 2 additions & 0 deletions python/private/internal_dev_deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

load("@bazel_ci_rules//:rbe_repo.bzl", "rbe_preconfig")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
load(":runtime_env_repo.bzl", "runtime_env_repo")

def _internal_dev_deps_impl(mctx):
_ = mctx # @unused
Expand All @@ -37,6 +38,7 @@ def _internal_dev_deps_impl(mctx):
name = "buildkite_config",
toolchain = "ubuntu1804-bazel-java11",
)
runtime_env_repo(name = "rules_python_runtime_env_tc_info")

internal_dev_deps = module_extension(
implementation = _internal_dev_deps_impl,
Expand Down
41 changes: 41 additions & 0 deletions python/private/runtime_env_repo.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Internal setup to help the runtime_env toolchain."""

load("//python/private:repo_utils.bzl", "repo_utils")

def _runtime_env_repo_impl(rctx):
pyenv = repo_utils.which_unchecked(rctx, "pyenv").binary
if pyenv != None:
pyenv_version_file = repo_utils.execute_checked(
rctx,
op = "GetPyenvVersionFile",
arguments = [pyenv, "version-file"],
).stdout.strip()

# When pyenv is used, the version file is what decided the
# version used. Watch it so we compute the correct value if the
# user changes it.
rctx.watch(pyenv_version_file)

version = repo_utils.execute_checked(
rctx,
op = "GetPythonVersion",
arguments = [
"python3",
"-I",
"-c",
"""import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")""",
],
environment = {
# Prevent the user's current shell from influencing the result.
# This envvar won't be present when a test is run.
# NOTE: This should be None, but Bazel 7 doesn't support None
# values. Thankfully, pyenv treats empty string the same as missing.
"PYENV_VERSION": "",
},
).stdout.strip()
rctx.file("info.bzl", "PYTHON_VERSION = '{}'\n".format(version))
rctx.file("BUILD.bazel", "")

runtime_env_repo = repository_rule(
implementation = _runtime_env_repo_impl,
)
3 changes: 3 additions & 0 deletions python/private/runtime_env_toolchain_interpreter.sh
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ if [ -e "$self_dir/pyvenv.cfg" ] || [ -e "$self_dir/../pyvenv.cfg" ]; then
;;
esac

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.
# NOTE: The PYTHONEXECUTABLE envvar only works for non-Mac starting in Python 3.11
Expand Down
4 changes: 4 additions & 0 deletions tests/runtime_env_toolchain/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

load("@rules_python_runtime_env_tc_info//:info.bzl", "PYTHON_VERSION")
load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
load("//tests/support:support.bzl", "CC_TOOLCHAIN")
load(":runtime_env_toolchain_tests.bzl", "runtime_env_toolchain_test_suite")
Expand All @@ -30,6 +31,9 @@ py_reconfig_test(
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.
Expand Down