Skip to content

Commit c2ff89f

Browse files
authored
fix: Avoid C++ toolchain requirement if possible (bazel-contrib#2919)
By making use of the new `launcher_maker_toolchain` in Bazel 9, rules_python can avoid the requirement for a C++ toolchain targeting the target platform if that platform isn't Windows. For example, this makes it possible to cross-compile pure Python targets from one Unix to another. Since Java targets have a dependency on Python targets through the `proguard_allowlister`, this also allows Java targets to be built without any C++ toolchain.
1 parent 846dfd0 commit c2ff89f

File tree

5 files changed

+60
-8
lines changed

5 files changed

+60
-8
lines changed

python/private/internal_config_repo.bzl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ config = struct(
3232
enable_pystar = True,
3333
enable_pipstar = {enable_pipstar},
3434
enable_deprecation_warnings = {enable_deprecation_warnings},
35+
bazel_9_or_later = {bazel_9_or_later},
3536
BuiltinPyInfo = getattr(getattr(native, "legacy_globals", None), "PyInfo", {builtin_py_info_symbol}),
3637
BuiltinPyRuntimeInfo = getattr(getattr(native, "legacy_globals", None), "PyRuntimeInfo", {builtin_py_runtime_info_symbol}),
3738
BuiltinPyCcLinkParamsProvider = getattr(getattr(native, "legacy_globals", None), "PyCcLinkParamsProvider", {builtin_py_cc_link_params_provider}),
@@ -87,7 +88,10 @@ _TRANSITION_SETTINGS_DEBUG_TEMPLATE = """
8788
"""
8889

8990
def _internal_config_repo_impl(rctx):
90-
if not native.bazel_version or int(native.bazel_version.split(".")[0]) >= 8:
91+
# An empty version signifies a development build, which is treated as
92+
# the latest version.
93+
bazel_major_version = int(native.bazel_version.split(".")[0]) if native.bazel_version else 99999
94+
if bazel_major_version >= 8:
9195
builtin_py_info_symbol = "None"
9296
builtin_py_runtime_info_symbol = "None"
9397
builtin_py_cc_link_params_provider = "None"
@@ -103,6 +107,7 @@ def _internal_config_repo_impl(rctx):
103107
builtin_py_info_symbol = builtin_py_info_symbol,
104108
builtin_py_runtime_info_symbol = builtin_py_runtime_info_symbol,
105109
builtin_py_cc_link_params_provider = builtin_py_cc_link_params_provider,
110+
bazel_9_or_later = str(bazel_major_version >= 9),
106111
))
107112

108113
shim_content = _PY_INTERNAL_SHIM

python/private/py_executable.bzl

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ load("@bazel_skylib//lib:paths.bzl", "paths")
1818
load("@bazel_skylib//lib:structs.bzl", "structs")
1919
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
2020
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
21+
load("@rules_python_internal//:rules_python_config.bzl", rp_config = "config")
2122
load(":attr_builders.bzl", "attrb")
2223
load(
2324
":attributes.bzl",
@@ -69,6 +70,7 @@ load(":venv_runfiles.bzl", "create_venv_app_files")
6970
_py_builtins = py_internal
7071
_EXTERNAL_PATH_PREFIX = "external"
7172
_ZIP_RUNFILES_DIRECTORY_NAME = "runfiles"
73+
_LAUNCHER_MAKER_TOOLCHAIN_TYPE = "@bazel_tools//tools/launcher:launcher_maker_toolchain_type"
7274

7375
# Non-Google-specific attributes for executables
7476
# These attributes are for rules that accept Python sources.
@@ -228,17 +230,19 @@ accepting arbitrary Python versions.
228230
"@platforms//os:windows",
229231
],
230232
),
231-
"_windows_launcher_maker": lambda: attrb.Label(
232-
default = "@bazel_tools//tools/launcher:launcher_maker",
233-
cfg = "exec",
234-
executable = True,
235-
),
236233
"_zipper": lambda: attrb.Label(
237234
cfg = "exec",
238235
executable = True,
239236
default = "@bazel_tools//tools/zip:zipper",
240237
),
241238
},
239+
{
240+
"_windows_launcher_maker": lambda: attrb.Label(
241+
default = "@bazel_tools//tools/launcher:launcher_maker",
242+
cfg = "exec",
243+
executable = True,
244+
),
245+
} if not rp_config.bazel_9_or_later else {},
242246
)
243247

244248
def convert_legacy_create_init_to_int(kwargs):
@@ -777,6 +781,11 @@ def _create_stage1_bootstrap(
777781
is_executable = True,
778782
)
779783

