Skip to content

Commit 7d36af2

Browse files
committed
feat(pypi): incrementally build platform configuration
Before this PR the configuration for platforms would be built non-incrementally and was somewhat special, making it harder to override particular attributes of the already configured ones.
1 parent 9792058 commit 7d36af2

File tree

2 files changed

+132
-43
lines changed

2 files changed

+132
-43
lines changed

python/private/pypi/extension.bzl

Lines changed: 71 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -377,26 +377,80 @@ def _whl_repo(*, src, whl_library_args, is_multiple_versions, download_only, net
377377
),
378378
)
379379

380-
def _configure(config, *, platform, os_name, arch_name, config_settings, env = {}, override = False):
381-
"""Set the value in the config if the value is provided"""
382-
config.setdefault("platforms", {})
383-
if platform:
384-
if not override and config.get("platforms", {}).get(platform):
385-
return
380+
def _plat(*, name, arch_name, os_name, config_settings = [], env = {}):
381+
return struct(
382+
name = name,
383+
arch_name = arch_name,
384+
os_name = os_name,
385+
config_settings = config_settings,
386+
env = env,
387+
)
386388

389+
def _configure(config, *, override = False, **kwargs):
390+
"""Set the value in the config if the value is provided"""
391+
env = kwargs.get("env")
392+
if env:
387393
for key in env:
388394
if key not in _SUPPORTED_PEP508_KEYS:
389395
fail("Unsupported key in the PEP508 environment: {}".format(key))
390396

391-
config["platforms"][platform] = struct(
392-
name = platform.replace("-", "_").lower(),
393-
os_name = os_name,
394-
arch_name = arch_name,
395-
config_settings = config_settings,
396-
env = env,
397-
)
398-
else:
399-
config["platforms"].pop(platform)
397+
for key, value in kwargs.items():
398+
if value and (override or key not in config):
399+
config[key] = value
400+
401+
def build_config(
402+
*,
403+
module_ctx,
404+
enable_pipstar):
405+
"""Parse 'configure' and 'default' extension tags
406+
407+
Args:
408+
module_ctx: {type}`module_ctx` module context.
409+
enable_pipstar: {type}`bool` a flag to enable dropping Python dependency for
410+
evaluation of the extension.
411+
412+
Returns:
413+
A struct with the configuration.
414+
"""
415+
defaults = {
416+
"platforms": {},
417+
}
418+
for mod in module_ctx.modules:
419+
if not (mod.is_root or mod.name == "rules_python"):
420+
continue
421+
422+
for tag in mod.tags.default:
423+
platform = tag.platform
424+
if platform:
425+
specific_config = defaults["platforms"].setdefault(platform, {})
426+
_configure(
427+
specific_config,
428+
arch_name = tag.arch_name,
429+
config_settings = tag.config_settings,
430+
env = tag.env,
431+
os_name = tag.os_name,
432+
name = platform.replace("-", "_").lower(),
433+
override = mod.is_root,
434+
)
435+
436+
if platform and not (tag.arch_name or tag.config_settings or tag.env or tag.os_name):
437+
defaults["platforms"].pop(platform)
438+
439+
# TODO @aignas 2025-05-19: add more attr groups:
440+
# * for AUTH - the default `netrc` usage could be configured through a common
441+
# attribute.
442+
# * for index/downloader config. This includes all of those attributes for
443+
# overrides, etc. Index overrides per platform could be also used here.
444+
# * for whl selection - selecting preferences of which `platform_tag`s we should use
445+
# for what. We could also model the `cp313t` freethreaded as separate platforms.
446+
447+
return struct(
448+
platforms = {
449+
name: _plat(**values)
450+
for name, values in defaults["platforms"].items()
451+
},
452+
enable_pipstar = enable_pipstar,
453+
)
400454

401455
def parse_modules(
402456
module_ctx,
@@ -448,33 +502,7 @@ You cannot use both the additive_build_content and additive_build_content_file a
448502
srcs_exclude_glob = whl_mod.srcs_exclude_glob,
449503
)
450504

