Skip to content

Commit ca41e7d

Browse files
committed
fix: fixes to prepare for making bootstrap=script the default for Linux
1 parent abdf560 commit ca41e7d

22 files changed

+360
-56
lines changed

.bazelignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ examples/pip_parse/bazel-pip_parse
2525
examples/pip_parse_vendored/bazel-pip_parse_vendored
2626
examples/pip_repository_annotations/bazel-pip_repository_annotations
2727
examples/py_proto_library/bazel-py_proto_library
28+
gazelle/bazel-gazelle
2829
tests/integration/compile_pip_requirements/bazel-compile_pip_requirements
2930
tests/integration/ignore_root_user_error/bazel-ignore_root_user_error
3031
tests/integration/local_toolchains/bazel-local_toolchains

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Unreleased changes template.
7272
* (toolchains) Previously [#2636](https://github.com/bazel-contrib/rules_python/pull/2636)
7373
changed the semantics of `ignore_root_user_error` from "ignore" to "warning". This is now
7474
flipped back to ignoring the issue, and will only emit a warning when the attribute is set
75-
`False`.
75+
`False`.
7676
* (pypi) The PyPI extension will no longer write the lock file entries as the
7777
extension has been marked reproducible.
7878
Fixes [#2434](https://github.com/bazel-contrib/rules_python/issues/2434).
@@ -81,6 +81,10 @@ Unreleased changes template.
8181
[PR #2746](https://github.com/bazel-contrib/rules_python/pull/2746).
8282
* (rules) {attr}`py_binary.srcs` and {attr}`py_test.srcs` is no longer mandatory when
8383
`main_module` is specified (for `--bootstrap_impl=script`)
84+
* (rules) On Windows, {obj}`--bootstrap_impl=system_python` is forced. This
85+
allows setting `--bootstrap_impl=script` in bazelrc for mixed-platform
86+
environments.
87+
8488

8589
[20250317]: https://github.com/astral-sh/python-build-standalone/releases/tag/20250317
8690

@@ -102,6 +106,11 @@ Unreleased changes template.
102106
* (packaging) An empty `requires_file` is treated as if it were omitted, resulting in a valid `METADATA` file.
103107
* (rules) py_wheel and sphinxdocs rules now propagate `target_compatible_with` to all targets they create.
104108
[PR #2788](https://github.com/bazel-contrib/rules_python/pull/2788).
109+
* Fixes when using {obj}`--bootstrap_impl=script`:
110+
* `compile_pip_requirements` is now works with it
111+
* The `sys._base_executable` value will reflect the underlying interpreter,
112+
not venv interpreter.
113+
* The {obj}`//python/runtime_env_toolchains:all` toolchain now works with it.
105114

106115
{#v0-0-0-added}
107116
### Added

examples/bzlmod/pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[project]
2+
name = "bzlmod_example"
3+
version = "0.0.0"
4+
5+
dependencies = [
6+
"typing-extensions"
7+
]
8+

examples/pip_repository_annotations/.bazelrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ try-import %workspace%/user.bazelrc
55
# is in examples/bzlmod as the `whl_mods` feature.
66
common --noenable_bzlmod
77
common --enable_workspace
8+
common --legacy_external_runfiles=false
89
common --incompatible_python_disallow_native_rules

examples/pip_repository_annotations/pip_repository_annotations_test.py

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import unittest
2222
from pathlib import Path
2323

24-
from rules_python.python.runfiles import runfiles
24+
from python.runfiles import runfiles
2525

2626

2727
class PipRepositoryAnnotationsTest(unittest.TestCase):
@@ -34,11 +34,7 @@ def wheel_pkg_dir(self) -> str:
3434

3535
def test_build_content_and_data(self):
3636
r = runfiles.Create()
37-
rpath = r.Rlocation(
38-
"pip_repository_annotations_example/external/{}/generated_file.txt".format(
39-
self.wheel_pkg_dir()
40-
)
41-
)
37+
rpath = r.Rlocation("{}/generated_file.txt".format(self.wheel_pkg_dir()))
4238
generated_file = Path(rpath)
4339
self.assertTrue(generated_file.exists())
4440

@@ -47,11 +43,7 @@ def test_build_content_and_data(self):
4743

4844
def test_copy_files(self):
4945
r = runfiles.Create()
50-
rpath = r.Rlocation(
51-
"pip_repository_annotations_example/external/{}/copied_content/file.txt".format(
52-
self.wheel_pkg_dir()
53-
)
54-
)
46+
rpath = r.Rlocation("{}/copied_content/file.txt".format(self.wheel_pkg_dir()))
5547
copied_file = Path(rpath)
5648
self.assertTrue(copied_file.exists())
5749

@@ -61,7 +53,7 @@ def test_copy_files(self):
6153
def test_copy_executables(self):
6254
r = runfiles.Create()
6355
rpath = r.Rlocation(
64-
"pip_repository_annotations_example/external/{}/copied_content/executable{}".format(
56+
"{}/copied_content/executable{}".format(
6557
self.wheel_pkg_dir(),
6658
".exe" if platform.system() == "windows" else ".py",
6759
)
@@ -82,7 +74,7 @@ def test_data_exclude_glob(self):
8274
current_wheel_version = "0.38.4"
8375

8476
r = runfiles.Create()
85-
dist_info_dir = "pip_repository_annotations_example/external/{}/site-packages/wheel-{}.dist-info".format(
77+
dist_info_dir = "{}/site-packages/wheel-{}.dist-info".format(
8678
self.wheel_pkg_dir(),
8779
current_wheel_version,
8880
)
@@ -113,11 +105,8 @@ def test_extra(self):
113105
# This test verifies that annotations work correctly for pip packages with extras
114106
# specified, in this case requests[security].
115107
r = runfiles.Create()
116-
rpath = r.Rlocation(
117-
"pip_repository_annotations_example/external/{}/generated_file.txt".format(
118-
self.requests_pkg_dir()
119-
)
120-
)
108+
path = "{}/generated_file.txt".format(self.requests_pkg_dir())
109+
rpath = r.Rlocation(path)
121110
generated_file = Path(rpath)
122111
self.assertTrue(generated_file.exists())
123112

gazelle/WORKSPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ load("//:internal_dev_deps.bzl", "internal_dev_deps")
4242

4343
internal_dev_deps()
4444

45+
register_toolchains("@rules_python//python/runtime_env_toolchains:all")
46+
4547
load("//:deps.bzl", _py_gazelle_deps = "gazelle_deps")
4648

4749
# gazelle:repository_macro deps.bzl%go_deps

python/config_settings/BUILD.bazel

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ load(
1111
"PrecompileSourceRetentionFlag",
1212
"VenvsSitePackages",
1313
"VenvsUseDeclareSymlinkFlag",
14+
rp_string_flag = "string_flag",
1415
)
1516
load(
1617
"//python/private/pypi:flags.bzl",
@@ -87,14 +88,27 @@ string_flag(
8788
visibility = ["//visibility:public"],
8889
)
8990

90-
string_flag(
91+
rp_string_flag(
9192
name = "bootstrap_impl",
9293
build_setting_default = BootstrapImplFlag.SYSTEM_PYTHON,
94+
override = select({
95+
# Windows doesn't yet support bootstrap=script, so force disable it
96+
":_is_windows": BootstrapImplFlag.SYSTEM_PYTHON,
97+
"//conditions:default": "",
98+
}),
9399
values = sorted(BootstrapImplFlag.__members__.values()),
94100
# NOTE: Only public because it's an implicit dependency
95101
visibility = ["//visibility:public"],
96102
)
97103

104+
# For some reason, @platforms//os:windows can't be directly used
105+
# in the select() for the flag. But it can be used when put behind
106+
# a config_setting().
107+
config_setting(
108+
name = "_is_windows",
109+
constraint_values = ["@platforms//os:windows"],
110+
)
111+
98112
# This is used for pip and hermetic toolchain resolution.
99113
string_flag(
100114
name = "py_linux_libc",

python/private/flags.bzl

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,38 @@ AddSrcsToRunfilesFlag = FlagEnum(
3535
is_enabled = _AddSrcsToRunfilesFlag_is_enabled,
3636
)
3737

38+
def _string_flag_impl(ctx):
39+
if ctx.attr.override:
40+
value = ctx.attr.override
41+
else:
42+
value = ctx.build_setting_value
43+
44+
if value not in ctx.attr.values:
45+
fail((
46+
"Invalid value for {name}: got {value}, must " +
47+
"be one of {allowed}"
48+
).format(
49+
name = ctx.label,
50+
value = value,
51+
allowed = ctx.attr.values,
52+
))
53+
54+
return [
55+
BuildSettingInfo(value = value),
56+
config_common.FeatureFlagInfo(value = value),
57+
]
58+
59+
string_flag = rule(
60+
implementation = _string_flag_impl,
61+
build_setting = config.string(flag = True),
62+
attrs = {
63+
"override": attr.string(),
64+
"values": attr.string_list(),
65+
},
66+
)
67+
3868
def _bootstrap_impl_flag_get_value(ctx):
39-
return ctx.attr._bootstrap_impl_flag[BuildSettingInfo].value
69+
return ctx.attr._bootstrap_impl_flag[config_common.FeatureFlagInfo].value
4070

4171
# buildifier: disable=name-conventions
4272
BootstrapImplFlag = enum(

python/private/get_local_runtime_info.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"micro": sys.version_info.micro,
2323
"include": sysconfig.get_path("include"),
2424
"implementation_name": sys.implementation.name,
25+
"base_executable": sys._base_executable,
2526
}
2627

2728
config_vars = [

python/private/local_runtime_repo.bzl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ def _local_runtime_repo_impl(rctx):
8484
info = json.decode(exec_result.stdout)
8585
logger.info(lambda: _format_get_info_result(info))
8686

87+
# We use base_executable because we want the path within a Python
88+
# installation directory ("PYTHONHOME"). The problems with sys.executable
89+
# are:
90+
# * If we're in an activated venv, then we don't want the venv's
91+
# `bin/python3` path to be used -- it isn't an actual Python installation.
92+
# * If sys.executable is a wrapper (e.g. pyenv), then (1) it may not be
93+
# located within an actual Python installation directory, and (2) it
94+
# can interfer with Python recognizing when it's within a venv.
95+
#
96+
# In some cases, it may be a symlink (usually e.g. `python3->python3.12`),
97+
# but we don't realpath() it to respect what it has decided is the
98+
# appropriate path.
99+
interpreter_path = info["base_executable"]
100+
87101
# NOTE: Keep in sync with recursive glob in define_local_runtime_toolchain_impl
88102
repo_utils.watch_tree(rctx, rctx.path(info["include"]))
89103

0 commit comments

Comments
 (0)