Skip to content
Merged
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ END_UNRELEASED_TEMPLATE
### Added
* (pypi) To configure the environment for `requirements.txt` evaluation, use the newly added
developer preview of the `pip.default` tag class. Only `rules_python` and root modules can use
this feature.
this feature. You can also configure `constraint_values` using `pip.default`.

{#v0-0-0-removed}
### Removed
Expand Down
31 changes: 14 additions & 17 deletions python/private/pypi/config_settings.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ def config_settings(
glibc_versions = [],
muslc_versions = [],
osx_versions = [],
target_platforms = [],
name = None,
platform_constraint_values = {},
**kwargs):
"""Generate all of the pip config settings.

Expand All @@ -126,31 +126,28 @@ def config_settings(
configure config settings for.
osx_versions (list[str]): The list of OSX OS versions to configure
config settings for.
target_platforms (list[str]): The list of "{os}_{cpu}" for deriving
constraint values for each condition.
platform_constraint_values: {type}`dict[str, list[str]]` the constraint
values to use instead of the default ones. Key are platform names
(a human-friendly platform string). Values are lists of
`constraint_value` label strings.
**kwargs: Other args passed to the underlying implementations, such as
{obj}`native`.
"""

glibc_versions = [""] + glibc_versions
muslc_versions = [""] + muslc_versions
osx_versions = [""] + osx_versions
target_platforms = [("", ""), ("osx", "universal2")] + [
t.split("_", 1)
for t in target_platforms
]
target_platforms = {
"": [],
# TODO @aignas 2025-06-15: allowing universal2 and platform specific wheels in one
# closure is making things maybe a little bit too complicated.
"osx_universal2": ["@platforms//os:osx"],
} | platform_constraint_values

for python_version in python_versions:
for os, cpu in target_platforms:
constraint_values = []
suffix = ""
if os:
constraint_values.append("@platforms//os:" + os)
suffix += "_" + os
if cpu:
suffix += "_" + cpu
if cpu != "universal2":
constraint_values.append("@platforms//cpu:" + cpu)
for platform_name, constraint_values in target_platforms.items():
suffix = "_{}".format(platform_name) if platform_name else ""
os, _, cpu = platform_name.partition("_")

_dist_config_settings(
suffix = suffix,
Expand Down
34 changes: 31 additions & 3 deletions python/private/pypi/extension.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ def _whl_repo(*, src, whl_library_args, is_multiple_versions, download_only, net
),
)

def _configure(config, *, platform, os_name, arch_name, override = False, env = {}):
def _configure(config, *, platform, os_name, arch_name, constraint_values, env = {}, override = False):
"""Set the value in the config if the value is provided"""
config.setdefault("platforms", {})
if platform:
Expand All @@ -387,6 +387,7 @@ def _configure(config, *, platform, os_name, arch_name, override = False, env =
name = platform.replace("-", "_").lower(),
os_name = os_name,
arch_name = arch_name,
constraint_values = constraint_values,
env = env,
)
else:
Expand All @@ -413,6 +414,10 @@ def _create_config(defaults):
arch_name = cpu,
os_name = "linux",
platform = "linux_{}".format(cpu),
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:{}".format(cpu),
],
env = {"platform_version": "0"},
)
for cpu in [
Expand All @@ -424,17 +429,25 @@ def _create_config(defaults):
arch_name = cpu,
# We choose the oldest non-EOL version at the time when we release `rules_python`.
# See https://endoflife.date/macos
env = {"platform_version": "14.0"},
os_name = "osx",
platform = "osx_{}".format(cpu),
constraint_values = [
"@platforms//os:osx",
"@platforms//cpu:{}".format(cpu),
],
env = {"platform_version": "14.0"},
)

_configure(
defaults,
arch_name = "x86_64",
env = {"platform_version": "0"},
os_name = "windows",
platform = "windows_x86_64",
constraint_values = [
"@platforms//os:windows",
"@platforms//cpu:x86_64",
],
env = {"platform_version": "0"},
)
return struct(**defaults)

