Skip to content

Commit a7ba30a

Browse files
committed
wip: make py reconfig native transitions
1 parent babb84e commit a7ba30a

File tree

6 files changed

+85
-143
lines changed

6 files changed

+85
-143
lines changed

python/private/py_binary_macro.bzl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@ load(":py_binary_rule.bzl", py_binary_rule = "py_binary")
1717
load(":py_executable.bzl", "convert_legacy_create_init_to_int")
1818

1919
def py_binary(**kwargs):
20+
py_binary_macro(py_binary_rule, **kwargs)
21+
22+
def py_binary_macro(py_rule, **kwargs):
2023
convert_legacy_create_init_to_int(kwargs)
21-
py_binary_rule(**kwargs)
24+
py_rule(**kwargs)

python/private/py_binary_rule.bzl

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ load(
2121
"py_executable_impl",
2222
)
2323

24-
_PY_TEST_ATTRS = {
24+
_COVERAGE_ATTRS = {
2525
# Magic attribute to help C++ coverage work. There's no
2626
# docs about this; see TestActionBuilder.java
2727
"_collect_cc_coverage": attr.label(
@@ -45,8 +45,16 @@ def _py_binary_impl(ctx):
4545
inherited_environment = [],
4646
)
4747

48-
py_binary = create_executable_rule(
49-
implementation = _py_binary_impl,
50-
attrs = dicts.add(AGNOSTIC_BINARY_ATTRS, _PY_TEST_ATTRS),
51-
executable = True,
52-
)
48+
def create_binary_rule(*, attrs = None, **kwargs):
49+
kwargs.setdefault("implementation", _py_binary_impl)
50+
kwargs.setdefault("executable", True)
51+
return create_executable_rule(
52+
attrs = dicts.add(
53+
AGNOSTIC_BINARY_ATTRS,
54+
_COVERAGE_ATTRS,
55+
attrs or {},
56+
),
57+
**kwargs
58+
)
59+
60+
py_binary = create_binary_rule()

python/private/py_executable.bzl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1747,6 +1747,20 @@ def _transition_executable_impl(input_settings, attr):
17471747
settings[_PYTHON_VERSION_FLAG] = attr.python_version
17481748
return settings
17491749

1750+
def create_transition(extend_implementation = None, inputs = None, outputs = None, **kwargs):
1751+
if extend_implementation:
1752+
implementation = lambda *args: extend_implementation(base_impl = _transition_executable_impl, *args)
1753+
else:
1754+
implementation = _transition_executable_impl
1755+
1756+
# todo: dedupe inputs/outputs
1757+
return transition(
1758+
implementation = implementation,
1759+
inputs = [_PYTHON_VERSION_FLAG] + (inputs or []),
1760+
outputs = [_PYTHON_VERSION_FLAG] + (outputs or []),
1761+
**kwargs
1762+
)
1763+
17501764
_transition_executable = transition(
17511765
implementation = _transition_executable_impl,
17521766
inputs = [
@@ -1757,6 +1771,8 @@ _transition_executable = transition(
17571771
],
17581772
)
17591773

1774+
transition_executable_impl = _transition_executable_impl
1775+
17601776
def create_executable_rule(*, attrs, **kwargs):
17611777
return create_base_executable_rule(
17621778
attrs = attrs,

python/private/py_test_macro.bzl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@ load(":py_executable.bzl", "convert_legacy_create_init_to_int")
1717
load(":py_test_rule.bzl", py_test_rule = "py_test")
1818

1919
def py_test(**kwargs):
20+
py_test_macro(py_test_rule, **kwargs)
21+
22+
def py_test_macro(py_rule, **kwargs):
2023
convert_legacy_create_init_to_int(kwargs)
21-
py_test_rule(**kwargs)
24+
py_rule(**kwargs)

python/private/py_test_rule.bzl

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,16 @@ def _py_test_impl(ctx):
4848
maybe_add_test_execution_info(providers, ctx)
4949
return providers
5050

51-
py_test = create_executable_rule(
52-
implementation = _py_test_impl,
53-
attrs = dicts.add(AGNOSTIC_TEST_ATTRS, _BAZEL_PY_TEST_ATTRS),
54-
test = True,
55-
)
51+
def create_test_rule(*, attrs = None, **kwargs):
52+
kwargs.setdefault("implementation", _py_test_impl)
53+
kwargs.setdefault("test", True)
54+
return create_executable_rule(
55+
attrs = dicts.add(
56+
AGNOSTIC_TEST_ATTRS,
57+
_BAZEL_PY_TEST_ATTRS,
58+
attrs or {},
59+
),
60+
**kwargs
61+
)
62+
63+
py_test = create_test_rule()

tests/support/sh_py_run_test.bzl

Lines changed: 34 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -20,160 +20,78 @@ without the overhead of a bazel-in-bazel integration test.
2020
load("@rules_shell//shell:sh_test.bzl", "sh_test")
2121
load("//python:py_binary.bzl", "py_binary")
2222
load("//python:py_test.bzl", "py_test")
23+
load("//python/private:py_binary_macro.bzl", "py_binary_macro")
24+
load("//python/private:py_binary_rule.bzl", "create_binary_rule")
25+
load("//python/private:py_executable.bzl", create_executable_transition = "create_transition")
26+
load("//python/private:py_test_macro.bzl", "py_test_macro")
27+
load("//python/private:py_test_rule.bzl", "create_test_rule")
2328
load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE") # buildifier: disable=bzl-visibility
2429
load("//tests/support:support.bzl", "VISIBLE_FOR_TESTING")
2530

26-
def _perform_transition_impl(input_settings, attr):
27-
settings = dict(input_settings)
31+
def _perform_transition_impl(input_settings, attr, base_impl):
32+
settings = base_impl(input_settings, attr) | dict(input_settings)
2833
settings[VISIBLE_FOR_TESTING] = True
2934
settings["//command_line_option:build_python_zip"] = attr.build_python_zip
3035
if attr.bootstrap_impl:
3136
settings["//python/config_settings:bootstrap_impl"] = attr.bootstrap_impl
3237
if attr.extra_toolchains:
3338
settings["//command_line_option:extra_toolchains"] = attr.extra_toolchains
34-
if attr.python_version:
35-
settings["//python/config_settings:python_version"] = attr.python_version
3639
if attr.venvs_use_declare_symlink:
3740
settings["//python/config_settings:venvs_use_declare_symlink"] = attr.venvs_use_declare_symlink
3841
return settings
3942

40-
_perform_transition = transition(
41-
implementation = _perform_transition_impl,
43+
_perform_transition = create_executable_transition(
44+
extend_implementation = _perform_transition_impl,
4245
inputs = [
4346
"//python/config_settings:bootstrap_impl",
4447
"//command_line_option:extra_toolchains",
45-
"//python/config_settings:python_version",
4648
"//python/config_settings:venvs_use_declare_symlink",
4749
],
4850
outputs = [
4951
"//command_line_option:build_python_zip",
5052
"//command_line_option:extra_toolchains",
5153
"//python/config_settings:bootstrap_impl",
52-
"//python/config_settings:python_version",
5354
"//python/config_settings:venvs_use_declare_symlink",
5455
VISIBLE_FOR_TESTING,
5556
],
5657
)
5758

58-
def _py_reconfig_impl(ctx):
59-
default_info = ctx.attr.target[DefaultInfo]
60-
exe_ext = default_info.files_to_run.executable.extension
61-
if exe_ext:
62-
exe_ext = "." + exe_ext
63-
exe_name = ctx.label.name + exe_ext
64-
65-
executable = ctx.actions.declare_file(exe_name)
66-
ctx.actions.symlink(output = executable, target_file = default_info.files_to_run.executable)
67-
68-
default_outputs = [executable]
69-
70-
# todo: could probably check target.owner vs src.owner to check if it should
71-
# be symlinked or included as-is
72-
# For simplicity of implementation, we're assuming the target being run is
73-
# py_binary-like. In order for Windows to work, we need to make sure the
74-
# file that the .exe launcher runs (the .zip or underlying non-exe
75-
# executable) is a sibling of the .exe file with the same base name.
76-
for src in default_info.files.to_list():
77-
if src.extension in ("", "zip"):
78-
ext = ("." if src.extension else "") + src.extension
79-
output = ctx.actions.declare_file(ctx.label.name + ext)
80-
ctx.actions.symlink(output = output, target_file = src)
81-
default_outputs.append(output)
82-
83-
return [
84-
DefaultInfo(
85-
executable = executable,
86-
files = depset(default_outputs),
87-
# On windows, the other default outputs must also be included
88-
# in runfiles so the exe launcher can find the backing file.
89-
runfiles = ctx.runfiles(default_outputs).merge(
90-
default_info.default_runfiles,
91-
),
92-
),
93-
ctx.attr.target[OutputGroupInfo],
94-
# Inherit the expanded environment from the inner target.
95-
ctx.attr.target[RunEnvironmentInfo],
96-
]
97-
98-
def _make_reconfig_rule(**kwargs):
99-
attrs = {
100-
"bootstrap_impl": attr.string(),
101-
"build_python_zip": attr.string(default = "auto"),
102-
"extra_toolchains": attr.string_list(
103-
doc = """
59+
_RECONFIG_ATTRS = {
60+
"bootstrap_impl": attr.string(),
61+
"build_python_zip": attr.string(default = "auto"),
62+
"extra_toolchains": attr.string_list(
63+
doc = """
10464
Value for the --extra_toolchains flag.
10565
10666
NOTE: You'll likely have to also specify //tests/support/cc_toolchains:all (or some CC toolchain)
10767
to make the RBE presubmits happy, which disable auto-detection of a CC
10868
toolchain.
10969
""",
110-
),
111-
"python_version": attr.string(),
112-
"target": attr.label(executable = True, cfg = "target"),
113-
"venvs_use_declare_symlink": attr.string(),
114-
"_allowlist_function_transition": attr.label(
115-
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
116-
),
117-
}
118-
return rule(
119-
implementation = _py_reconfig_impl,
120-
attrs = attrs,
121-
cfg = _perform_transition,
122-
**kwargs
123-
)
70+
),
71+
"venvs_use_declare_symlink": attr.string(),
72+
}
12473

125-
_py_reconfig_binary = _make_reconfig_rule(executable = True)
126-
127-
_py_reconfig_test = _make_reconfig_rule(test = True)
128-
129-
def _py_reconfig_executable(*, name, py_reconfig_rule, py_inner_rule, **kwargs):
130-
reconfig_only_kwarg_names = [
131-
# keep sorted
132-
"bootstrap_impl",
133-
"build_python_zip",
134-
"extra_toolchains",
135-
"python_version",
136-
"venvs_use_declare_symlink",
137-
]
138-
reconfig_kwargs = {
139-
key: kwargs.pop(key, None)
140-
for key in reconfig_only_kwarg_names
141-
}
142-
reconfig_kwargs["target_compatible_with"] = kwargs.get("target_compatible_with")
143-
144-
inner_name = "_{}_inner".format(name)
145-
py_reconfig_rule(
146-
name = name,
147-
target = inner_name,
148-
**reconfig_kwargs
149-
)
150-
py_inner_rule(
151-
name = inner_name,
152-
tags = ["manual"],
153-
**kwargs
154-
)
74+
_py_reconfig_binary = create_binary_rule(
75+
attrs = _RECONFIG_ATTRS,
76+
cfg = _perform_transition,
77+
)
78+
79+
_py_reconfig_test = create_test_rule(
80+
attrs = _RECONFIG_ATTRS,
81+
cfg = _perform_transition,
82+
)
15583

156-
def py_reconfig_test(*, name, **kwargs):
84+
def py_reconfig_test(**kwargs):
15785
"""Create a py_test with customized build settings for testing.
15886
15987
Args:
160-
name: str, name of teset target.
161-
**kwargs: kwargs to pass along to _py_reconfig_test and py_test.
88+
name: str, name of test target.
89+
**kwargs: kwargs to pass along to _py_reconfig_test.
16290
"""
163-
_py_reconfig_executable(
164-
name = name,
165-
py_reconfig_rule = _py_reconfig_test,
166-
py_inner_rule = py_test,
167-
**kwargs
168-
)
91+
py_test_macro(_py_reconfig_test, **kwargs)
16992

170-
def py_reconfig_binary(*, name, **kwargs):
171-
_py_reconfig_executable(
172-
name = name,
173-
py_reconfig_rule = _py_reconfig_binary,
174-
py_inner_rule = py_binary,
175-
**kwargs
176-
)
93+
def py_reconfig_binary(**kwargs):
94+
py_binary_macro(_py_reconfig_binary, **kwargs)
17795

17896
def sh_py_run_test(*, name, sh_src, py_src, **kwargs):
17997
"""Run a py_binary within a sh_test.
@@ -196,26 +114,12 @@ def sh_py_run_test(*, name, sh_src, py_src, **kwargs):
196114
"BIN_RLOCATION": "$(rlocationpaths {})".format(bin_name),
197115
},
198116
)
199-
200-
py_binary_kwargs = {
201-
key: kwargs.pop(key)
202-
for key in ("imports", "deps", "env")
203-
if key in kwargs
204-
}
205-
206-
_py_reconfig_binary(
117+
py_reconfig_binary(
207118
name = bin_name,
208-
tags = ["manual"],
209-
target = "_{}_plain_bin".format(name),
210-
**kwargs
211-
)
212-
213-
py_binary(
214-
name = "_{}_plain_bin".format(name),
215119
srcs = [py_src],
216120
main = py_src,
217121
tags = ["manual"],
218-
**py_binary_kwargs
122+
**kwargs
219123
)
220124

221125
def _current_build_settings_impl(ctx):

0 commit comments

Comments
 (0)