Skip to content

Commit 6afb5a6

Browse files
committed
Merge branch 'main' into chore/remove-default-values-for-python-version
2 parents 60dae67 + 63114a3 commit 6afb5a6

File tree

11 files changed

+445
-156
lines changed

11 files changed

+445
-156
lines changed

docs/api/rules_python/python/cc/index.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,10 @@ This target provides:
2525

2626
* `CcInfo`: The C++ information about the Python libraries.
2727
:::
28+
29+
:::{bzl:target} toolchain_type
30+
31+
Toolchain type identifier for the Python C toolchain.
32+
33+
This toolchain type is typically implemented by {obj}`py_cc_toolchain`.
34+
:::

docs/api/rules_python/python/index.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88
:::{bzl:target} toolchain_type
99

1010
Identifier for the toolchain type for the target platform.
11+
12+
This toolchain type gives information about the runtime for the target platform.
13+
It is typically implemented by the {obj}`py_runtime` rule
14+
15+
::::{seealso}
16+
{any}`Custom Toolchains` for how to define custom toolchains
17+
::::
18+
1119
:::
1220

1321
:::{bzl:target} exec_tools_toolchain_type

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ by buildifier.
5757
self
5858
getting-started
5959
pypi-dependencies
60-
toolchains
60+
Toolchains <toolchains>
6161
pip
6262
coverage
6363
precompiling

docs/toolchains.md

Lines changed: 197 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,13 @@ Remember to call `use_repo()` to make repos visible to your module:
161161

162162
#### Toolchain usage in other rules
163163