451-
defaults = {
452-
"enable_pipstar": enable_pipstar,
453-
"platforms": {},
454-
}
455-
for mod in module_ctx.modules:
456-
if not (mod.is_root or mod.name == "rules_python"):
457-
continue
458-
459-
for tag in mod.tags.default:
460-
_configure(
461-
defaults,
462-
arch_name = tag.arch_name,
463-
config_settings = tag.config_settings,
464-
env = tag.env,
465-
os_name = tag.os_name,
466-
platform = tag.platform,
467-
override = mod.is_root,
468-
# TODO @aignas 2025-05-19: add more attr groups:
469-
# * for AUTH - the default `netrc` usage could be configured through a common
470-
# attribute.
471-
# * for index/downloader config. This includes all of those attributes for
472-
# overrides, etc. Index overrides per platform could be also used here.
473-
# * for whl selection - selecting preferences of which `platform_tag`s we should use
474-
# for what. We could also model the `cp313t` freethreaded as separate platforms.
475-
)
476-
477-
config = struct(**defaults)
505+
config = build_config(module_ctx = module_ctx, enable_pipstar = enable_pipstar)
478506

479507
# TODO @aignas 2025-06-03: Merge override API with the builder?
480508
_overriden_whl_set = {}
@@ -659,6 +687,7 @@ You cannot use both the additive_build_content and additive_build_content_file a
659687
k: dict(sorted(args.items()))
660688
for k, args in sorted(whl_libraries.items())
661689
},
690+
config = config,
662691
)
663692

664693
def _pip_impl(module_ctx):

tests/pypi/extension/extension_tests.bzl

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
load("@rules_testing//lib:test_suite.bzl", "test_suite")
1818
load("@rules_testing//lib:truth.bzl", "subjects")
19-
load("//python/private/pypi:extension.bzl", "parse_modules") # buildifier: disable=bzl-visibility
19+
load("//python/private/pypi:extension.bzl", "build_config", "parse_modules") # buildifier: disable=bzl-visibility
2020
load("//python/private/pypi:parse_simpleapi_html.bzl", "parse_simpleapi_html") # buildifier: disable=bzl-visibility
2121
load("//python/private/pypi:whl_config_setting.bzl", "whl_config_setting") # buildifier: disable=bzl-visibility
2222

@@ -92,6 +92,18 @@ def _parse_modules(env, enable_pipstar = 0, **kwargs):
9292
),
9393
)
9494

95+
def _build_config(env, enable_pipstar = 0, **kwargs):
96+
return env.expect.that_struct(
97+
build_config(
98+
enable_pipstar = enable_pipstar,
99+
**kwargs
100+
),
101+
attrs = dict(
102+
platforms = subjects.dict,
103+
enable_pipstar = subjects.bool,
104+
),
105+
)
106+
95107
def _default(
96108
arch_name = None,
97109
config_settings = None,
@@ -1206,6 +1218,54 @@ optimum[onnxruntime-gpu]==1.17.1 ; sys_platform == 'linux'
12061218

12071219
_tests.append(_test_pipstar_platforms)
12081220

1221+
def _test_build_pipstar_platform(env):
1222+
config = _build_config(
1223+
env,
1224+
module_ctx = _mock_mctx(
1225+
_mod(
1226+
name = "rules_python",
1227+
default = [
1228+
_default(
1229+
platform = "myplat",
1230+
os_name = "linux",
1231+
arch_name = "x86_64",
1232+
config_settings = [
1233+
"@platforms//os:linux",
1234+
"@platforms//cpu:x86_64",
1235+
],
1236+
),
1237+
_default(),
1238+
_default(
1239+
platform = "myplat2",
1240+
os_name = "linux",
1241+
arch_name = "x86_64",
1242+
config_settings = [
1243+
"@platforms//os:linux",
1244+
"@platforms//cpu:x86_64",
1245+
],
1246+
),
1247+
_default(platform = "myplat2"),
1248+
],
1249+
),
1250+
),
1251+
enable_pipstar = True,
1252+
)
1253+
config.enable_pipstar().equals(True)
1254+
config.platforms().contains_exactly({
1255+
"myplat": struct(
1256+
name = "myplat",
1257+
os_name = "linux",
1258+
arch_name = "x86_64",
1259+
config_settings = [
1260+
"@platforms//os:linux",
1261+
"@platforms//cpu:x86_64",
1262+
],
1263+
env = {},
1264+
),
1265+
})
1266+
1267+
_tests.append(_test_build_pipstar_platform)
1268+
12091269
def extension_test_suite(name):
12101270
"""Create the test suite.
12111271

0 commit comments

Comments
 (0)