From b83b5cb10f04dbb41c210a1636cac5ef67ba98d5 Mon Sep 17 00:00:00 2001 From: Connor McEntee Date: Wed, 19 Aug 2026 12:21:55 -0600 Subject: [PATCH] feat(uv): parameterize the platform parent in gazelle_python_manifest The per-venv synthetic platform gazelle_python_manifest generates hardcoded parents = ["@platforms//host"], and the dep_group transition replaces --platforms wholesale. Anything building under the transitioned configuration therefore resolved toolchains against a bare os+cpu platform, which breaks builds with a custom --host_platform (hermetic cc toolchains gating on extra constraints fail sdist builds in the hub at analysis) and cross-compilation (wheel selection snaps back to the host platform, issue #1416). Add a platform_parents parameter, defaulting to the current behavior so existing callers are unaffected, and thread it into the synthetic platform's parents. The list must hold exactly one label (platform() accepts at most one parent); the macro fails eagerly with a message naming the parameter otherwise. The default is built as Label("@platforms//host") in the defining module, so callers need no direct bazel_dep on platforms to use it. A new e2e snapshot case builds a manifest under a caller-supplied parent and pins a probe transitioned through the macro-generated platform, selecting on a constraint only the custom parent carries. Fixes #1416 Co-Authored-By: Claude Fable 5 --- docs/uv.md | 7 ++ e2e/cases/BUILD.bazel | 13 ++++ .../gazelle-platform-parents-1416/BUILD.bazel | 65 +++++++++++++++++++ ..._platform_parents_1416.gazelle_python.yaml | 18 +++++ ...azelle_platform_parents_1416.probe_out.txt | 1 + uv/private/gazelle_manifest/defs.bzl | 31 +++++++-- 6 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 e2e/cases/gazelle-platform-parents-1416/BUILD.bazel create mode 100644 e2e/cases/snapshots/gazelle_platform_parents_1416.gazelle_python.yaml create mode 100644 e2e/cases/snapshots/gazelle_platform_parents_1416.probe_out.txt diff --git a/docs/uv.md b/docs/uv.md index ac8b14f27..6de057026 100644 --- a/docs/uv.md +++ b/docs/uv.md @@ -421,6 +421,13 @@ gazelle_python_manifest( - `include_stub_packages` — Whether to index conventional stub distributions such as `types-requests` and `asyncpg-stubs`. The Gazelle Python extension then adds matching stub dependencies automatically. Defaults to `False`. +- `platform_parents` — Single-element list holding the parent platform for the synthetic + platforms this rule uses to pin each venv's dependency group (Bazel's `platform()` rule + accepts at most one parent). Defaults to `["@platforms//host"]`, which carries only bare + OS and CPU constraints. Set it when that isn't enough: pass your custom `--host_platform` + when hermetic C++ toolchains gate on extra constraints, so sdist builds in the hub can + still resolve a cc toolchain; or pass the target platform when cross-compiling, so wheel + selection follows it instead of snapping back to the host. - `venvs` — List of dependency group names whose wheels should be indexed. Module mappings from all listed dependency groups are merged into a single manifest. diff --git a/e2e/cases/BUILD.bazel b/e2e/cases/BUILD.bazel index f5ebd0dd9..bac80e1a4 100644 --- a/e2e/cases/BUILD.bazel +++ b/e2e/cases/BUILD.bazel @@ -241,5 +241,18 @@ write_source_files( # modules_mapping so generator drift (module additions, stub # synthesis, merge ordering) surfaces as a diff. "snapshots/uv_gazelle_778.gazelle_python.yaml": "//uv-gazelle-778:gazelle_python_manifest", + + # Gazelle manifest built by //gazelle-platform-parents-1416 with a + # caller-supplied `platform_parents` (issue #1416). Pins that the + # manifest builds end-to-end when the macro's synthetic per-venv + # platforms parent onto a custom platform rather than + # `@platforms//host`. + "snapshots/gazelle_platform_parents_1416.gazelle_python.yaml": "//gazelle-platform-parents-1416:gazelle_python_manifest", + # Companion probe: a genrule transitioned onto the macro-generated + # platform, selecting on a constraint only the custom parent carries. + # Pins "custom-parent": if the macro ignores `platform_parents` the + # constraint is dropped and the diff test fails, rather than the + # regression silently building. + "snapshots/gazelle_platform_parents_1416.probe_out.txt": "//gazelle-platform-parents-1416:probe", }, ) diff --git a/e2e/cases/gazelle-platform-parents-1416/BUILD.bazel b/e2e/cases/gazelle-platform-parents-1416/BUILD.bazel new file mode 100644 index 000000000..dd01c2665 --- /dev/null +++ b/e2e/cases/gazelle-platform-parents-1416/BUILD.bazel @@ -0,0 +1,65 @@ +load("@aspect_rules_py//uv:defs.bzl", "gazelle_python_manifest") +load("@bazel_lib//lib:transitions.bzl", "platform_transition_filegroup") + +# Regression test for `gazelle_python_manifest(platform_parents = ...)` +# (issue #1416: the macro's synthetic per-venv platforms were pinned to +# `@platforms//host`, breaking cross-compilation and custom host platforms). +# Reads from the //uv-deps-650 hub, which already declares an `extras` venv. +# +# Documented exception to per-case isolation: this test applies Gazelle to a +# venv from another case (//uv-deps-650), so the cross-case reference is by +# design. +package(default_visibility = ["//:__pkg__"]) + +# A case-local constraint that only `:parent_platform` carries: observing it +# downstream proves the macro-generated platform inherited from +# `:parent_platform`. +constraint_setting(name = "parentage") + +constraint_value( + name = "custom_parent", + constraint_setting = ":parentage", +) + +# Stands in for a user's custom `--host_platform` — e.g. one carrying the extra +# constraints a hermetic C++ toolchain gates on. Parenting it on +# `@platforms//host` keeps the case host-OS-agnostic. +platform( + name = "parent_platform", + constraint_values = [":custom_parent"], + parents = ["@platforms//host"], +) + +gazelle_python_manifest( + name = "gazelle_python_manifest", + hub = "pypi_uv_deps_650", + platform_parents = [":parent_platform"], + venvs = ["extras"], +) + +# The macro generates a synthetic platform named `_{name}_{hub}_{venv}` in +# this package; `:probe` depends on that naming scheme, so a rename inside the +# macro breaks this target with a missing-target analysis error. +# +# The snapshot pins "custom-parent": if the macro ignores `platform_parents`, +# the generated platform loses `:custom_parent`, the select falls through to +# "default-parent", and the snapshot diff test fails. +config_setting( + name = "under_custom_parent", + constraint_values = [":custom_parent"], +) + +genrule( + name = "probe_src", + outs = ["probe_out.txt"], + cmd = select({ + ":under_custom_parent": "echo custom-parent > $@", + "//conditions:default": "echo default-parent > $@", + }), +) + +platform_transition_filegroup( + name = "probe", + srcs = [":probe_src"], + target_platform = ":_gazelle_python_manifest_pypi_uv_deps_650_extras", +) diff --git a/e2e/cases/snapshots/gazelle_platform_parents_1416.gazelle_python.yaml b/e2e/cases/snapshots/gazelle_platform_parents_1416.gazelle_python.yaml new file mode 100644 index 000000000..e4d43618d --- /dev/null +++ b/e2e/cases/snapshots/gazelle_platform_parents_1416.gazelle_python.yaml @@ -0,0 +1,18 @@ +manifest: + modules_mapping: + brotli: brotli + brotlicffi: brotlicffi + build: build + certifi: certifi + cffi: cffi + charset_normalizer: charset_normalizer + colorama: colorama + idna: idna + packaging: packaging + pycparser: pycparser + pyproject_hooks: pyproject_hooks + requests: requests + setuptools: setuptools + urllib3: urllib3 + pip_repository: + name: pypi_uv_deps_650 diff --git a/e2e/cases/snapshots/gazelle_platform_parents_1416.probe_out.txt b/e2e/cases/snapshots/gazelle_platform_parents_1416.probe_out.txt new file mode 100644 index 000000000..a3838b087 --- /dev/null +++ b/e2e/cases/snapshots/gazelle_platform_parents_1416.probe_out.txt @@ -0,0 +1 @@ +custom-parent diff --git a/uv/private/gazelle_manifest/defs.bzl b/uv/private/gazelle_manifest/defs.bzl index 25d0727b9..3186a2f3b 100644 --- a/uv/private/gazelle_manifest/defs.bzl +++ b/uv/private/gazelle_manifest/defs.bzl @@ -70,7 +70,12 @@ _modules_mapping = rule( update = Label(":update.sh") -def gazelle_python_manifest(name, hub, venvs = [], include_stub_packages = False): +def gazelle_python_manifest( + name, + hub, + venvs = [], + include_stub_packages = False, + platform_parents = None): """Generates a Gazelle Python manifest from uv-managed wheels. Args: @@ -79,7 +84,27 @@ def gazelle_python_manifest(name, hub, venvs = [], include_stub_packages = False venvs: Dependency groups whose wheels should be indexed. include_stub_packages: Whether conventional stub distributions should be indexed for Gazelle's automatic stub dependency resolution. + platform_parents: Single-element list holding the parent platform for the + synthetic platforms this macro uses to select each venv's wheels. + (Bazel's `platform()` rule accepts at most one parent; the list shape + mirrors its `parents` attribute.) Defaults to + `[Label("@platforms//host")]`, resolved in rules_py's own repository — + you do not need a `bazel_dep` on `platforms` to use the default. The + host platform carries only OS and CPU constraints; point this at the + platform the wheels should be resolved for when that is not enough: + + - If the build sets a custom `--host_platform` (for example to carry + the constraints hermetic C++ toolchains require), pass that + platform here so sdist builds inside the hub can still resolve a + cc toolchain. + - When cross-compiling, pass the target platform so wheel selection + follows it instead of snapping back to the host. """ + if platform_parents == None: + platform_parents = [Label("@platforms//host")] + if len(platform_parents) != 1: + fail("gazelle_python_manifest: platform_parents must contain exactly one platform label (Bazel's platform() rule accepts at most one parent); got {}".format(platform_parents)) + file = "gazelle_python.yaml" hub = hub.lstrip("@") @@ -88,9 +113,7 @@ def gazelle_python_manifest(name, hub, venvs = [], include_stub_packages = False platform_name = "_{}_{}_{}".format(name, hub, venv) native.platform( name = platform_name, - parents = [ - "@platforms//host", - ], + parents = platform_parents, flags = [ "--@{}//dep_group={}".format(hub, venv), ],