164-
Python toolchains can be utilized in other bazel rules, such as `genrule()`, by adding the `toolchains=["@rules_python//python:current_py_toolchain"]` attribute. You can obtain the path to the Python interpreter using the `$(PYTHON2)` and `$(PYTHON3)` ["Make" Variables](https://bazel.build/reference/be/make-variables). See the
165-
{gh-path}`test_current_py_toolchain <tests/load_from_macro/BUILD.bazel>` target for an example.
166-
164+
Python toolchains can be utilized in other bazel rules, such as `genrule()`, by
165+
adding the `toolchains=["@rules_python//python:current_py_toolchain"]`
166+
attribute. You can obtain the path to the Python interpreter using the
167+
`$(PYTHON2)` and `$(PYTHON3)` ["Make"
168+
Variables](https://bazel.build/reference/be/make-variables). See the
169+
{gh-path}`test_current_py_toolchain <tests/load_from_macro/BUILD.bazel>` target
170+
for an example.
167171

168172
## Workspace configuration
169173

@@ -242,3 +246,193 @@ there is a toolchain misconfiguration somewhere.
242246
To aid migration off the Bazel-builtin toolchain, rules_python provides
243247
{obj}`@rules_python//python/runtime_env_toolchains:all`. This is an equivalent
244248
toolchain, but is implemented using rules_python's objects.
249+
250+
251+
## Custom toolchains
252+
253+
While rules_python provides toolchains by default, it is not required to use
254+
them, and you can define your own toolchains to use instead. This section
255+
gives an introduction for how to define them yourself.
256+
257+
:::{note}
258+
* Defining your own toolchains is an advanced feature.
259+
* APIs used for defining them are less stable and may change more often.
260+
:::
261+
262+
Under the hood, there are multiple toolchains that comprise the different
263+
information necessary to build Python targets. Each one has an
264+
associated _toolchain type_ that identifies it. We call the collection of these
265+
toolchains a "toolchain suite".
266+
267+
One of the underlying design goals of the toolchains is to support complex and
268+
bespoke environments. Such environments may use an arbitrary combination of
269+
{obj}`RBE`, cross-platform building, multiple Python versions,
270+
building Python from source, embeding Python (as opposed to building separate
271+
interpreters), using prebuilt binaries, or using binaries built from source. To
272+
that end, many of the attributes they accept, and fields they provide, are
273+
optional.
274+
275+
### Target toolchain type
276+
277+
The target toolchain type is {obj}`//python:toolchain_type`, and it
278+
is for _target configuration_ runtime information, e.g., the Python version
279+
and interpreter binary that a program will use.
280+
281+
The is typically implemented using {obj}`py_runtime()`, which
282+
provides the {obj}`PyRuntimeInfo` provider. For historical reasons from the
283+
Python 2 transition, `py_runtime` is wrapped in {obj}`py_runtime_pair`,
284+
which provides {obj}`ToolchainInfo` with the field `py3_runtime`, which is an
285+
instance of `PyRuntimeInfo`.
286+
287+
This toolchain type is intended to hold only _target configuration_ values. As
288+
such, when defining its associated {external:bzl:obj}`toolchain` target, only
289+
set {external:bzl:obj}`toolchain.target_compatible_with` and/or
290+
{external:bzl:obj}`toolchain.target_settings` constraints; there is no need to
291+
set {external:bzl:obj}`toolchain.exec_compatible_with`.
292+
293+
### Python C toolchain type
294+
295+
The Python C toolchain type ("py cc") is {obj}`//python/cc:toolchain_type`, and
296+
it has C/C++ information for the _target configuration_, e.g. the C headers that
297+
provide `Python.h`.
298+
299+
This is typically implemented using {obj}`py_cc_toolchain()`, which provides
300+
{obj}`ToolchainInfo` with the field `py_cc_toolchain` set, which is a
301+
{obj}`PyCcToolchainInfo` provider instance.
302+
303+
This toolchain type is intended to hold only _target configuration_ values
304+
relating to the C/C++ information for the Python runtime. As such, when defining
305+
its associated {external:obj}`toolchain` target, only set
306+
{external:bzl:obj}`toolchain.target_compatible_with` and/or
307+
{external:bzl:obj}`toolchain.target_settings` constraints; there is no need to
308+
set {external:bzl:obj}`toolchain.exec_compatible_with`.
309+
310+
### Exec tools toolchain type
311+
312+
The exec tools toolchain type is {obj}`//python:exec_tools_toolchain_type`,
313+
and it is for supporting tools for _building_ programs, e.g. the binary to
314+
precompile code at build time.
315+
316+
This toolchain type is intended to hold only _exec configuration_ values --
317+
usually tools (prebuilt or from-source) used to build Python targets.
318+
319+
This is typically implemented using {obj}`py_exec_tools_toolchain`, which
320+
provides {obj}`ToolchainInfo` with the field `exec_tools` set, which is an
321+
instance of {obj}`PyExecToolsInfo`.
322+
323+
The toolchain constraints of this toolchain type can be a bit more nuanced than
324+
the other toolchain types. Typically, you set
325+
{external:bzl:obj}`toolchain.target_settings` to the Python version the tools
326+
are for, and {external:bzl:obj}`toolchain.exec_compatible_with` to the platform
327+
they can run on. This allows the toolchain to first be considered based on the
328+
target configuration (e.g. Python version), then for one to be chosen based on
329+
finding one compatible with the available host platforms to run the tool on.
330+
331+
However, what `target_compatible_with`/`target_settings` and
332+
`exec_compatible_with` values to use depend on details of the tools being used.
333+
For example:
334+
* If you had a precompiler that supported any version of Python, then
335+
putting the Python version in `target_settings` is unnecessary.
336+
* If you had a prebuilt polyglot precompiler binary that could run on any
337+
platform, then setting `exec_compatible_with` is unnecessary.
338+
339+
This can work because, when the rules invoke these build tools, they pass along
340+
all necessary information so that the tool can be entirely independent of the
341+
target configuration being built for.
342+
343+
Alternatively, if you had a precompiler that only ran on linux, and only
344+
produced valid output for programs intended to run on linux, then _both_
345+
`exec_compatible_with` and `target_compatible_with` must be set to linux.
346+
347+
### Custom toolchain example
348+
349+
Here, we show an example for a semi-complicated toolchain suite, one that is:
350+
351+
* A CPython-based interpreter
352+
* For Python version 3.12.0
353+
* Using an in-build interpreter built from source
354+
* That only runs on Linux
355+
* Using a prebuilt precompiler that only runs on Linux, and only produces byte
356+
code valid for 3.12
357+
* With the exec tools interpreter disabled (unnecessary with a prebuild
358+
precompiler)
359+
* Providing C headers and libraries
360+
361+
Defining toolchains for this might look something like this:
362+
363+
```
364+
# File: toolchain_impls/BUILD
365+
load("@rules_python//python:py_cc_toolchain.bzl", "py_cc_toolchain")
366+
load("@rules_python//python:py_exec_tools_toolchain.bzl", "py_exec_tools_toolchain")
367+
load("@rules_python//python:py_runtime.bzl", "py_runtime")
368+
load("@rules_python//python:py_runtime_pair.bzl", "py_runtime_pair")
369+
370+
MAJOR = 3
371+
MINOR = 12
372+
MICRO = 0
373+
374+
py_runtime(
375+
name = "runtime",
376+
interpreter = ":python",
377+
interpreter_version_info = {
378+
"major": str(MAJOR),
379+
"minor": str(MINOR),
380+
"micro": str(MICRO),
381+
}
382+
implementation = "cpython"
383+
)
384+
py_runtime_pair(
385+
name = "runtime_pair",
386+
py3_runtime = ":runtime"
387+
)
388+
389+
py_cc_toolchain(
390+
name = "py_cc_toolchain_impl",
391+
headers = ":headers",
392+
libs = ":libs",
393+
python_version = "{}.{}".format(MAJOR, MINOR)
394+
)
395+
396+
py_exec_tools_toolchain(
397+
name = "exec_tools_toolchain_impl",
398+
exec_interpreter = "@rules_python/python:null_target",
399+
precompiler = "precompiler-cpython-3.12"
400+
)
401+
402+
cc_binary(name = "python3.12", ...)
403+
cc_library(name = "headers", ...)
404+
cc_library(name = "libs", ...)
405+
406+
# File: toolchains/BUILD
407+
# Putting toolchain() calls in a separate package from the toolchain
408+
# implementations minimizes Bazel loading overhead
409+
410+
toolchain(
411+
name = "runtime_toolchain",
412+
toolchain = "//toolchain_impl:runtime_pair",
413+
toolchain_type = "@rules_python//python:toolchain_type",
414+
target_compatible_with = ["@platforms/os:linux"]
415+
)
416+
toolchain(
417+
name = "py_cc_toolchain",
418+
toolchain = "//toolchain_impl:py_cc_toolchain_impl",
419+
toolchain_type = "@rules_python//python/cc:toolchain_type",
420+
target_compatible_with = ["@platforms/os:linux"]
421+
)
422+
423+
toolchain(
424+
name = "exec_tools_toolchain",
425+
toolchain = "//toolchain_impl:exec_tools_toolchain_impl",
426+
toolchain_type = "@rules_python//python:exec_tools_toolchain_type",
427+
target_settings = [
428+
"@rules_python//python/config_settings:is_python_3.12",
429+
],
430+
exec_comaptible_with = ["@platforms/os:linux"]
431+
)
432+
```
433+
434+
:::{note}
435+
The toolchain() calls should be in a separate BUILD file from everything else.
436+
This avoids Bazel having to perform unnecessary work when it discovers the list
437+
of available toolchains.
438+
:::

examples/bzlmod/MODULE.bazel.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)