aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Levasseur <rlevasseur@google.com>2023-07-08 09:58:12 -0700
committerGitHub <noreply@github.com>2023-07-08 16:58:12 +0000
commitb8f16458c1d785a24921e569cc6174e8e3f6b45e (patch)
tree16bde246b8e6e39a11619c4ad6bc6355f45506f4
parent4082693e23ec9615f3e9b2ed9fae542e2b3bed12 (diff)
downloadbazelbuild-rules_python-b8f16458c1d785a24921e569cc6174e8e3f6b45e.tar.gz
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 https://github.com/bazelbuild/rules_python/issues/824
-rw-r--r--.bazelci/presubmit.yml11
-rw-r--r--.bazelrc4
-rw-r--r--docs/BUILD.bazel21
-rw-r--r--docs/py_cc_toolchain.md32
-rw-r--r--docs/py_cc_toolchain_info.md27
-rw-r--r--python/BUILD.bazel1
-rw-r--r--python/cc/BUILD.bazel44
-rw-r--r--python/cc/py_cc_toolchain.bzl19
-rw-r--r--python/cc/py_cc_toolchain_info.bzl23
-rw-r--r--python/private/BUILD.bazel23
-rw-r--r--python/private/current_py_cc_headers.bzl41
-rw-r--r--python/private/py_cc_toolchain_info.bzl43
-rw-r--r--python/private/py_cc_toolchain_macro.bzl31
-rw-r--r--python/private/py_cc_toolchain_rule.bzl57
-rw-r--r--python/private/toolchains_repo.bzl9
-rw-r--r--python/private/util.bzl37
-rw-r--r--python/repositories.bzl7
-rw-r--r--tests/BUILD.bazel2
-rw-r--r--tests/cc/BUILD.bazel108
-rw-r--r--tests/cc/current_py_cc_headers/BUILD.bazel17
-rw-r--r--tests/cc/current_py_cc_headers/current_py_cc_headers_tests.bzl69
-rw-r--r--tests/cc/fake_cc_toolchain_config.bzl (renamed from tools/build_defs/python/tests/fake_cc_toolchain_config.bzl)0
-rw-r--r--tests/cc/py_cc_toolchain/BUILD.bazel3
-rw-r--r--tests/cc/py_cc_toolchain/py_cc_toolchain_tests.bzl85
-rw-r--r--tests/cc_info_subject.bzl128
-rw-r--r--tests/default_info_subject.bzl34
-rw-r--r--tests/py_cc_toolchain_info_subject.bzl52
-rw-r--r--tests/struct_subject.bzl50
-rw-r--r--tools/build_defs/python/tests/BUILD.bazel66
-rw-r--r--tools/build_defs/python/tests/py_test/py_test_tests.bzl4
30 files changed, 974 insertions, 74 deletions
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index ac24113..1da4d9f 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -113,8 +113,19 @@ tasks:
<<: *reusable_config
name: Test on RBE using minimum supported Bazel version
platform: rbe_ubuntu1604
+ build_flags:
+ # BazelCI sets --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1,
+ # which prevents cc toolchain autodetection from working correctly
+ # on Bazel 5.4 and earlier. To workaround this, manually specify the
+ # build kite cc toolchain.
+ - "--extra_toolchains=@buildkite_config//config:cc-toolchain"
test_flags:
- "--test_tag_filters=-integration-test,-acceptance-test"
+ # BazelCI sets --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1,
+ # which prevents cc toolchain autodetection from working correctly
+ # on Bazel 5.4 and earlier. To workaround this, manually specify the
+ # build kite cc toolchain.
+ - "--extra_toolchains=@buildkite_config//config:cc-toolchain"
rbe:
<<: *reusable_config
name: Test on RBE
diff --git a/.bazelrc b/.bazelrc
index d5b0566..3611999 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -3,8 +3,8 @@
# This lets us glob() up all the files inside the examples to make them inputs to tests
# (Note, we cannot use `common --deleted_packages` because the bazel version command doesn't support it)
# To update these lines, run tools/bazel_integration_test/update_deleted_packages.sh
-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
-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
+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
+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
test --test_output=errors
diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel
index 938ba85..1fb4f81 100644
--- a/docs/BUILD.bazel
+++ b/docs/BUILD.bazel
@@ -25,6 +25,8 @@ _DOCS = {
"packaging": "//docs:packaging-docs",
"pip": "//docs:pip-docs",
"pip_repository": "//docs:pip-repository",
+ "py_cc_toolchain": "//docs:py_cc_toolchain-docs",
+ "py_cc_toolchain_info": "//docs:py_cc_toolchain_info-docs",
"python": "//docs:core-docs",
}
@@ -134,6 +136,25 @@ stardoc(
deps = [":packaging_bzl"],
)
+stardoc(
+ name = "py_cc_toolchain-docs",
+ out = "py_cc_toolchain.md_",
+ # NOTE: The public file isn't used as the input because it would document
+ # the macro, which doesn't have the attribute documentation. The macro
+ # doesn't do anything interesting to users, so bypass it to avoid having to
+ # copy/paste all the rule's doc in the macro.
+ input = "//python/private:py_cc_toolchain_rule.bzl",
+ target_compatible_with = _NOT_WINDOWS,
+ deps = ["//python/private:py_cc_toolchain_bzl"],
+)
+
+stardoc(
+ name = "py_cc_toolchain_info-docs",
+ out = "py_cc_toolchain_info.md_",
+ input = "//python/cc:py_cc_toolchain_info.bzl",
+ deps = ["//python/cc:py_cc_toolchain_info_bzl"],
+)
+
[
diff_test(
name = "check_" + k,
diff --git a/docs/py_cc_toolchain.md b/docs/py_cc_toolchain.md
new file mode 100644
index 0000000..3a59ea9
--- /dev/null
+++ b/docs/py_cc_toolchain.md
@@ -0,0 +1,32 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+Implementation of py_cc_toolchain rule.
+
+NOTE: This is a beta-quality feature. APIs subject to change until
+https://github.com/bazelbuild/rules_python/issues/824 is considered done.
+
+
+<a id="py_cc_toolchain"></a>
+
+## py_cc_toolchain
+
+<pre>
+py_cc_toolchain(<a href="#py_cc_toolchain-name">name</a>, <a href="#py_cc_toolchain-headers">headers</a>, <a href="#py_cc_toolchain-python_version">python_version</a>)
+</pre>
+
+A toolchain for a Python runtime's C/C++ information (e.g. headers)
+
+This rule carries information about the C/C++ side of a Python runtime, e.g.
+headers, shared libraries, etc.
+
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="py_cc_toolchain-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="py_cc_toolchain-headers"></a>headers | Target that provides the Python headers. Typically this is a cc_library target. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
+| <a id="py_cc_toolchain-python_version"></a>python_version | The Major.minor Python version, e.g. 3.11 | String | required | |
+
+
diff --git a/docs/py_cc_toolchain_info.md b/docs/py_cc_toolchain_info.md
new file mode 100644
index 0000000..4e59a78
--- /dev/null
+++ b/docs/py_cc_toolchain_info.md
@@ -0,0 +1,27 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+Provider for C/C++ information about the Python runtime.
+
+NOTE: This is a beta-quality feature. APIs subject to change until
+https://github.com/bazelbuild/rules_python/issues/824 is considered done.
+
+
+<a id="PyCcToolchainInfo"></a>
+
+## PyCcToolchainInfo
+
+<pre>
+PyCcToolchainInfo(<a href="#PyCcToolchainInfo-headers">headers</a>, <a href="#PyCcToolchainInfo-python_version">python_version</a>)
+</pre>
+
+C/C++ information about the Python runtime.
+
+**FIELDS**
+
+
+| Name | Description |
+| :------------- | :------------- |
+| <a id="PyCcToolchainInfo-headers"></a>headers | (struct) Information about the header files, with fields: * providers_map: a dict of string to provider instances. The key should be a fully qualified name (e.g. <code>@rules_foo//bar:baz.bzl#MyInfo</code>) of the provider to uniquely identify its type.<br><br> The following keys are always present: * CcInfo: the CcInfo provider instance for the headers. * DefaultInfo: the DefaultInfo provider instance for the headers.<br><br> A map is used to allow additional providers from the originating headers target (typically a <code>cc_library</code>) to be propagated to consumers (directly exposing a Target object can cause memory issues and is an anti-pattern).<br><br> When consuming this map, it's suggested to use <code>providers_map.values()</code> to return all providers; or copy the map and filter out or replace keys as appropriate. Note that any keys begining with <code>_</code> (underscore) are considered private and should be forward along as-is (this better allows e.g. <code>:current_py_cc_headers</code> to act as the underlying headers target it represents). |
+| <a id="PyCcToolchainInfo-python_version"></a>python_version | (str) The Python Major.Minor version. |
+
+
diff --git a/python/BUILD.bazel b/python/BUILD.bazel
index d75889d..c5f2580 100644
--- a/python/BUILD.bazel
+++ b/python/BUILD.bazel
@@ -33,6 +33,7 @@ licenses(["notice"])
filegroup(
name = "distribution",
srcs = glob(["**"]) + [
+ "//python/cc:distribution",
"//python/config_settings:distribution",
"//python/constraints:distribution",
"//python/private:distribution",
diff --git a/python/cc/BUILD.bazel b/python/cc/BUILD.bazel
new file mode 100644
index 0000000..d4a6bb8
--- /dev/null
+++ b/python/cc/BUILD.bazel
@@ -0,0 +1,44 @@
+# Package for C/C++ specific functionality of the Python rules.
+
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+load("//python/private:current_py_cc_headers.bzl", "current_py_cc_headers")
+load("//python/private:util.bzl", "BZLMOD_ENABLED")
+
+package(
+ default_visibility = ["//:__subpackages__"],
+)
+
+# This target provides the C headers for whatever the current toolchain is
+# for the consuming rule. It basically acts like a cc_library by forwarding
+# on the providers for the underlying cc_library that the toolchain is using.
+current_py_cc_headers(
+ name = "current_py_cc_headers",
+ # Building this directly will fail unless a py cc toolchain is registered,
+ # and it's only under bzlmod that one is registered by default.
+ tags = [] if BZLMOD_ENABLED else ["manual"],
+ visibility = ["//visibility:public"],
+)
+
+toolchain_type(
+ name = "toolchain_type",
+ visibility = ["//visibility:public"],
+)
+
+bzl_library(
+ name = "py_cc_toolchain_bzl",
+ srcs = ["py_cc_toolchain.bzl"],
+ visibility = ["//visibility:public"],
+ deps = ["//python/private:py_cc_toolchain_bzl"],
+)
+
+bzl_library(
+ name = "py_cc_toolchain_info_bzl",
+ srcs = ["py_cc_toolchain_info.bzl"],
+ visibility = ["//visibility:public"],
+ deps = ["//python/private:py_cc_toolchain_info_bzl"],
+)
+
+filegroup(
+ name = "distribution",
+ srcs = glob(["**"]),
+)
diff --git a/python/cc/py_cc_toolchain.bzl b/python/cc/py_cc_toolchain.bzl
new file mode 100644
index 0000000..2e782ef
--- /dev/null
+++ b/python/cc/py_cc_toolchain.bzl
@@ -0,0 +1,19 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Public entry point for py_cc_toolchain rule."""
+
+load("//python/private:py_cc_toolchain_macro.bzl", _py_cc_toolchain = "py_cc_toolchain")
+
+py_cc_toolchain = _py_cc_toolchain
diff --git a/python/cc/py_cc_toolchain_info.bzl b/python/cc/py_cc_toolchain_info.bzl
new file mode 100644
index 0000000..9ea394a
--- /dev/null
+++ b/python/cc/py_cc_toolchain_info.bzl
@@ -0,0 +1,23 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Provider for C/C++ information about the Python runtime.
+
+NOTE: This is a beta-quality feature. APIs subject to change until
+https://github.com/bazelbuild/rules_python/issues/824 is considered done.
+"""
+
+load("//python/private:py_cc_toolchain_info.bzl", _PyCcToolchainInfo = "PyCcToolchainInfo")
+
+PyCcToolchainInfo = _PyCcToolchainInfo
diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel
index f454f42..10af17e 100644
--- a/python/private/BUILD.bazel
+++ b/python/private/BUILD.bazel
@@ -51,6 +51,28 @@ bzl_library(
deps = ["@bazel_skylib//lib:types"],
)
+bzl_library(
+ name = "py_cc_toolchain_bzl",
+ srcs = [
+ "py_cc_toolchain_macro.bzl",
+ "py_cc_toolchain_rule.bzl",
+ ],
+ visibility = [
+ "//docs:__subpackages__",
+ "//python/cc:__pkg__",
+ ],
+ deps = [
+ ":py_cc_toolchain_info_bzl",
+ ":util_bzl",
+ ],
+)
+
+bzl_library(
+ name = "py_cc_toolchain_info_bzl",
+ srcs = ["py_cc_toolchain_info.bzl"],
+ visibility = ["//python/cc:__pkg__"],
+)
+
# @bazel_tools can't define bzl_library itself, so we just put a wrapper around it.
bzl_library(
name = "bazel_tools_bzl",
@@ -73,6 +95,7 @@ exports_files(
"reexports.bzl",
"stamp.bzl",
"util.bzl",
+ "py_cc_toolchain_rule.bzl",
],
visibility = ["//docs:__pkg__"],
)
diff --git a/python/private/current_py_cc_headers.bzl b/python/private/current_py_cc_headers.bzl
new file mode 100644
index 0000000..be7f8f8
--- /dev/null
+++ b/python/private/current_py_cc_headers.bzl
@@ -0,0 +1,41 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Implementation of current_py_cc_headers rule."""
+
+def _current_py_cc_headers_impl(ctx):
+ py_cc_toolchain = ctx.toolchains["//python/cc:toolchain_type"].py_cc_toolchain
+ return py_cc_toolchain.headers.providers_map.values()
+
+current_py_cc_headers = rule(
+ implementation = _current_py_cc_headers_impl,
+ toolchains = ["//python/cc:toolchain_type"],
+ provides = [CcInfo],
+ doc = """\
+Provides the currently active Python toolchain's C headers.
+
+This is a wrapper around the underlying `cc_library()` for the
+C headers for the consuming target's currently active Python toolchain.
+
+To use, simply depend on this target where you would have wanted the
+toolchain's underlying `:python_headers` target:
+
+```starlark
+cc_library(
+ name = "foo",
+ deps = ["@rules_python//python/cc:current_py_cc_headers"]
+)
+```
+""",
+)
diff --git a/python/private/py_cc_toolchain_info.bzl b/python/private/py_cc_toolchain_info.bzl
new file mode 100644
index 0000000..e7afc10
--- /dev/null
+++ b/python/private/py_cc_toolchain_info.bzl
@@ -0,0 +1,43 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Implementation of PyCcToolchainInfo."""
+
+PyCcToolchainInfo = provider(
+ doc = "C/C++ information about the Python runtime.",
+ fields = {
+ "headers": """\
+(struct) Information about the header files, with fields:
+ * providers_map: a dict of string to provider instances. The key should be
+ a fully qualified name (e.g. `@rules_foo//bar:baz.bzl#MyInfo`) of the
+ provider to uniquely identify its type.
+
+ The following keys are always present:
+ * CcInfo: the CcInfo provider instance for the headers.
+ * DefaultInfo: the DefaultInfo provider instance for the headers.
+
+ A map is used to allow additional providers from the originating headers
+ target (typically a `cc_library`) to be propagated to consumers (directly
+ exposing a Target object can cause memory issues and is an anti-pattern).
+
+ When consuming this map, it's suggested to use `providers_map.values()` to
+ return all providers; or copy the map and filter out or replace keys as
+ appropriate. Note that any keys begining with `_` (underscore) are
+ considered private and should be forward along as-is (this better allows
+ e.g. `:current_py_cc_headers` to act as the underlying headers target it
+ represents).
+""",
+ "python_version": "(str) The Python Major.Minor version.",
+ },
+)
diff --git a/python/private/py_cc_toolchain_macro.bzl b/python/private/py_cc_toolchain_macro.bzl
new file mode 100644
index 0000000..35276f7
--- /dev/null
+++ b/python/private/py_cc_toolchain_macro.bzl
@@ -0,0 +1,31 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Fronting macro for the py_cc_toolchain rule."""
+
+load(":py_cc_toolchain_rule.bzl", _py_cc_toolchain = "py_cc_toolchain")
+load(":util.bzl", "add_tag")
+
+# A fronting macro is used because macros have user-observable behavior;
+# using one from the onset avoids introducing those changes in the future.
+def py_cc_toolchain(**kwargs):
+ """Creates a py_cc_toolchain target.
+
+ Args:
+ **kwargs: Keyword args to pass onto underlying rule.
+ """
+
+ # This tag is added to easily identify usages through other macros.
+ add_tag(kwargs, "@rules_python//python:py_cc_toolchain")
+ _py_cc_toolchain(**kwargs)
diff --git a/python/private/py_cc_toolchain_rule.bzl b/python/private/py_cc_toolchain_rule.bzl
new file mode 100644
index 0000000..c80f845
--- /dev/null
+++ b/python/private/py_cc_toolchain_rule.bzl
@@ -0,0 +1,57 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Implementation of py_cc_toolchain rule.
+
+NOTE: This is a beta-quality feature. APIs subject to change until
+https://github.com/bazelbuild/rules_python/issues/824 is considered done.
+"""
+
+load(":py_cc_toolchain_info.bzl", "PyCcToolchainInfo")
+
+def _py_cc_toolchain_impl(ctx):
+ py_cc_toolchain = PyCcToolchainInfo(
+ headers = struct(
+ providers_map = {
+ "CcInfo": ctx.attr.headers[CcInfo],
+ "DefaultInfo": ctx.attr.headers[DefaultInfo],
+ },
+ ),
+ python_version = ctx.attr.python_version,
+ )
+ return [platform_common.ToolchainInfo(
+ py_cc_toolchain = py_cc_toolchain,
+ )]
+
+py_cc_toolchain = rule(
+ implementation = _py_cc_toolchain_impl,
+ attrs = {
+ "headers": attr.label(
+ doc = ("Target that provides the Python headers. Typically this " +
+ "is a cc_library target."),
+ providers = [CcInfo],
+ mandatory = True,
+ ),
+ "python_version": attr.string(
+ doc = "The Major.minor Python version, e.g. 3.11",
+ mandatory = True,
+ ),
+ },
+ doc = """\
+A toolchain for a Python runtime's C/C++ information (e.g. headers)
+
+This rule carries information about the C/C++ side of a Python runtime, e.g.
+headers, shared libraries, etc.
+""",
+)
diff --git a/python/private/toolchains_repo.bzl b/python/private/toolchains_repo.bzl
index f47ea8f..5923787 100644
--- a/python/private/toolchains_repo.bzl
+++ b/python/private/toolchains_repo.bzl
@@ -83,6 +83,15 @@ toolchain(
toolchain = "@{user_repository_name}_{platform}//:python_runtimes",
toolchain_type = "@bazel_tools//tools/python:toolchain_type",
)
+
+toolchain(
+ name = "{prefix}{platform}_py_cc_toolchain",
+ target_compatible_with = {compatible_with},
+ target_settings = {target_settings},
+ toolchain = "@{user_repository_name}_{platform}//:py_cc_toolchain",
+ toolchain_type = "@rules_python//python/cc:toolchain_type",
+
+)
""".format(
compatible_with = meta.compatible_with,
platform = platform,
diff --git a/python/private/util.bzl b/python/private/util.bzl
index f0d4373..4c4b8fc 100644
--- a/python/private/util.bzl
+++ b/python/private/util.bzl
@@ -1,7 +1,25 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
"""Functionality shared by multiple pieces of code."""
load("@bazel_skylib//lib:types.bzl", "types")
+# When bzlmod is enabled, canonical repos names have @@ in them, while under
+# workspace builds, there is never a @@ in labels.
+BZLMOD_ENABLED = "@@" in str(Label("//:unused"))
+
def copy_propagating_kwargs(from_kwargs, into_kwargs = None):
"""Copies args that must be compatible between two targets with a dependency relationship.
@@ -46,15 +64,26 @@ def add_migration_tag(attrs):
Returns:
The same `attrs` object, but modified.
"""
+ add_tag(attrs, _MIGRATION_TAG)
+ return attrs
+
+def add_tag(attrs, tag):
+ """Adds `tag` to `attrs["tags"]`.
+
+ Args:
+ attrs: dict of keyword args. It is modified in place.
+ tag: str, the tag to add.
+ """
if "tags" in attrs and attrs["tags"] != None:
tags = attrs["tags"]
# Preserve the input type: this allows a test verifying the underlying
# rule can accept the tuple for the tags argument.
if types.is_tuple(tags):
- attrs["tags"] = tags + (_MIGRATION_TAG,)
+ attrs["tags"] = tags + (tag,)
else:
- attrs["tags"] = tags + [_MIGRATION_TAG]
+ # List concatenation is necessary because the original value
+ # may be a frozen list.
+ attrs["tags"] = tags + [tag]
else:
- attrs["tags"] = [_MIGRATION_TAG]
- return attrs
+ attrs["tags"] = [tag]
diff --git a/python/repositories.bzl b/python/repositories.bzl
index 04de657..38a580e 100644
--- a/python/repositories.bzl
+++ b/python/repositories.bzl
@@ -265,6 +265,7 @@ def _python_repository_impl(rctx):
# Generated by python/repositories.bzl
load("@bazel_tools//tools/python:toolchain.bzl", "py_runtime_pair")
+load("@rules_python//python/cc:py_cc_toolchain.bzl", "py_cc_toolchain")
package(default_visibility = ["//visibility:public"])
@@ -336,6 +337,12 @@ py_runtime_pair(
py2_runtime = None,
py3_runtime = ":py3_runtime",
)
+
+py_cc_toolchain(
+ name = "py_cc_toolchain",
+ headers = ":python_headers",
+ python_version = "{python_version}",
+)
""".format(
glob_exclude = repr(glob_exclude),
glob_include = repr(glob_include),
diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel
index abbe62d..2dd2282 100644
--- a/tests/BUILD.bazel
+++ b/tests/BUILD.bazel
@@ -28,5 +28,7 @@ build_test(
"//python:py_runtime_info_bzl",
"//python:py_runtime_pair_bzl",
"//python:py_test_bzl",
+ "//python/cc:py_cc_toolchain_bzl",
+ "//python/cc:py_cc_toolchain_info_bzl",
],
)
diff --git a/tests/cc/BUILD.bazel b/tests/cc/BUILD.bazel
new file mode 100644
index 0000000..1339557
--- /dev/null
+++ b/tests/cc/BUILD.bazel
@@ -0,0 +1,108 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+load("@rules_cc//cc:defs.bzl", "cc_toolchain", "cc_toolchain_suite")
+load("@rules_testing//lib:util.bzl", "PREVENT_IMPLICIT_BUILDING_TAGS")
+load("//python/cc:py_cc_toolchain.bzl", "py_cc_toolchain")
+load(":fake_cc_toolchain_config.bzl", "fake_cc_toolchain_config")
+
+package(default_visibility = ["//:__subpackages__"])
+
+toolchain(
+ name = "fake_py_cc_toolchain",
+ tags = PREVENT_IMPLICIT_BUILDING_TAGS,
+ toolchain = ":fake_py_cc_toolchain_impl",
+ toolchain_type = "@rules_python//python/cc:toolchain_type",
+)
+
+py_cc_toolchain(
+ name = "fake_py_cc_toolchain_impl",
+ testonly = True,
+ headers = ":fake_headers",
+ python_version = "3.999",
+ tags = PREVENT_IMPLICIT_BUILDING_TAGS,
+)
+
+# buildifier: disable=native-cc
+cc_library(
+ name = "fake_headers",
+ testonly = True,
+ hdrs = ["fake_header.h"],
+ data = ["data.txt"],
+ includes = ["fake_include"],
+ tags = PREVENT_IMPLICIT_BUILDING_TAGS,
+)
+
+cc_toolchain_suite(
+ name = "cc_toolchain_suite",
+ tags = ["manual"],
+ toolchains = {
+ "darwin_x86_64": ":mac_toolchain",
+ "k8": ":linux_toolchain",
+ },
+)
+
+filegroup(name = "empty")
+
+cc_toolchain(
+ name = "mac_toolchain",
+ all_files = ":empty",
+ compiler_files = ":empty",
+ dwp_files = ":empty",
+ linker_files = ":empty",
+ objcopy_files = ":empty",
+ strip_files = ":empty",
+ supports_param_files = 0,
+ toolchain_config = ":mac_toolchain_config",
+ toolchain_identifier = "mac-toolchain",
+)
+
+toolchain(
+ name = "mac_toolchain_definition",
+ target_compatible_with = ["@platforms//os:macos"],
+ toolchain = ":mac_toolchain",
+ toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
+
+fake_cc_toolchain_config(
+ name = "mac_toolchain_config",
+ target_cpu = "darwin_x86_64",
+ toolchain_identifier = "mac-toolchain",
+)
+
+cc_toolchain(
+ name = "linux_toolchain",
+ all_files = ":empty",
+ compiler_files = ":empty",
+ dwp_files = ":empty",
+ linker_files = ":empty",
+ objcopy_files = ":empty",
+ strip_files = ":empty",
+ supports_param_files = 0,
+ toolchain_config = ":linux_toolchain_config",
+ toolchain_identifier = "linux-toolchain",
+)
+
+toolchain(
+ name = "linux_toolchain_definition",
+ target_compatible_with = ["@platforms//os:linux"],
+ toolchain = ":linux_toolchain",
+ toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
+
+fake_cc_toolchain_config(
+ name = "linux_toolchain_config",
+ target_cpu = "k8",
+ toolchain_identifier = "linux-toolchain",
+)
diff --git a/tests/cc/current_py_cc_headers/BUILD.bazel b/tests/cc/current_py_cc_headers/BUILD.bazel
new file mode 100644
index 0000000..e2d6a1b
--- /dev/null
+++ b/tests/cc/current_py_cc_headers/BUILD.bazel
@@ -0,0 +1,17 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+load(":current_py_cc_headers_tests.bzl", "current_py_cc_headers_test_suite")
+
+current_py_cc_headers_test_suite(name = "current_py_cc_headers_tests")
diff --git a/tests/cc/current_py_cc_headers/current_py_cc_headers_tests.bzl b/tests/cc/current_py_cc_headers/current_py_cc_headers_tests.bzl
new file mode 100644
index 0000000..2b8b2ee
--- /dev/null
+++ b/tests/cc/current_py_cc_headers/current_py_cc_headers_tests.bzl
@@ -0,0 +1,69 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for current_py_cc_headers."""
+
+load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
+load("@rules_testing//lib:truth.bzl", "matching")
+load("//tests:cc_info_subject.bzl", "cc_info_subject")
+
+_tests = []
+
+def _test_current_toolchain_headers(name):
+ analysis_test(
+ name = name,
+ impl = _test_current_toolchain_headers_impl,
+ target = "//python/cc:current_py_cc_headers",
+ config_settings = {
+ "//command_line_option:extra_toolchains": [str(Label("//tests/cc:all"))],
+ },
+ attrs = {
+ "header": attr.label(
+ default = "//tests/cc:fake_header.h",
+ allow_single_file = True,
+ ),
+ },
+ )
+
+def _test_current_toolchain_headers_impl(env, target):
+ # Check that the forwarded CcInfo looks vaguely correct.
+ compilation_context = env.expect.that_target(target).provider(
+ CcInfo,
+ factory = cc_info_subject,
+ ).compilation_context()
+ compilation_context.direct_headers().contains_exactly([
+ env.ctx.file.header,
+ ])
+ compilation_context.direct_public_headers().contains_exactly([
+ env.ctx.file.header,
+ ])
+
+ # NOTE: The include dir gets added twice, once for the source path,
+ # and once for the config-specific path.
+ compilation_context.system_includes().contains_at_least_predicates([
+ matching.str_matches("*/fake_include"),
+ ])
+
+ # Check that the forward DefaultInfo looks correct
+ env.expect.that_target(target).runfiles().contains_predicate(
+ matching.str_matches("*/cc/data.txt"),
+ )
+
+_tests.append(_test_current_toolchain_headers)
+
+def current_py_cc_headers_test_suite(name):
+ test_suite(
+ name = name,
+ tests = _tests,
+ )
diff --git a/tools/build_defs/python/tests/fake_cc_toolchain_config.bzl b/tests/cc/fake_cc_toolchain_config.bzl
index b3214a6..b3214a6 100644
--- a/tools/build_defs/python/tests/fake_cc_toolchain_config.bzl
+++ b/tests/cc/fake_cc_toolchain_config.bzl
diff --git a/tests/cc/py_cc_toolchain/BUILD.bazel b/tests/cc/py_cc_toolchain/BUILD.bazel
new file mode 100644
index 0000000..57d030c
--- /dev/null
+++ b/tests/cc/py_cc_toolchain/BUILD.bazel
@@ -0,0 +1,3 @@
+load(":py_cc_toolchain_tests.bzl", "py_cc_toolchain_test_suite")
+
+py_cc_toolchain_test_suite(name = "py_cc_toolchain_tests")
diff --git a/tests/cc/py_cc_toolchain/py_cc_toolchain_tests.bzl b/tests/cc/py_cc_toolchain/py_cc_toolchain_tests.bzl
new file mode 100644
index 0000000..09bd646
--- /dev/null
+++ b/tests/cc/py_cc_toolchain/py_cc_toolchain_tests.bzl
@@ -0,0 +1,85 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for py_cc_toolchain."""
+
+load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
+load("@rules_testing//lib:truth.bzl", "matching")
+load("//tests:cc_info_subject.bzl", "cc_info_subject")
+load("//tests:default_info_subject.bzl", "default_info_subject")
+load("//tests:py_cc_toolchain_info_subject.bzl", "PyCcToolchainInfoSubject")
+
+_tests = []
+
+def _py_cc_toolchain_test(name):
+ analysis_test(
+ name = name,
+ impl = _py_cc_toolchain_test_impl,
+ target = "//tests/cc:fake_py_cc_toolchain_impl",
+ attrs = {
+ "header": attr.label(
+ default = "//tests/cc:fake_header.h",
+ allow_single_file = True,
+ ),
+ },
+ )
+
+def _py_cc_toolchain_test_impl(env, target):
+ env.expect.that_target(target).has_provider(platform_common.ToolchainInfo)
+
+ toolchain = PyCcToolchainInfoSubject.new(
+ target[platform_common.ToolchainInfo].py_cc_toolchain,
+ meta = env.expect.meta.derive(expr = "py_cc_toolchain_info"),
+ )
+ toolchain.python_version().equals("3.999")
+
+ toolchain_headers = toolchain.headers()
+ toolchain_headers.providers_map().keys().contains_exactly(["CcInfo", "DefaultInfo"])
+
+ cc_info = cc_info_subject(
+ # TODO: Use DictSubject.get once available,
+ # https://github.com/bazelbuild/rules_testing/issues/51
+ toolchain_headers.actual.providers_map["CcInfo"],
+ meta = env.expect.meta.derive(expr = "cc_info"),
+ )
+
+ compilation_context = cc_info.compilation_context()
+ compilation_context.direct_headers().contains_exactly([
+ env.ctx.file.header,
+ ])
+ compilation_context.direct_public_headers().contains_exactly([
+ env.ctx.file.header,
+ ])
+
+ # NOTE: The include dir gets added twice, once for the source path,
+ # and once for the config-specific path, but we don't care about that.
+ compilation_context.system_includes().contains_at_least_predicates([
+ matching.str_matches("*/fake_include"),
+ ])
+
+ default_info = default_info_subject(
+ toolchain_headers.actual.providers_map["DefaultInfo"],
+ meta = env.expect.meta.derive(expr = "default_info"),
+ )
+ default_info.runfiles().contains_predicate(
+ matching.str_matches("*/cc/data.txt"),
+ )
+
+_tests.append(_py_cc_toolchain_test)
+
+def py_cc_toolchain_test_suite(name):
+ test_suite(
+ name = name,
+ tests = _tests,
+ )
diff --git a/tests/cc_info_subject.bzl b/tests/cc_info_subject.bzl
new file mode 100644
index 0000000..31ac03a
--- /dev/null
+++ b/tests/cc_info_subject.bzl
@@ -0,0 +1,128 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""CcInfo testing subject."""
+
+load("@rules_testing//lib:truth.bzl", "subjects")
+
+def cc_info_subject(info, *, meta):
+ """Creates a new `CcInfoSubject` for a CcInfo provider instance.
+
+ Args:
+ info: The CcInfo object.
+ meta: ExpectMeta object.
+
+ Returns:
+ A `CcInfoSubject` struct.
+ """
+
+ # buildifier: disable=uninitialized
+ public = struct(
+ # go/keep-sorted start
+ compilation_context = lambda *a, **k: _cc_info_subject_compilation_context(self, *a, **k),
+ # go/keep-sorted end
+ )
+ self = struct(
+ actual = info,
+ meta = meta,
+ )
+ return public
+
+def _cc_info_subject_compilation_context(self):
+ """Returns the CcInfo.compilation_context as a subject.
+
+ Args:
+ self: implicitly added.
+
+ Returns:
+ [`CompilationContext`] instance.
+ """
+ return _compilation_context_subject_new(
+ self.actual.compilation_context,
+ meta = self.meta.derive("compilation_context()"),
+ )
+
+def _compilation_context_subject_new(info, *, meta):
+ """Creates a CompilationContextSubject.
+
+ Args:
+ info: ([`CompilationContext`]) object instance.
+ meta: rules_testing `ExpectMeta` instance.
+
+ Returns:
+ [`CompilationContextSubject`] object.
+ """
+
+ # buildifier: disable=uninitialized
+ public = struct(
+ # go/keep-sorted start
+ direct_headers = lambda *a, **k: _compilation_context_subject_direct_headers(self, *a, **k),
+ direct_public_headers = lambda *a, **k: _compilation_context_subject_direct_public_headers(self, *a, **k),
+ system_includes = lambda *a, **k: _compilation_context_subject_system_includes(self, *a, **k),
+ # go/keep-sorted end
+ )
+ self = struct(
+ actual = info,
+ meta = meta,
+ )
+ return public
+
+def _compilation_context_subject_direct_headers(self):
+ """Returns the direct headers as a subjecct.
+
+ Args:
+ self: implicitly added
+
+ Returns:
+ [`CollectionSubject`] of `File` objects of the direct headers.
+ """
+ return subjects.collection(
+ self.actual.direct_headers,
+ meta = self.meta.derive("direct_headers()"),
+ container_name = "direct_headers",
+ element_plural_name = "header files",
+ )
+
+def _compilation_context_subject_direct_public_headers(self):
+ """Returns the direct public headers as a subjecct.
+
+ Args:
+ self: implicitly added
+
+ Returns:
+ [`CollectionSubject`] of `File` objects of the direct headers.
+ """
+ return subjects.collection(
+ self.actual.direct_public_headers,
+ meta = self.meta.derive("direct_public_headers()"),
+ container_name = "direct_public_headers",
+ element_plural_name = "public header files",
+ )
+
+def _compilation_context_subject_system_includes(self):
+ """Returns the system include directories as a subject.
+
+ NOTE: The system includes are the `cc_library.includes` attribute.
+
+ Args:
+ self: implicitly added
+
+ Returns:
+ [`CollectionSubject`] of [`str`]
+ """
+ return subjects.collection(
+ self.actual.system_includes.to_list(),
+ meta = self.meta.derive("includes()"),
+ container_name = "includes",
+ element_plural_name = "include paths",
+ )
diff --git a/tests/default_info_subject.bzl b/tests/default_info_subject.bzl
new file mode 100644
index 0000000..205dc1e
--- /dev/null
+++ b/tests/default_info_subject.bzl
@@ -0,0 +1,34 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""DefaultInfo testing subject."""
+
+# TODO: Load this through truth.bzl#subjects when made available
+# https://github.com/bazelbuild/rules_testing/issues/54
+load("@rules_testing//lib/private:runfiles_subject.bzl", "RunfilesSubject") # buildifier: disable=bzl-visibility
+
+# TODO: Use rules_testing's DefaultInfoSubject once it's available
+# https://github.com/bazelbuild/rules_testing/issues/52
+def default_info_subject(info, *, meta):
+ # buildifier: disable=uninitialized
+ public = struct(
+ runfiles = lambda *a, **k: _default_info_subject_runfiles(self, *a, **k),
+ )
+ self = struct(actual = info, meta = meta)
+ return public
+
+def _default_info_subject_runfiles(self):
+ return RunfilesSubject.new(
+ self.actual.default_runfiles,
+ meta = self.meta.derive("runfiles()"),
+ )
diff --git a/tests/py_cc_toolchain_info_subject.bzl b/tests/py_cc_toolchain_info_subject.bzl
new file mode 100644
index 0000000..20585e9
--- /dev/null
+++ b/tests/py_cc_toolchain_info_subject.bzl
@@ -0,0 +1,52 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""PyCcToolchainInfo testing subject."""
+
+# TODO: Load this through truth.bzl#subjects when made available
+# https://github.com/bazelbuild/rules_testing/issues/54
+load("@rules_testing//lib/private:dict_subject.bzl", "DictSubject") # buildifier: disable=bzl-visibility
+
+# TODO: Load this through truth.bzl#subjects when made available
+# https://github.com/bazelbuild/rules_testing/issues/54
+load("@rules_testing//lib/private:str_subject.bzl", "StrSubject") # buildifier: disable=bzl-visibility
+load(":struct_subject.bzl", "struct_subject")
+
+def _py_cc_toolchain_info_subject_new(info, *, meta):
+ # buildifier: disable=uninitialized
+ public = struct(
+ headers = lambda *a, **k: _py_cc_toolchain_info_subject_headers(self, *a, **k),
+ python_version = lambda *a, **k: _py_cc_toolchain_info_subject_python_version(self, *a, **k),
+ actual = info,
+ )
+ self = struct(actual = info, meta = meta)
+ return public
+
+def _py_cc_toolchain_info_subject_headers(self):
+ return struct_subject(
+ self.actual.headers,
+ meta = self.meta.derive("headers()"),
+ providers_map = DictSubject.new,
+ )
+
+def _py_cc_toolchain_info_subject_python_version(self):
+ return StrSubject.new(
+ self.actual.python_version,
+ meta = self.meta.derive("python_version()"),
+ )
+
+# Disable this to aid doc generation
+# buildifier: disable=name-conventions
+PyCcToolchainInfoSubject = struct(
+ new = _py_cc_toolchain_info_subject_new,
+)
diff --git a/tests/struct_subject.bzl b/tests/struct_subject.bzl
new file mode 100644
index 0000000..9d18980
--- /dev/null
+++ b/tests/struct_subject.bzl
@@ -0,0 +1,50 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# TODO: Replace this with rules_testing StructSubject
+# https://github.com/bazelbuild/rules_testing/issues/53
+"""Subject for an arbitrary struct."""
+
+def struct_subject(actual, *, meta, **attr_factories):
+ """Creates a struct subject.
+
+ Args:
+ actual: struct, the struct to wrap.
+ meta: rules_testing ExpectMeta object.
+ **attr_factories: dict of attribute names to factory functions. Each
+ attribute must exist on the `actual` value. The factory functions
+ have the signature `def factory(value, *, meta)`, where `value`
+ is the actual attribute value of the struct, and `meta` is
+ a rules_testing ExpectMeta object.
+
+ Returns:
+ StructSubject object.
+ """
+ public_attrs = {}
+ for name, factory in attr_factories.items():
+ if not hasattr(actual, name):
+ fail("Struct missing attribute: '{}'".format(name))
+
+ def attr_accessor(*, __name = name, __factory = factory):
+ return __factory(
+ getattr(actual, __name),
+ meta = meta.derive(__name + "()"),
+ )
+
+ public_attrs[name] = attr_accessor
+ public = struct(
+ actual = actual,
+ **public_attrs
+ )
+ return public
diff --git a/tools/build_defs/python/tests/BUILD.bazel b/tools/build_defs/python/tests/BUILD.bazel
index b5694e2..e271850 100644
--- a/tools/build_defs/python/tests/BUILD.bazel
+++ b/tools/build_defs/python/tests/BUILD.bazel
@@ -12,9 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-load("@rules_cc//cc:defs.bzl", "cc_toolchain", "cc_toolchain_suite")
-load(":fake_cc_toolchain_config.bzl", "fake_cc_toolchain_config")
-
platform(
name = "mac",
constraint_values = [
@@ -28,66 +25,3 @@ platform(
"@platforms//os:linux",
],
)
-
-cc_toolchain_suite(
- name = "cc_toolchain_suite",
- tags = ["manual"],
- toolchains = {
- "darwin_x86_64": ":mac_toolchain",
- "k8": ":linux_toolchain",
- },
-)
-
-filegroup(name = "empty")
-
-cc_toolchain(
- name = "mac_toolchain",
- all_files = ":empty",
- compiler_files = ":empty",
- dwp_files = ":empty",
- linker_files = ":empty",
- objcopy_files = ":empty",
- strip_files = ":empty",
- supports_param_files = 0,
- toolchain_config = ":mac_toolchain_config",
- toolchain_identifier = "mac-toolchain",
-)
-
-toolchain(
- name = "mac_toolchain_definition",
- target_compatible_with = ["@platforms//os:macos"],
- toolchain = ":mac_toolchain",
- toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
-)
-
-fake_cc_toolchain_config(
- name = "mac_toolchain_config",
- target_cpu = "darwin_x86_64",
- toolchain_identifier = "mac-toolchain",
-)
-
-cc_toolchain(
- name = "linux_toolchain",
- all_files = ":empty",
- compiler_files = ":empty",
- dwp_files = ":empty",
- linker_files = ":empty",
- objcopy_files = ":empty",
- strip_files = ":empty",
- supports_param_files = 0,
- toolchain_config = ":linux_toolchain_config",
- toolchain_identifier = "linux-toolchain",
-)
-
-toolchain(
- name = "linux_toolchain_definition",
- target_compatible_with = ["@platforms//os:linux"],
- toolchain = ":linux_toolchain",
- toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
-)
-
-fake_cc_toolchain_config(
- name = "linux_toolchain_config",
- target_cpu = "k8",
- toolchain_identifier = "linux-toolchain",
-)
diff --git a/tools/build_defs/python/tests/py_test/py_test_tests.bzl b/tools/build_defs/python/tests/py_test/py_test_tests.bzl
index 5983581..1ecb252 100644
--- a/tools/build_defs/python/tests/py_test/py_test_tests.bzl
+++ b/tools/build_defs/python/tests/py_test/py_test_tests.bzl
@@ -24,8 +24,8 @@ load("//tools/build_defs/python/tests:util.bzl", pt_util = "util")
# Explicit Label() calls are required so that it resolves in @rules_python context instead of
# @rules_testing context.
-_FAKE_CC_TOOLCHAIN = Label("//tools/build_defs/python/tests:cc_toolchain_suite")
-_FAKE_CC_TOOLCHAINS = [str(Label("//tools/build_defs/python/tests:all"))]
+_FAKE_CC_TOOLCHAIN = Label("//tests/cc:cc_toolchain_suite")
+_FAKE_CC_TOOLCHAINS = [str(Label("//tests/cc:all"))]
_PLATFORM_MAC = Label("//tools/build_defs/python/tests:mac")
_PLATFORM_LINUX = Label("//tools/build_defs/python/tests:linux")