Skip to content

Commit 6fd4c0b

Browse files
aignasrickeylev
andauthored
feat: support arbitrary target_settings in our platforms 3/n (#2990)
With this PR we can support arbitrary target settings instead of just plain `constraint_values`. We still have custom logic to ensure that all of the tests pass. However, the plan is to remove those tests once we have simplified the wheel selection mechanisms and the `pkg_aliases` macro. I.e. if we have at most 1 wheel per platform that the `pypi` bzlmod extension passes to the `pkg_aliases` macro, then we can just have a simple `selects.with_or` where we list out all of the target platform values. This PR may result in us creating more targets but that is the price that we have to pay if we want to do this incrementally. Work towards #2747 Work towards #2548 Work towards #260 Co-authored-by: Richard Levasseur <[email protected]>
1 parent b924c43 commit 6fd4c0b

File tree

10 files changed

+83
-42
lines changed

10 files changed

+83
-42
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ END_UNRELEASED_TEMPLATE
6464
### Added
6565
* (pypi) To configure the environment for `requirements.txt` evaluation, use the newly added
6666
developer preview of the `pip.default` tag class. Only `rules_python` and root modules can use
67-
this feature. You can also configure `constraint_values` using `pip.default`.
67+
this feature. You can also configure custom `config_settings` using `pip.default`.
6868

6969
{#v0-0-0-removed}
7070
### Removed

python/private/pypi/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ bzl_library(
6464
deps = [
6565
":flags_bzl",
6666
"//python/private:flags_bzl",
67+
"@bazel_skylib//lib:selects",
6768
],
6869
)
6970

python/private/pypi/config_settings.bzl

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ suffix.
7070
:::
7171
"""
7272

73+
load("@bazel_skylib//lib:selects.bzl", "selects")
7374
load("//python/private:flags.bzl", "LibcFlag")
7475
load(":flags.bzl", "INTERNAL_FLAGS", "UniversalWhlFlag")
7576

@@ -112,7 +113,7 @@ def config_settings(
112113
muslc_versions = [],
113114
osx_versions = [],
114115
name = None,
115-
platform_constraint_values = {},
116+
platform_config_settings = {},
116117
**kwargs):
117118
"""Generate all of the pip config settings.
118119
@@ -126,7 +127,7 @@ def config_settings(
126127
configure config settings for.
127128
osx_versions (list[str]): The list of OSX OS versions to configure
128129
config settings for.
129-
platform_constraint_values: {type}`dict[str, list[str]]` the constraint
130+
platform_config_settings: {type}`dict[str, list[str]]` the constraint
130131
values to use instead of the default ones. Key are platform names
131132
(a human-friendly platform string). Values are lists of
132133
`constraint_value` label strings.
@@ -142,13 +143,24 @@ def config_settings(
142143
# TODO @aignas 2025-06-15: allowing universal2 and platform specific wheels in one
143144
# closure is making things maybe a little bit too complicated.
144145
"osx_universal2": ["@platforms//os:osx"],
145-
} | platform_constraint_values
146+
} | platform_config_settings
146147

147148
for python_version in python_versions:
148-
for platform_name, constraint_values in target_platforms.items():
149+
for platform_name, config_settings in target_platforms.items():
149150
suffix = "_{}".format(platform_name) if platform_name else ""
150151
os, _, cpu = platform_name.partition("_")
151152

153+
# We parse the target settings and if there is a "platforms//os" or
154+
# "platforms//cpu" value in here, we also add it into the constraint_values
155+
#
156+
# this is to ensure that we can still pass all of the unit tests for config
157+
# setting specialization.
158+
constraint_values = []
159+
for setting in config_settings:
160+
setting_label = Label(setting)
161+
if setting_label.repo_name == "platforms" and setting_label.package in ["os", "cpu"]:
162+
constraint_values.append(setting)
163+
152164
_dist_config_settings(
153165
suffix = suffix,
154166
plat_flag_values = _plat_flag_values(
@@ -158,6 +170,7 @@ def config_settings(
158170
glibc_versions = glibc_versions,
159171
muslc_versions = muslc_versions,
160172
),
173+
config_settings = config_settings,
161174
constraint_values = constraint_values,
162175
python_version = python_version,
163176
**kwargs
@@ -318,7 +331,7 @@ def _plat_flag_values(os, cpu, osx_versions, glibc_versions, muslc_versions):
318331

319332
return ret
320333

321-
def _dist_config_setting(*, name, compatible_with = None, native = native, **kwargs):
334+
def _dist_config_setting(*, name, compatible_with = None, selects = selects, native = native, config_settings = None, **kwargs):
322335
"""A macro to create a target for matching Python binary and source distributions.
323336
324337
Args:
@@ -327,6 +340,12 @@ def _dist_config_setting(*, name, compatible_with = None, native = native, **kwa
327340
compatible with the given dist config setting. For example, if only
328341
non-freethreaded python builds are allowed, add
329342
FLAGS._is_py_freethreaded_no here.
343+
config_settings: {type}`list[str | Label]` the list of target settings that must
344+
be matched before we try to evaluate the config_setting that we may create in
345+
this function.
346+
selects (struct): The struct containing config_setting_group function
347+
to use for creating config setting groups. Can be overridden for unit tests
348+
reasons.
330349
native (struct): The struct containing alias and config_setting rules
331350
to use for creating the objects. Can be overridden for unit tests
332351
reasons.
@@ -346,4 +365,14 @@ def _dist_config_setting(*, name, compatible_with = None, native = native, **kwa
346365
)
347366
name = dist_config_setting_name
348367

349-
native.config_setting(name = name, **kwargs)
368+
# first define the config setting that has all of the constraint values
369+
_name = "_" + name
370+
native.config_setting(
371+
name = _name,
372+
**kwargs
373+
)
374+
selects.config_setting_group(
375+
name = name,
376+
match_all = config_settings + [_name],
377+
visibility = kwargs.get("visibility"),
378+
)

python/private/pypi/extension.bzl

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ def _whl_repo(*, src, whl_library_args, is_multiple_versions, download_only, net
372372
),
373373
)
374374

375-
def _configure(config, *, platform, os_name, arch_name, constraint_values, env = {}, override = False):
375+
def _configure(config, *, platform, os_name, arch_name, config_settings, env = {}, override = False):
376376
"""Set the value in the config if the value is provided"""
377377
config.setdefault("platforms", {})
378378
if platform:
@@ -387,7 +387,7 @@ def _configure(config, *, platform, os_name, arch_name, constraint_values, env =
387387
name = platform.replace("-", "_").lower(),
388388
os_name = os_name,
389389
arch_name = arch_name,
390-
constraint_values = constraint_values,
390+
config_settings = config_settings,
391391
env = env,
392392
)
393393
else:
@@ -414,7 +414,7 @@ def _create_config(defaults):
414414
arch_name = cpu,
415415
os_name = "linux",
416416
platform = "linux_{}".format(cpu),
417-
constraint_values = [
417+
config_settings = [
418418
"@platforms//os:linux",
419419
"@platforms//cpu:{}".format(cpu),
420420
],
@@ -431,7 +431,7 @@ def _create_config(defaults):
431431
# See https://endoflife.date/macos
432432
os_name = "osx",
433433
platform = "osx_{}".format(cpu),
434-
constraint_values = [
434+
config_settings = [
435435
"@platforms//os:osx",
436436
"@platforms//cpu:{}".format(cpu),
437437
],
@@ -443,7 +443,7 @@ def _create_config(defaults):
443443
arch_name = "x86_64",
444444
os_name = "windows",
445445
platform = "windows_x86_64",
446-
constraint_values = [
446+
config_settings = [
447447
"@platforms//os:windows",
448448
"@platforms//cpu:x86_64",
449449
],
@@ -513,7 +513,7 @@ You cannot use both the additive_build_content and additive_build_content_file a
513513
_configure(
514514
defaults,
515515
arch_name = tag.arch_name,
516-
constraint_values = tag.constraint_values,
516+
config_settings = tag.config_settings,
517517
env = tag.env,
518518
os_name = tag.os_name,
519519
platform = tag.platform,
@@ -693,9 +693,9 @@ You cannot use both the additive_build_content and additive_build_content_file a
693693
}
694694
for hub_name, extra_whl_aliases in extra_aliases.items()
695695
},
696-
platform_constraint_values = {
696+
platform_config_settings = {
697697
hub_name: {
698-
platform_name: sorted([str(Label(cv)) for cv in p.constraint_values])
698+
platform_name: sorted([str(Label(cv)) for cv in p.config_settings])
699699
for platform_name, p in config.platforms.items()
700700
}
701701
for hub_name in hub_whl_map
@@ -790,7 +790,7 @@ def _pip_impl(module_ctx):
790790
for key, values in whl_map.items()
791791
},
792792
packages = mods.exposed_packages.get(hub_name, []),
793-
platform_constraint_values = mods.platform_constraint_values.get(hub_name, {}),
793+
platform_config_settings = mods.platform_config_settings.get(hub_name, {}),
794794
groups = mods.hub_group_map.get(hub_name),
795795
)
796796

@@ -812,10 +812,11 @@ Either this or {attr}`env` `platform_machine` key should be specified.
812812
:::
813813
""",
814814
),
815-
"constraint_values": attr.label_list(
815+
"config_settings": attr.label_list(
816816
mandatory = True,
817817
doc = """\
818-
The constraint_values to use in select statements.
818+
The list of labels to `config_setting` targets that need to be matched for the platform to be
819+
selected.
819820
""",
820821
),
821822
"os_name": attr.string(
@@ -1145,6 +1146,9 @@ terms used in this extension.
11451146
11461147
[environment_markers]: https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers
11471148
:::
1149+
1150+
:::{versionadded} VERSION_NEXT_FEATURE
1151+
:::
11481152
""",
11491153
),
11501154
"override": _override_tag,

python/private/pypi/hub_repository.bzl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def _impl(rctx):
3434
},
3535
extra_hub_aliases = rctx.attr.extra_hub_aliases,
3636
requirement_cycles = rctx.attr.groups,
37-
platform_constraint_values = rctx.attr.platform_constraint_values,
37+
platform_config_settings = rctx.attr.platform_config_settings,
3838
)
3939
for path, contents in aliases.items():
4040
rctx.file(path, contents)
@@ -84,7 +84,7 @@ hub_repository = repository_rule(
8484
The list of packages that will be exposed via all_*requirements macros. Defaults to whl_map keys.
8585
""",
8686
),
87-
"platform_constraint_values": attr.string_list_dict(
87+
"platform_config_settings": attr.string_list_dict(
8888
doc = "The constraint values for each platform name. The values are string canonical string Label representations",
8989
mandatory = False,
9090
),

python/private/pypi/render_pkg_aliases.bzl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,14 @@ def _major_minor_versions(python_versions):
155155
# Use a dict as a simple set
156156
return sorted({_major_minor(v): None for v in python_versions})
157157

158-
def render_multiplatform_pkg_aliases(*, aliases, platform_constraint_values = {}, **kwargs):
158+
def render_multiplatform_pkg_aliases(*, aliases, platform_config_settings = {}, **kwargs):
159159
"""Render the multi-platform pkg aliases.
160160
161161
Args:
162162
aliases: dict[str, list(whl_config_setting)] A list of aliases that will be
163163
transformed from ones having `filename` to ones having `config_setting`.
164-
platform_constraint_values: {type}`dict[str, list[str]]` contains all of the
165-
target platforms and their appropriate `constraint_values`.
164+
platform_config_settings: {type}`dict[str, list[str]]` contains all of the
165+
target platforms and their appropriate `target_settings`.
166166
**kwargs: extra arguments passed to render_pkg_aliases.
167167
168168
Returns:
@@ -189,20 +189,20 @@ def render_multiplatform_pkg_aliases(*, aliases, platform_constraint_values = {}
189189
muslc_versions = flag_versions.get("muslc_versions", []),
190190
osx_versions = flag_versions.get("osx_versions", []),
191191
python_versions = _major_minor_versions(flag_versions.get("python_versions", [])),
192-
platform_constraint_values = platform_constraint_values,
192+
platform_config_settings = platform_config_settings,
193193
visibility = ["//:__subpackages__"],
194194
)
195195
return contents
196196

197-
def _render_config_settings(platform_constraint_values, **kwargs):
197+
def _render_config_settings(platform_config_settings, **kwargs):
198198
return """\
199199
load("@rules_python//python/private/pypi:config_settings.bzl", "config_settings")
200200
201201
{}""".format(render.call(
202202
"config_settings",
203203
name = repr("config_settings"),
204-
platform_constraint_values = render.dict(
205-
platform_constraint_values,
204+
platform_config_settings = render.dict(
205+
platform_config_settings,
206206
value_repr = render.list,
207207
),
208208
**_repr_dict(value_repr = render.list, **kwargs)

tests/pypi/config_settings/config_settings_tests.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ def config_settings_test_suite(name): # buildifier: disable=function-docstring
657657
glibc_versions = [(2, 14), (2, 17)],
658658
muslc_versions = [(1, 1)],
659659
osx_versions = [(10, 9), (11, 0)],
660-
platform_constraint_values = {
660+
platform_config_settings = {
661661
"linux_aarch64": [
662662
"@platforms//cpu:aarch64",
663663
"@platforms//os:linux",

tests/pypi/extension/extension_tests.bzl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,17 @@ def _parse_modules(env, enable_pipstar = 0, **kwargs):
7878

7979
def _default(
8080
arch_name = None,
81-
constraint_values = None,
81+
config_settings = None,
8282
os_name = None,
8383
platform = None,
84-
target_settings = None,
8584
env = None,
8685
whl_limit = None,
8786
whl_platforms = None):
8887
return struct(
8988
arch_name = arch_name,
90-
constraint_values = constraint_values,
9189
os_name = os_name,
9290
platform = platform,
93-
target_settings = target_settings,
91+
config_settings = config_settings,
9492
env = env or {},
9593
whl_platforms = whl_platforms,
9694
whl_limit = whl_limit,
@@ -1051,7 +1049,7 @@ def _test_pipstar_platforms(env):
10511049
default = [
10521050
_default(
10531051
platform = "{}_{}".format(os, cpu),
1054-
constraint_values = [
1052+
config_settings = [
10551053
"@platforms//os:{}".format(os),
10561054
"@platforms//cpu:{}".format(cpu),
10571055
],

tests/pypi/pkg_aliases/pkg_aliases_test.bzl

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,9 @@ _tests.append(_test_multiplatform_whl_aliases_filename_versioned)
392392
def _mock_alias(container):
393393
return lambda name, **kwargs: container.append(name)
394394

395+
def _mock_config_setting_group(container):
396+
return lambda name, **kwargs: container.append(name)
397+
395398
def _mock_config_setting(container):
396399
def _inner(name, flag_values = None, constraint_values = None, **_):
397400
if flag_values or constraint_values:
@@ -417,9 +420,12 @@ def _test_config_settings_exist_legacy(env):
417420
python_versions = ["3.11"],
418421
native = struct(
419422
alias = _mock_alias(available_config_settings),
420-
config_setting = _mock_config_setting(available_config_settings),
423+
config_setting = _mock_config_setting([]),
421424
),
422-
platform_constraint_values = {
425+
selects = struct(
426+
config_setting_group = _mock_config_setting_group(available_config_settings),
427+
),
428+
platform_config_settings = {
423429
"linux_aarch64": [
424430
"@platforms//cpu:aarch64",
425431
"@platforms//os:linux",
@@ -454,7 +460,7 @@ def _test_config_settings_exist(env):
454460
"any": {},
455461
"macosx_11_0_arm64": {
456462
"osx_versions": [(11, 0)],
457-
"platform_constraint_values": {
463+
"platform_config_settings": {
458464
"osx_aarch64": [
459465
"@platforms//cpu:aarch64",
460466
"@platforms//os:osx",
@@ -463,7 +469,7 @@ def _test_config_settings_exist(env):
463469
},
464470
"manylinux_2_17_x86_64": {
465471
"glibc_versions": [(2, 17), (2, 18)],
466-
"platform_constraint_values": {
472+
"platform_config_settings": {
467473
"linux_x86_64": [
468474
"@platforms//cpu:x86_64",
469475
"@platforms//os:linux",
@@ -472,7 +478,7 @@ def _test_config_settings_exist(env):
472478
},
473479
"manylinux_2_18_x86_64": {
474480
"glibc_versions": [(2, 17), (2, 18)],
475-
"platform_constraint_values": {
481+
"platform_config_settings": {
476482
"linux_x86_64": [
477483
"@platforms//cpu:x86_64",
478484
"@platforms//os:linux",
@@ -481,7 +487,7 @@ def _test_config_settings_exist(env):
481487
},
482488
"musllinux_1_1_aarch64": {
483489
"muslc_versions": [(1, 2), (1, 1), (1, 0)],
484-
"platform_constraint_values": {
490+
"platform_config_settings": {
485491
"linux_aarch64": [
486492
"@platforms//cpu:aarch64",
487493
"@platforms//os:linux",
@@ -500,7 +506,10 @@ def _test_config_settings_exist(env):
500506
python_versions = ["3.11"],
501507
native = struct(
502508
alias = _mock_alias(available_config_settings),
503-
config_setting = _mock_config_setting(available_config_settings),
509+
config_setting = _mock_config_setting([]),
510+
),
511+
selects = struct(
512+
config_setting_group = _mock_config_setting_group(available_config_settings),
504513
),
505514
**kwargs
506515
)

0 commit comments

Comments
 (0)