Skip to content

Commit b8f1645

Browse files
authored
feat: Expose Python C headers through the toolchain. (#1287)
This allows getting a build's `cc_library` of Python headers through toolchain resolution instead of having to use the underlying toolchain's repository `:python_headers` target directly. Without this feature, it's not possible to reliably and correctly get the C information about the runtime a build is going to use. Existing solutions require carefully setting up repo names, external state, and/or using specific build rules. In comparison, with this feature, consumers are able to simply ask for the current headers via a helper target or manually lookup the toolchain and pull the relevant information; toolchain resolution handles finding the correct headers. The basic way this works is by registering a second toolchain to carry C/C++ related information; as such, it is named `py_cc_toolchain`. The py cc toolchain has the same constraint settings as the regular py toolchain; an expected invariant is that there is a 1:1 correspondence between the two. This base functionality allows a consuming rule implementation to use toolchain resolution to find the Python C toolchain information. Usually what downstream consumers need are the headers to feed into another `cc_library` (or equivalent) target, so, rather than have every project re-implement the same "lookup and forward cc_library info" logic, this is provided by the `//python/cc:current_py_cc_headers` target. Targets that need the headers can then depend on that target as if it was a `cc_library` target. Work towards #824
1 parent 4082693 commit b8f1645

30 files changed

+974
-74
lines changed

.bazelci/presubmit.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,19 @@ tasks:
113113
<<: *reusable_config
114114
name: Test on RBE using minimum supported Bazel version
115115
platform: rbe_ubuntu1604
116+
build_flags:
117+
# BazelCI sets --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1,
118+
# which prevents cc toolchain autodetection from working correctly
119+
# on Bazel 5.4 and earlier. To workaround this, manually specify the
120+
# build kite cc toolchain.
121+
- "--extra_toolchains=@buildkite_config//config:cc-toolchain"
116122
test_flags:
117123
- "--test_tag_filters=-integration-test,-acceptance-test"
124+
# BazelCI sets --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1,
125+
# which prevents cc toolchain autodetection from working correctly
126+
# on Bazel 5.4 and earlier. To workaround this, manually specify the
127+
# build kite cc toolchain.
128+
- "--extra_toolchains=@buildkite_config//config:cc-toolchain"
118129
rbe:
119130
<<: *reusable_config
120131
name: Test on RBE

.bazelrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
# This lets us glob() up all the files inside the examples to make them inputs to tests
44
# (Note, we cannot use `common --deleted_packages` because the bazel version command doesn't support it)
55
# To update these lines, run tools/bazel_integration_test/update_deleted_packages.sh
6-
build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_point,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,tests/compile_pip_requirements,tests/compile_pip_requirements_test_from_external_workspace,tests/ignore_root_user_error,tests/pip_repository_entry_points
7-
query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_point,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,tests/compile_pip_requirements,tests/compile_pip_requirements_test_from_external_workspace,tests/ignore_root_user_error,tests/pip_repository_entry_points
6+
build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_point,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,tests/compile_pip_requirements,tests/compile_pip_requirements_test_from_external_workspace,tests/ignore_root_user_error,tests/pip_repository_entry_points
7+
query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_point,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,tests/compile_pip_requirements,tests/compile_pip_requirements_test_from_external_workspace,tests/ignore_root_user_error,tests/pip_repository_entry_points
88

99
test --test_output=errors
1010

docs/BUILD.bazel

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ _DOCS = {
2525
"packaging": "//docs:packaging-docs",
2626
"pip": "//docs:pip-docs",
2727
"pip_repository": "//docs:pip-repository",
28+
"py_cc_toolchain": "//docs:py_cc_toolchain-docs",
29+
"py_cc_toolchain_info": "//docs:py_cc_toolchain_info-docs",
2830
"python": "//docs:core-docs",
2931
}
3032

@@ -134,6 +136,25 @@ stardoc(
134136
deps = [":packaging_bzl"],
135137
)
136138

139+
stardoc(
140+
name = "py_cc_toolchain-docs",
141+
out = "py_cc_toolchain.md_",
142+
# NOTE: The public file isn't used as the input because it would document
143+
# the macro, which doesn't have the attribute documentation. The macro
144+
# doesn't do anything interesting to users, so bypass it to avoid having to
145+
# copy/paste all the rule's doc in the macro.
146+
input = "//python/private:py_cc_toolchain_rule.bzl",
147+
target_compatible_with = _NOT_WINDOWS,
148+
deps = ["//python/private:py_cc_toolchain_bzl"],
149+
)
150+
151+
stardoc(
152+
name = "py_cc_toolchain_info-docs",
153+
out = "py_cc_toolchain_info.md_",
154+
input = "//python/cc:py_cc_toolchain_info.bzl",
155+
deps = ["//python/cc:py_cc_toolchain_info_bzl"],
156+
)
157+
137158
[
138159
diff_test(
139160
name = "check_" + k,

docs/py_cc_toolchain.md

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

docs/py_cc_toolchain_info.md

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

python/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ licenses(["notice"])
3333
filegroup(
3434
name = "distribution",
3535
srcs = glob(["**"]) + [
36+
"//python/cc:distribution",
3637
"//python/config_settings:distribution",
3738
"//python/constraints:distribution",
3839
"//python/private:distribution",

python/cc/BUILD.bazel

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Package for C/C++ specific functionality of the Python rules.
2+
3+
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
4+
load("//python/private:current_py_cc_headers.bzl", "current_py_cc_headers")
5+
load("//python/private:util.bzl", "BZLMOD_ENABLED")
6+
7+
package(
8+
default_visibility = ["//:__subpackages__"],
9+
)
10+
11+
# This target provides the C headers for whatever the current toolchain is
12+
# for the consuming rule. It basically acts like a cc_library by forwarding
13+
# on the providers for the underlying cc_library that the toolchain is using.
14+
current_py_cc_headers(
15+
name = "current_py_cc_headers",
16+
# Building this directly will fail unless a py cc toolchain is registered,
17+
# and it's only under bzlmod that one is registered by default.
18+
tags = [] if BZLMOD_ENABLED else ["manual"],
19+
visibility = ["//visibility:public"],
20+
)
21+
22+
toolchain_type(
23+
name = "toolchain_type",
24+
visibility = ["//visibility:public"],
25+
)
26+
27+
bzl_library(
28+
name = "py_cc_toolchain_bzl",
29+
srcs = ["py_cc_toolchain.bzl"],
30+
visibility = ["//visibility:public"],
31+
deps = ["//python/private:py_cc_toolchain_bzl"],
32+
)
33+
34+
bzl_library(
35+
name = "py_cc_toolchain_info_bzl",
36+
srcs = ["py_cc_toolchain_info.bzl"],
37+
visibility = ["//visibility:public"],
38+
deps = ["//python/private:py_cc_toolchain_info_bzl"],
39+
)
40+
41+
filegroup(
42+
name = "distribution",
43+
srcs = glob(["**"]),
44+
)

python/cc/py_cc_toolchain.bzl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright 2023 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+
"""Public entry point for py_cc_toolchain rule."""
16+
17+
load("//python/private:py_cc_toolchain_macro.bzl", _py_cc_toolchain = "py_cc_toolchain")
18+
19+
py_cc_toolchain = _py_cc_toolchain

python/cc/py_cc_toolchain_info.bzl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2023 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+
"""Provider for C/C++ information about the Python runtime.
16+
17+
NOTE: This is a beta-quality feature. APIs subject to change until
18+
https://github.com/bazelbuild/rules_python/issues/824 is considered done.
19+
"""
20+
21+
load("//python/private:py_cc_toolchain_info.bzl", _PyCcToolchainInfo = "PyCcToolchainInfo")
22+
23+
PyCcToolchainInfo = _PyCcToolchainInfo

python/private/BUILD.bazel

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,28 @@ bzl_library(
5151
deps = ["@bazel_skylib//lib:types"],
5252
)
5353

54+
bzl_library(
55+
name = "py_cc_toolchain_bzl",
56+
srcs = [
57+
"py_cc_toolchain_macro.bzl",
58+
"py_cc_toolchain_rule.bzl",
59+
],
60+
visibility = [
61+
"//docs:__subpackages__",
62+
"//python/cc:__pkg__",
63+
],
64+
deps = [
65+
":py_cc_toolchain_info_bzl",
66+
":util_bzl",
67+
],
68+
)
69+
70+
bzl_library(
71+
name = "py_cc_toolchain_info_bzl",
72+
srcs = ["py_cc_toolchain_info.bzl"],
73+
visibility = ["//python/cc:__pkg__"],
74+
)
75+
5476
# @bazel_tools can't define bzl_library itself, so we just put a wrapper around it.
5577
bzl_library(
5678
name = "bazel_tools_bzl",
@@ -73,6 +95,7 @@ exports_files(
7395
"reexports.bzl",
7496
"stamp.bzl",
7597
"util.bzl",
98+
"py_cc_toolchain_rule.bzl",
7699
],
77100
visibility = ["//docs:__pkg__"],
78101
)

0 commit comments

Comments
 (0)