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), ],