Expand Down Expand Up @@ -500,6 +513,7 @@ You cannot use both the additive_build_content and additive_build_content_file a
_configure(
defaults,
arch_name = tag.arch_name,
constraint_values = tag.constraint_values,
env = tag.env,
os_name = tag.os_name,
platform = tag.platform,
Expand Down Expand Up @@ -679,6 +693,13 @@ You cannot use both the additive_build_content and additive_build_content_file a
}
for hub_name, extra_whl_aliases in extra_aliases.items()
},
platform_constraint_values = {
hub_name: {
platform_name: sorted([str(Label(cv)) for cv in p.constraint_values])
for platform_name, p in config.platforms.items()
}
for hub_name in hub_whl_map
},
whl_libraries = {
k: dict(sorted(args.items()))
for k, args in sorted(whl_libraries.items())
Expand Down Expand Up @@ -769,6 +790,7 @@ def _pip_impl(module_ctx):
for key, values in whl_map.items()
},
packages = mods.exposed_packages.get(hub_name, []),
platform_constraint_values = mods.platform_constraint_values.get(hub_name, {}),
groups = mods.hub_group_map.get(hub_name),
)

Expand All @@ -788,6 +810,12 @@ The CPU architecture name to be used.
:::{note}
Either this or {attr}`env` `platform_machine` key should be specified.
:::
""",
),
"constraint_values": attr.label_list(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be a string_list, not label list?

If it's a label, it'll trigger repo-evaluation, even if it goes unused.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be changed to target_settings in the next thing up the stack.

However, when we will have target_settings I would have expected that we would need the attr.label_list because we want to ensure that the users don't need to use canonical representation of the labels. The fetch would not matter that much, because this affects only the hub repo and the extension itself, both of which are reasonably cheap.

What do you mean about repo-evaluation? I thought that we can use @platforms//os:foo and does not cause any fetching?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it does cause repo fetching? Though, when I think back, maybe I'm confusing label_list() in repo/bzlmod with rctx.path(<some label>).

In any case, lets double check to verify.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followup thought for subsequent PR: replace constraint_values and target_settings with simply "config_settings" ?

The custom platform stuff in the toolchains has target_compatible_with (constraints) and target_settings because that's what the toolchain() rule accepts. However, for the pip stuff, it just needs something valid for a select() key.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, I like this suggestion. config_settings does sound more natural.

mandatory = True,
doc = """\
The constraint_values to use in select statements.
""",
),
"os_name": attr.string(
Expand Down
5 changes: 5 additions & 0 deletions python/private/pypi/hub_repository.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def _impl(rctx):
},
extra_hub_aliases = rctx.attr.extra_hub_aliases,
requirement_cycles = rctx.attr.groups,
platform_constraint_values = rctx.attr.platform_constraint_values,
)
for path, contents in aliases.items():
rctx.file(path, contents)
Expand Down Expand Up @@ -83,6 +84,10 @@ hub_repository = repository_rule(
The list of packages that will be exposed via all_*requirements macros. Defaults to whl_map keys.
""",
),
"platform_constraint_values": attr.string_list_dict(
doc = "The constraint values for each platform name. The values are string canonical string Label representations",
mandatory = False,
),
"repo_name": attr.string(
mandatory = True,
doc = "The apparent name of the repo. This is needed because in bzlmod, the name attribute becomes the canonical name.",
Expand Down
12 changes: 9 additions & 3 deletions python/private/pypi/render_pkg_aliases.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,14 @@ def _major_minor_versions(python_versions):
# Use a dict as a simple set
return sorted({_major_minor(v): None for v in python_versions})

def render_multiplatform_pkg_aliases(*, aliases, **kwargs):
def render_multiplatform_pkg_aliases(*, aliases, platform_constraint_values = {}, **kwargs):
"""Render the multi-platform pkg aliases.

Args:
aliases: dict[str, list(whl_config_setting)] A list of aliases that will be
transformed from ones having `filename` to ones having `config_setting`.
platform_constraint_values: {type}`dict[str, list[str]]` contains all of the
target platforms and their appropriate `constraint_values`.
**kwargs: extra arguments passed to render_pkg_aliases.

Returns:
Expand All @@ -187,18 +189,22 @@ def render_multiplatform_pkg_aliases(*, aliases, **kwargs):
muslc_versions = flag_versions.get("muslc_versions", []),
osx_versions = flag_versions.get("osx_versions", []),
python_versions = _major_minor_versions(flag_versions.get("python_versions", [])),
target_platforms = flag_versions.get("target_platforms", []),
platform_constraint_values = platform_constraint_values,
visibility = ["//:__subpackages__"],
)
return contents

def _render_config_settings(**kwargs):
def _render_config_settings(platform_constraint_values, **kwargs):
return """\
load("@rules_python//python/private/pypi:config_settings.bzl", "config_settings")

{}""".format(render.call(
"config_settings",
name = repr("config_settings"),
platform_constraint_values = render.dict(
platform_constraint_values,
value_repr = render.list,
),
**_repr_dict(value_repr = render.list, **kwargs)
))

Expand Down
39 changes: 30 additions & 9 deletions tests/pypi/config_settings/config_settings_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -657,13 +657,34 @@ def config_settings_test_suite(name): # buildifier: disable=function-docstring
glibc_versions = [(2, 14), (2, 17)],
muslc_versions = [(1, 1)],
osx_versions = [(10, 9), (11, 0)],
target_platforms = [
"windows_x86_64",
"windows_aarch64",
"linux_x86_64",
"linux_ppc",
"linux_aarch64",
"osx_x86_64",
"osx_aarch64",
],
platform_constraint_values = {
"linux_aarch64": [
"@platforms//cpu:aarch64",
"@platforms//os:linux",
],
"linux_ppc": [
"@platforms//cpu:ppc",
"@platforms//os:linux",
],
"linux_x86_64": [
"@platforms//cpu:x86_64",
"@platforms//os:linux",
],
"osx_aarch64": [
"@platforms//cpu:aarch64",
"@platforms//os:osx",
],
"osx_x86_64": [
"@platforms//cpu:x86_64",
"@platforms//os:osx",
],
"windows_aarch64": [
"@platforms//cpu:aarch64",
"@platforms//os:windows",
],
"windows_x86_64": [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
],
},
)
4 changes: 4 additions & 0 deletions tests/pypi/extension/extension_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,10 @@ def _test_pipstar_platforms(env):
default = [
_default(
platform = "{}_{}".format(os, cpu),
constraint_values = [
"@platforms//os:{}".format(os),
"@platforms//cpu:{}".format(cpu),
],
)
for os, cpu in [
("linux", "x86_64"),
Expand Down
42 changes: 34 additions & 8 deletions tests/pypi/pkg_aliases/pkg_aliases_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -419,10 +419,16 @@ def _test_config_settings_exist_legacy(env):
alias = _mock_alias(available_config_settings),
config_setting = _mock_config_setting(available_config_settings),
),
target_platforms = [
"linux_aarch64",
"linux_x86_64",
],
platform_constraint_values = {
"linux_aarch64": [
"@platforms//cpu:aarch64",
"@platforms//os:linux",
],
"linux_x86_64": [
"@platforms//cpu:x86_64",
"@platforms//os:linux",
],
},
)

got_aliases = multiplatform_whl_aliases(
Expand All @@ -448,19 +454,39 @@ def _test_config_settings_exist(env):
"any": {},
"macosx_11_0_arm64": {
"osx_versions": [(11, 0)],
"target_platforms": ["osx_aarch64"],
"platform_constraint_values": {
"osx_aarch64": [
"@platforms//cpu:aarch64",
"@platforms//os:osx",
],
},
},
"manylinux_2_17_x86_64": {
"glibc_versions": [(2, 17), (2, 18)],
"target_platforms": ["linux_x86_64"],
"platform_constraint_values": {
"linux_x86_64": [
"@platforms//cpu:x86_64",
"@platforms//os:linux",
],
},
},
"manylinux_2_18_x86_64": {
"glibc_versions": [(2, 17), (2, 18)],
"target_platforms": ["linux_x86_64"],
"platform_constraint_values": {
"linux_x86_64": [
"@platforms//cpu:x86_64",
"@platforms//os:linux",
],
},
},
"musllinux_1_1_aarch64": {
"muslc_versions": [(1, 2), (1, 1), (1, 0)],
"target_platforms": ["linux_aarch64"],
"platform_constraint_values": {
"linux_aarch64": [
"@platforms//cpu:aarch64",
"@platforms//os:linux",
],
},
},
}.items():
aliases = {
Expand Down
13 changes: 12 additions & 1 deletion tests/pypi/render_pkg_aliases/render_pkg_aliases_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ def _test_bzlmod_aliases(env):
},
},
extra_hub_aliases = {"bar_baz": ["foo"]},
platform_constraint_values = {
"linux_x86_64": [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
},
)

want_key = "bar_baz/BUILD.bazel"
Expand Down Expand Up @@ -130,8 +136,13 @@ load("@rules_python//python/private/pypi:config_settings.bzl", "config_settings"

config_settings(
name = "config_settings",
platform_constraint_values = {
"linux_x86_64": [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
},
python_versions = ["3.2"],
target_platforms = ["linux_x86_64"],
visibility = ["//:__subpackages__"],
)""",
)
Expand Down