feat: add //command_line_option:extra_toolchains pseudo-flag (#3810)
Add support for transitioning on the
//command_line_option:extra_toolchains built-in flag using
py_binary.config_settings.
Note that, unlike the normal Bazel built-in flag, it must be specified
as a simple comma-separated string when set using config_settings, which
is then parsed as a CSV string.
This is to make it easier to, on a per-target basis, such as with a
zipapp or wheels,
change the Python toolchains used.
diff --git a/command_line_option/BUILD.bazel b/command_line_option/BUILD.bazel
index a4d5f0f..15c22f3 100644
--- a/command_line_option/BUILD.bazel
+++ b/command_line_option/BUILD.bazel
@@ -18,6 +18,11 @@
actual = "//python:none",
)
+alias(
+ name = "extra_toolchains",
+ actual = "//python:none",
+)
+
filegroup(
name = "distribution",
srcs = glob(["**"]),
diff --git a/docs/api/rules_python/command_line_option/index.md b/docs/api/rules_python/command_line_option/index.md
index a7dfa34..02d2920 100644
--- a/docs/api/rules_python/command_line_option/index.md
+++ b/docs/api/rules_python/command_line_option/index.md
@@ -51,3 +51,26 @@
The special value `INHERIT` can be specified to use the existing flag value.
:::
+
+## extra_toolchains
+
+:::{bzl:target} extra_toolchains
+
+Special target for the Bazel-builtin `//command_line_option:extra_toolchains`
+flag.
+
+See the [Bazel documentation for --extra_toolchains](https://bazel.build/reference/command-line-reference#flag--extra_toolchains).
+
+The special value `INHERIT` can be specified to use the existing flag value.
+
+:::{note}
+Unlike the normal Bazel built-in flag, which accepts a list of labels, this
+pseudo-flag must be specified as a single, comma-separated string when set
+using the `config_settings` attribute. For example:
+
+```python
+"//command_line_option:extra_toolchains": "//my/tc1,//my/tc2"
+```
+:::
+:::
+
diff --git a/python/features.bzl b/python/features.bzl
index 26acc8c..a2cb95f 100644
--- a/python/features.bzl
+++ b/python/features.bzl
@@ -86,6 +86,7 @@
_TARGETS = {
"//command_line_option:build_runfile_links": True,
"//command_line_option:enable_runfiles": True,
+ "//command_line_option:extra_toolchains": True,
"//python/cc:current_py_cc_headers_abi3": True,
}
diff --git a/python/private/attributes.bzl b/python/private/attributes.bzl
index beaa7a6..ad741d0 100644
--- a/python/private/attributes.bzl
+++ b/python/private/attributes.bzl
@@ -446,10 +446,15 @@
if key.package == "command_line_option":
if value == "INHERIT":
continue
- else:
- str_key = "//command_line_option:" + key.name
+ str_key = "//command_line_option:" + key.name
+ if key.name == "extra_toolchains":
+ if value == "":
+ value = []
+ else:
+ value = [v.strip() for v in value.split(",") if v.strip()]
else:
str_key = str(key)
+
settings[str_key] = value
return settings
diff --git a/python/private/common_labels.bzl b/python/private/common_labels.bzl
index ff4e7e1..a83ba2b 100644
--- a/python/private/common_labels.bzl
+++ b/python/private/common_labels.bzl
@@ -14,6 +14,7 @@
# NOTE: Special target; see definition for details.
ENABLE_RUNFILES = str(Label("//command_line_option:enable_runfiles")),
EXEC_TOOLS_TOOLCHAIN = str(Label("//python/config_settings:exec_tools_toolchain")),
+ EXTRA_TOOLCHAINS = str(Label("//command_line_option:extra_toolchains")),
NONE = str(Label("//python:none")),
PIP_ENV_MARKER_CONFIG = str(Label("//python/config_settings:pip_env_marker_config")),
PIP_WHL_OSX_VERSION = str(Label("//python/config_settings:pip_whl_osx_version")),
diff --git a/python/private/transition_labels.bzl b/python/private/transition_labels.bzl
index 5f0aa69..7a6531e 100644
--- a/python/private/transition_labels.bzl
+++ b/python/private/transition_labels.bzl
@@ -12,6 +12,7 @@
labels.BOOTSTRAP_IMPL,
labels.DEBUGGER,
labels.EXEC_TOOLS_TOOLCHAIN,
+ "//command_line_option:extra_toolchains",
labels.PIP_ENV_MARKER_CONFIG,
labels.PIP_WHL_OSX_VERSION,
labels.PRECOMPILE,
diff --git a/tests/base_rules/py_executable_base_tests.bzl b/tests/base_rules/py_executable_base_tests.bzl
index dff0399..7531de4 100644
--- a/tests/base_rules/py_executable_base_tests.bzl
+++ b/tests/base_rules/py_executable_base_tests.bzl
@@ -109,6 +109,29 @@
_tests.append(_test_basic_zip)
+def _test_config_settings_extra_toolchains(name, config):
+ rt_util.helper_target(
+ config.rule,
+ name = name + "_subject",
+ srcs = ["main.py"],
+ main = "main.py",
+ config_settings = {
+ "//command_line_option:extra_toolchains": "{tc},{tc}".format(tc = CC_TOOLCHAIN),
+ },
+ )
+ analysis_test(
+ name = name,
+ impl = _test_config_settings_extra_toolchains_impl,
+ target = name + "_subject",
+ )
+
+def _test_config_settings_extra_toolchains_impl(env, target):
+ # If we got here, it means analysis succeeded, which implies the transition
+ # successfully parsed the CSV string into a list.
+ env.expect.that_target(target).has_provider(PyInfo)
+
+_tests.append(_test_config_settings_extra_toolchains)
+
def _test_cross_compile_to_unix(name, config):
rt_util.helper_target(
config.rule,
diff --git a/tests/integration/bzlmod_lockfile/MODULE.bazel.lock b/tests/integration/bzlmod_lockfile/MODULE.bazel.lock
index 0dcc4b2..d21fec2 100644
--- a/tests/integration/bzlmod_lockfile/MODULE.bazel.lock
+++ b/tests/integration/bzlmod_lockfile/MODULE.bazel.lock
@@ -250,7 +250,7 @@
},
"@@rules_python+//python/uv:uv.bzl%uv": {
"general": {
- "bzlTransitiveDigest": "yrEbeCJlv5gbdoRjwwR/EDEkc3pfuQy/FTYKMNoK5Wc=",
+ "bzlTransitiveDigest": "Z5ZPR9z4PkJRXSyJ4KQEqM4kwiqWBCn8Ajzxy9YlS/g=",
"usagesDigest": "6yXGw7XDyXjOfqBL0SBu1YBEMMYPQzCE3jTzUCkxPgg=",
"recordedInputs": [
"REPO_MAPPING:rules_python+,bazel_tools bazel_tools",