Skip to content

Commit 0c1ce6f

Browse files
authored
internal(pystar): Copy @bazel_tools//tools/python files to rules_python (bazel-contrib#1437)
This copies the useful pieces from @bazel_tools//tools/python into rules_python. They're copied in relatively as-is, and not yet used. Subsequent commits will make them usable. These pieces are: * Bootstrap template (python_bootstrap_template.txt) * The py_runtime_pair rule (split from toolchain.bzl) * Autodetecting toolchain setup (split from toolchain.bzl) Work towards bazel-contrib#1069
1 parent 4a0ba3b commit 0c1ce6f

File tree

3 files changed

+826
-0
lines changed

3 files changed

+826
-0
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Copyright 2019 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Definitions related to the Python toolchain."""
16+
17+
load(":utils.bzl", "expand_pyversion_template")
18+
19+
def define_autodetecting_toolchain(
20+
name,
21+
pywrapper_template,
22+
windows_config_setting):
23+
"""Defines the autodetecting Python toolchain.
24+
25+
This includes both strict and non-strict variants.
26+
27+
For use only by @bazel_tools//tools/python:BUILD; see the documentation
28+
comment there.
29+
30+
Args:
31+
name: The name of the toolchain to introduce. Must have value
32+
"autodetecting_toolchain". This param is present only to make the
33+
BUILD file more readable.
34+
pywrapper_template: The label of the pywrapper_template.txt file.
35+
windows_config_setting: The label of a config_setting that matches when
36+
the platform is windows, in which case the toolchain is configured
37+
in a way that triggers a workaround for #7844.
38+
"""
39+
if native.package_name() != "tools/python":
40+
fail("define_autodetecting_toolchain() is private to " +
41+
"@bazel_tools//tools/python")
42+
if name != "autodetecting_toolchain":
43+
fail("Python autodetecting toolchain must be named " +
44+
"'autodetecting_toolchain'")
45+
46+
expand_pyversion_template(
47+
name = "_generate_wrappers",
48+
template = pywrapper_template,
49+
out2 = ":py2wrapper.sh",
50+
out3 = ":py3wrapper.sh",
51+
out2_nonstrict = ":py2wrapper_nonstrict.sh",
52+
out3_nonstrict = ":py3wrapper_nonstrict.sh",
53+
visibility = ["//visibility:private"],
54+
)
55+
56+
# Note that the pywrapper script is a .sh file, not a sh_binary target. If
57+
# we needed to make it a proper shell target, e.g. because it needed to
58+
# access runfiles and needed to depend on the runfiles library, then we'd
59+
# have to use a workaround to allow it to be depended on by py_runtime. See
60+
# https://github.com/bazelbuild/bazel/issues/4286#issuecomment-475661317.
61+
62+
# buildifier: disable=native-py
63+
py_runtime(
64+
name = "_autodetecting_py3_runtime",
65+
interpreter = ":py3wrapper.sh",
66+
python_version = "PY3",
67+
stub_shebang = "#!/usr/bin/env python3",
68+
visibility = ["//visibility:private"],
69+
)
70+
71+
# buildifier: disable=native-py
72+
py_runtime(
73+
name = "_autodetecting_py3_runtime_nonstrict",
74+
interpreter = ":py3wrapper_nonstrict.sh",
75+
python_version = "PY3",
76+
stub_shebang = "#!/usr/bin/env python3",
77+
visibility = ["//visibility:private"],
78+
)
79+
80+
# This is a dummy runtime whose interpreter_path triggers the native rule
81+
# logic to use the legacy behavior on Windows.
82+
# TODO(#7844): Remove this target.
83+
# buildifier: disable=native-py
84+
py_runtime(
85+
name = "_magic_sentinel_runtime",
86+
interpreter_path = "/_magic_pyruntime_sentinel_do_not_use",
87+
python_version = "PY3",
88+
visibility = ["//visibility:private"],
89+
)
90+
91+
py_runtime_pair(
92+
name = "_autodetecting_py_runtime_pair",
93+
py3_runtime = select({
94+
# If we're on windows, inject the sentinel to tell native rule logic
95+
# that we attempted to use the autodetecting toolchain and need to
96+
# switch back to legacy behavior.
97+
# TODO(#7844): Remove this hack.
98+
windows_config_setting: ":_magic_sentinel_runtime",
99+
"//conditions:default": ":_autodetecting_py3_runtime",
100+
}),
101+
visibility = ["//visibility:public"],
102+
)
103+
104+
py_runtime_pair(
105+
name = "_autodetecting_py_runtime_pair_nonstrict",
106+
py3_runtime = select({
107+
# Same hack as above.
108+
# TODO(#7844): Remove this hack.
109+
windows_config_setting: ":_magic_sentinel_runtime",
110+
"//conditions:default": ":_autodetecting_py3_runtime_nonstrict",
111+
}),
112+
visibility = ["//visibility:public"],
113+
)
114+
115+
native.toolchain(
116+
name = name,
117+
toolchain = ":_autodetecting_py_runtime_pair",
118+
toolchain_type = ":toolchain_type",
119+
visibility = ["//visibility:public"],
120+
)
121+
122+
native.toolchain(
123+
name = name + "_nonstrict",
124+
toolchain = ":_autodetecting_py_runtime_pair_nonstrict",
125+
toolchain_type = ":toolchain_type",
126+
visibility = ["//visibility:public"],
127+
)

python/private/py_runtime_pair.bzl

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Copyright 2019 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Implementation of py_runtime_pair."""
16+
17+
# TODO: move py_runtime_pair into rules_python (and the rest of @bazel_tools//python)
18+
# py_runtime should be loaded from rules_python, but this creates a circular dep, because py_runtime_pair is imported there.
19+
py_runtime = native.py_runtime
20+
21+
def _py_runtime_pair_impl(ctx):
22+
if ctx.attr.py2_runtime != None:
23+
py2_runtime = ctx.attr.py2_runtime[PyRuntimeInfo]
24+
if py2_runtime.python_version != "PY2":
25+
fail("The Python runtime in the 'py2_runtime' attribute did not have " +
26+
"version 'PY2'")
27+
else:
28+
py2_runtime = None
29+
30+
if ctx.attr.py3_runtime != None:
31+
py3_runtime = ctx.attr.py3_runtime[PyRuntimeInfo]
32+
if py3_runtime.python_version != "PY3":
33+
fail("The Python runtime in the 'py3_runtime' attribute did not have " +
34+
"version 'PY3'")
35+
else:
36+
py3_runtime = None
37+
38+
# TODO: Uncomment this after --incompatible_python_disable_py2 defaults to true
39+
# if _is_py2_disabled(ctx) and py2_runtime != None:
40+
# fail("Using Python 2 is not supported and disabled; see " +
41+
# "https://github.com/bazelbuild/bazel/issues/15684")
42+
43+
return [platform_common.ToolchainInfo(
44+
py2_runtime = py2_runtime,
45+
py3_runtime = py3_runtime,
46+
)]
47+
48+
# buildifier: disable=unused-variable
49+
def _is_py2_disabled(ctx):
50+
# In Google, this file isn't bundled with Bazel, so we have to conditionally
51+
# check for this flag.
52+
# TODO: Remove this once a build with the flag is released in Google
53+
if not hasattr(ctx.fragments.py, "disable_py"):
54+
return False
55+
return ctx.fragments.py.disable_py2
56+
57+
py_runtime_pair = rule(
58+
implementation = _py_runtime_pair_impl,
59+
attrs = {
60+
# The two runtimes are used by the py_binary at runtime, and so need to
61+
# be built for the target platform.
62+
"py2_runtime": attr.label(
63+
providers = [PyRuntimeInfo],
64+
cfg = "target",
65+
doc = """\
66+
The runtime to use for Python 2 targets. Must have `python_version` set to
67+
`PY2`.
68+
""",
69+
),
70+
"py3_runtime": attr.label(
71+
providers = [PyRuntimeInfo],
72+
cfg = "target",
73+
doc = """\
74+
The runtime to use for Python 3 targets. Must have `python_version` set to
75+
`PY3`.
76+
""",
77+
),
78+
},
79+
fragments = ["py"],
80+
doc = """\
81+
A toolchain rule for Python.
82+
83+
This wraps up to two Python runtimes, one for Python 2 and one for Python 3.
84+
The rule consuming this toolchain will choose which runtime is appropriate.
85+
Either runtime may be omitted, in which case the resulting toolchain will be
86+
unusable for building Python code using that version.
87+
88+
Usually the wrapped runtimes are declared using the `py_runtime` rule, but any
89+
rule returning a `PyRuntimeInfo` provider may be used.
90+
91+
This rule returns a `platform_common.ToolchainInfo` provider with the following
92+
schema:
93+
94+
```python
95+
platform_common.ToolchainInfo(
96+
py2_runtime = <PyRuntimeInfo or None>,
97+
py3_runtime = <PyRuntimeInfo or None>,
98+
)
99+
```
100+
101+
Example usage:
102+
103+
```python
104+
# In your BUILD file...
105+
106+
load("@rules_python//python:defs.bzl", "py_runtime_pair")
107+
108+
py_runtime(
109+
name = "my_py2_runtime",
110+
interpreter_path = "/system/python2",
111+
python_version = "PY2",
112+
)
113+
114+
py_runtime(
115+
name = "my_py3_runtime",
116+
interpreter_path = "/system/python3",
117+
python_version = "PY3",
118+
)
119+
120+
py_runtime_pair(
121+
name = "my_py_runtime_pair",
122+
py2_runtime = ":my_py2_runtime",
123+
py3_runtime = ":my_py3_runtime",
124+
)
125+
126+
toolchain(
127+
name = "my_toolchain",
128+
target_compatible_with = <...>,
129+
toolchain = ":my_py_runtime_pair",
130+
toolchain_type = "@rules_python//python:toolchain_type",
131+
)
132+
```
133+
134+
```python
135+
# In your WORKSPACE...
136+
137+
register_toolchains("//my_pkg:my_toolchain")
138+
```
139+
""",
140+
)

0 commit comments

Comments
 (0)