Skip to content

Commit 0cecfa1

Browse files
committed
add arbitray settings support for local toolchains
1 parent 96f888b commit 0cecfa1

File tree

5 files changed

+103
-7
lines changed

5 files changed

+103
-7
lines changed

docs/toolchains.md

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -377,15 +377,14 @@ local_runtime_repo(
377377
local_runtime_toolchains_repo(
378378
name = "local_toolchains",
379379
runtimes = ["local_python3"],
380+
# TIP: The `target_settings` arg can be used to activate them based on
381+
# command line flags; see docs below.
380382
)
381383

382384
# Step 3: Register the toolchains
383385
register_toolchains("@local_toolchains//:all", dev_dependency = True)
384386
```
385387

386-
Note that `register_toolchains` will insert the local toolchain earlier in the
387-
toolchain ordering, so it will take precedence over other registered toolchains.
388-
389388
:::{important}
390389
Be sure to set `dev_dependency = True`. Using a local toolchain only makes sense
391390
for the root module.
@@ -397,6 +396,49 @@ downstream modules.
397396

398397
Multiple runtimes and/or toolchains can be defined, which allows for multiple
399398
Python versions and/or platforms to be configured in a single `MODULE.bazel`.
399+
Note that `register_toolchains` will insert the local toolchain earlier in the
400+
toolchain ordering, so it will take precedence over other registered toolchains.
401+
402+
This behavior can be mitigated by specifying the
403+
{obj}`local_runtime_toolchains_repo.target_settings` arg. This allows attaching
404+
arbitrary {obj}`config_setting()` conditions for activating the toolchain.
405+
406+
```
407+
# File: MODULE.bazel
408+
bazel_dep(name = "bazel_skylib", version = "1.7.1")
409+
410+
local_runtime_toolchains_repo(
411+
name = "local_toolchains",
412+
runtimes = ["local_python3"],
413+
target_settings = {
414+
"local_python3": ["@//:is_py_local_set"]
415+
}
416+
)
417+
418+
# Step 2: Create toolchains for the runtimes
419+
local_runtime_toolchains_repo(
420+
name = "local_toolchains",
421+
runtimes = ["local_python3"],
422+
# TIP: The `target_settings` arg can be used to activate them based on
423+
# command line flags; see docs below.
424+
)
425+
426+
# File: BUILD.bazel
427+
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
428+
429+
config_setting(
430+
name = "is_py_local_set",
431+
flag_values = {":py": "local"},
432+
)
433+
434+
string_flag(
435+
name = "py",
436+
build_setting_default = "",
437+
)
438+
439+
```
440+
441+
400442

401443
## Runtime environment toolchain
402444

python/private/local_runtime_toolchains_repo.bzl

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ define_local_toolchain_suites(
2626
name = "toolchains",
2727
version_aware_repo_names = {version_aware_names},
2828
version_unaware_repo_names = {version_unaware_names},
29+
repo_target_settings = {target_settings},
2930
)
3031
"""
3132

@@ -39,6 +40,10 @@ def _local_runtime_toolchains_repo(rctx):
3940

4041
rctx.file("BUILD.bazel", _TOOLCHAIN_TEMPLATE.format(
4142
version_aware_names = render.list(rctx.attr.runtimes),
43+
target_settings = render.dict(
44+
rctx.attr.target_settings,
45+
value_repr = render.list,
46+
),
4247
version_unaware_names = render.list(rctx.attr.default_runtimes or rctx.attr.runtimes),
4348
))
4449

@@ -78,7 +83,29 @@ Note that order matters: it determines the toolchain priority within the
7883
package.
7984
""",
8085
),
81-
"runtime_target_settings": attr.string_list_dict(
86+
"target_settings": attr.string_list_dict(
87+
doc = """
88+
Constraints that must be satisfied for a toolchain to be used.
89+
90+
This is a dict where:
91+
* **key**: A repo name; one of the names from the `runtimes` or
92+
`default_runtimes` args.
93+
* **value**: list of labels. The labels are typically {obj}`config_setting()`
94+
targets.
95+
:::{note}
96+
Specify `@//foo:bar`, not simply `//foo:bar` or `:bar`. The additional `@` is
97+
needed because the strings are evaluated in a different context than where
98+
they originate.
99+
:::
100+
101+
The list of settings are added to the {obj}`toolchain.target_settings` value
102+
for each respective repo.
103+
104+
This allows a local toolchain to only be used if certain flags or
105+
config setting conditions are met. Such conditions can include user-defined
106+
flags, platform constraints, etc.
107+
108+
""",
82109
),
83110
"_rule_name": attr.string(default = "local_toolchains_repo"),
84111
},

python/private/py_toolchain_suite.bzl

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,11 @@ def _internal_toolchain_suite(prefix, runtime_repo_name, target_compatible_with,
142142
# call in python/repositories.bzl. Bzlmod doesn't need anything; it will
143143
# register `:all`.
144144

145-
def define_local_toolchain_suites(name, version_aware_repo_names, version_unaware_repo_names):
145+
def define_local_toolchain_suites(
146+
name,
147+
version_aware_repo_names,
148+
repo_target_settings,
149+
version_unaware_repo_names):
146150
"""Define toolchains for `local_runtime_repo` backed toolchains.
147151
148152
This generates `toolchain` targets that can be registered using `:all`. The
@@ -160,11 +164,13 @@ def define_local_toolchain_suites(name, version_aware_repo_names, version_unawar
160164
i = 0
161165
for i, repo in enumerate(version_aware_repo_names, start = i):
162166
prefix = render.left_pad_zero(i, 4)
167+
target_settings = ["@{}//:is_matching_python_version".format(repo)]
168+
target_settings.extend(repo_target_settings.get(repo, []))
163169
_internal_toolchain_suite(
164170
prefix = prefix,
165171
runtime_repo_name = repo,
172+
target_settings = target_settings,
166173
target_compatible_with = ["@{}//:os".format(repo)],
167-
target_settings = ["@{}//:is_matching_python_version".format(repo)],
168174
)
169175

170176
# The version unaware entries must go last because they will match any Python
@@ -174,6 +180,6 @@ def define_local_toolchain_suites(name, version_aware_repo_names, version_unawar
174180
_internal_toolchain_suite(
175181
prefix = prefix,
176182
runtime_repo_name = repo,
177-
target_settings = [],
183+
target_settings = repo_target_settings.get(repo, []),
178184
target_compatible_with = ["@{}//:os".format(repo)],
179185
)

tests/integration/local_toolchains/BUILD.bazel

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
1516
load("@rules_python//python:py_test.bzl", "py_test")
1617

1718
py_test(
@@ -20,3 +21,16 @@ py_test(
2021
# Make this test better respect pyenv
2122
env_inherit = ["PYENV_VERSION"],
2223
)
24+
25+
config_setting(
26+
name = "is_py_local_set",
27+
flag_values = {
28+
":py": "local",
29+
},
30+
)
31+
32+
# set to "local" to use the local toolchain
33+
string_flag(
34+
name = "py",
35+
build_setting_default = "",
36+
)

tests/integration/local_toolchains/MODULE.bazel

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
module(name = "module_under_test")
1515

1616
bazel_dep(name = "rules_python", version = "0.0.0")
17+
bazel_dep(name = "bazel_skylib", version = "1.7.1")
18+
1719
local_path_override(
1820
module_name = "rules_python",
1921
path = "../../..",
@@ -32,6 +34,11 @@ local_runtime_repo(
3234
local_runtime_toolchains_repo(
3335
name = "local_toolchains",
3436
runtimes = ["local_python3"],
37+
target_settings = {
38+
"local_python3": [
39+
"@//:is_py_local_set",
40+
],
41+
},
3542
)
3643

3744
python = use_extension("@rules_python//python/extensions:python.bzl", "python")

0 commit comments

Comments
 (0)