784+
def _find_launcher_maker(ctx):
785+
if rp_config.bazel_9_or_later:
786+
return (ctx.toolchains[_LAUNCHER_MAKER_TOOLCHAIN_TYPE].binary, _LAUNCHER_MAKER_TOOLCHAIN_TYPE)
787+
return (ctx.executable._windows_launcher_maker, None)
788+
780789
def _create_windows_exe_launcher(
781790
ctx,
782791
*,
@@ -796,15 +805,17 @@ def _create_windows_exe_launcher(
796805
launch_info.add("1" if use_zip_file else "0", format = "use_zip_file=%s")
797806

798807
launcher = ctx.attr._launcher[DefaultInfo].files_to_run.executable
808+
executable, toolchain = _find_launcher_maker(ctx)
799809
ctx.actions.run(
800-
executable = ctx.executable._windows_launcher_maker,
810+
executable = executable,
801811
arguments = [launcher.path, launch_info, output.path],
802812
inputs = [launcher],
803813
outputs = [output],
804814
mnemonic = "PyBuildLauncher",
805815
progress_message = "Creating launcher for %{label}",
806816
# Needed to inherit PATH when using non-MSVC compilers like MinGW
807817
use_default_shell_env = True,
818+
toolchain = toolchain,
808819
)
809820

810821
def _create_zip_file(ctx, *, output, zip_main, runfiles):
@@ -1838,7 +1849,7 @@ def create_executable_rule_builder(implementation, **kwargs):
18381849
ruleb.ToolchainType(TOOLCHAIN_TYPE),
18391850
ruleb.ToolchainType(EXEC_TOOLS_TOOLCHAIN_TYPE, mandatory = False),
18401851
ruleb.ToolchainType("@bazel_tools//tools/cpp:toolchain_type", mandatory = False),
1841-
],
1852+
] + ([ruleb.ToolchainType(_LAUNCHER_MAKER_TOOLCHAIN_TYPE)] if rp_config.bazel_9_or_later else []),
18421853
cfg = dict(
18431854
implementation = _transition_executable_impl,
18441855
inputs = TRANSITION_LABELS + [labels.PYTHON_VERSION],

tests/base_rules/py_executable_base_tests.bzl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"""Tests common to py_binary and py_test (executable rules)."""
1515

1616
load("@rules_python//python:py_runtime_info.bzl", RulesPythonPyRuntimeInfo = "PyRuntimeInfo")
17+
load("@rules_python_internal//:rules_python_config.bzl", rp_config = "config")
1718
load("@rules_testing//lib:analysis_test.bzl", "analysis_test")
1819
load("@rules_testing//lib:truth.bzl", "matching")
1920
load("@rules_testing//lib:util.bzl", rt_util = "util")
@@ -114,6 +115,29 @@ def _test_basic_zip_impl(env, target):
114115

115116
_tests.append(_test_basic_zip)
116117

118+
def _test_cross_compile_to_unix(name, config):
119+
rt_util.helper_target(
120+
config.rule,
121+
name = name + "_subject",
122+
main_module = "dummy",
123+
)
124+
analysis_test(
125+
name = name,
126+
impl = _test_cross_compile_to_unix_impl,
127+
target = name + "_subject",
128+
# Cross-compilation of py_test fails since the default test toolchain
129+
# requires an execution platform that matches the target platform.
130+
config_settings = {
131+
"//command_line_option:platforms": [platform_targets.EXOTIC_UNIX],
132+
} if rp_config.bazel_9_or_later and not "py_test" in str(config.rule) else {},
133+
expect_failure = True,
134+
)
135+
136+
def _test_cross_compile_to_unix_impl(_env, _target):
137+
pass
138+
139+
_tests.append(_test_cross_compile_to_unix)
140+
117141
def _test_executable_in_runfiles(name, config):
118142
rt_util.helper_target(
119143
config.rule,

tests/support/platforms/BUILD.bazel

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,11 @@ platform(
7575
"@platforms//cpu:aarch64",
7676
],
7777
)
78+
79+
platform(
80+
name = "exotic_unix",
81+
constraint_values = [
82+
"@platforms//os:linux",
83+
"@platforms//cpu:s390x",
84+
],
85+
)

tests/support/platforms/platforms.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ platform_targets = struct(
1010
WINDOWS = Label("//tests/support/platforms:windows"),
1111
WINDOWS_AARCH64 = Label("//tests/support/platforms:windows_aarch64"),
1212
WINDOWS_X86_64 = Label("//tests/support/platforms:windows_x86_64"),
13+
14+
# Unspecified Unix platform that is unlikely to be the host platform in CI,
15+
# but still provides a Python toolchain.
16+
EXOTIC_UNIX = Label("//tests/support/platforms:exotic_unix"),
1317
)

0 commit comments

Comments
 (0)