Skip to content

ci(uv): run cross-built wheels on native-arch runners - #1507

Merged
jbedard merged 4 commits into
mainfrom
xangcastle/crossbuild-exec-verify-macos
Sep 9, 2026
Merged

jbedard merged 4 commits into
mainfrom
xangcastle/crossbuild-exec-verify-macos

Conversation

@xangcastle

@xangcastle xangcastle commented Aug 29, 2026

Copy link
Copy Markdown
Member

CI and e2e only: no change under uv/; the public API is unchanged.

Native-arch verification (.github/workflows/ci-workflows.yaml): each crossbuild case exports <case>_{amd64,arm64}_bundle tarballs (wheel plus its standalone test_<case>.py, e2e/crossbuild/tools/wheel_export.bzl). Three pipelines cross-build on one runner, upload the bundles, and pip install + run them on a native runner of the target architecture, with no containers and no emulation in the verdict:

  • darwin → linux/amd64 + linux/arm64 (gated to main and *macos* branches),
  • ubuntu/amd64 → ubuntu/arm64,
  • ubuntu/arm64 → ubuntu/amd64.

test-all gates on the two Linux pipelines and accepts skipped for the macOS one outside the gated branches. The bundle-building steps pass --remote_download_outputs=toplevel: --config=ci builds without the bytes, and with a warm cache the tarballs this job uploads were not materialized (the first run of this branch passed only because its cache was cold).

New cases, all built for linux/amd64 and linux/arm64 with collect_wheels (Tag metadata, ELF arch of every bundled .so) plus a native import test:

  • pycross-geohash: C++ extension with no upstream wheel. On a macOS host, test.sh also cross-builds it for macOS amd64 (a manual collect_wheels with check_elf = False; the platform transition always resolves to os:macos, so target_compatible_with cannot tell hosts apart).
  • pycross-msgpack: Cython-generated C extension via setuptools.
  • pycross-numpy: meson-python with config_settings = {"setup-args": ["-Dblas=none", "-Dlapack=none"]} (feat(uv): config_settings on uv.override_package for PEP 517 backends #1524); the hermetic sandbox has no BLAS.
  • pycross-psutil: plain setuptools C extension.
  • pycross-compile_pyc: regression guard for cross-arch whl_install pyc precompilation.

The Rust cases stay out of this PR on purpose: pycross-rust keeps building as on main and gets no export, so the pipelines never depend on the Rust toolchain work in #1526. Once that lands, the Rust cases join the native runs with a one-line pycross_wheel_export each.

The macOS smoke job is removed: the root and e2e/cases suites already run on Linux for every PR, and the crossbuild cases are exercised by the darwin cross-build pipeline above, so it tripled macOS minutes without covering anything new.

collect_wheels gains tags and check_elf for the macOS collection; pycross-setuptools shares its setproctitle wheel label between collect_wheels and the export instead of repeating it.


Changes are visible to end-users: no

Test plan

  • New test cases added
  • cd e2e/crossbuild && bazel test //... && bash test.sh passes locally.
  • CI: the three build/run pipelines plus test-all.

@coderabbitai

coderabbitai Bot commented Aug 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 30fca8df-5295-4056-bf8b-f05c123fa487

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@aspect-workflows

aspect-workflows Bot commented Aug 29, 2026

Copy link
Copy Markdown

✨ Aspect Workflows Tasks

📅 Wed Sep 9 18:43:29 UTC 2026

✅ 44 successful tasks

  • ✅ buildifier · ⏱ 17s · 🐙 GitHub Actions · ☑️ Check
    💬 Format complete (clean)
  • ✅ gazelle · ⏱ 23.8s · 🐙 GitHub Actions · ☑️ Check
    💬 Gazelle complete (clean)
  • ✅ test-e2e-bazel-8 [test] · ⏱ 5m 19s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (324/324 passed)
  • ✅ test-e2e-bazel-9 [test] · ⏱ 7m 5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (314/314 passed)
  • ✅ test-e2e-crossbuild-bazel-8 [test] · ⏱ 6m 21s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (39/39 passed)
  • ✅ test-e2e-crossbuild-bazel-9 [test] · ⏱ 6m 59s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (39/39 passed)
  • ✅ test-e2e-interpreter-build-config-bazel-8 [test] · ⏱ 26.3s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-build-config-bazel-9 [test] · ⏱ 1m 7s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-input-validation-bazel-8 [test] · ⏱ 20.4s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-input-validation-bazel-9 [test] · ⏱ 41s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-runtime-metadata-bazel-8 [test] · ⏱ 26.9s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-e2e-interpreter-runtime-metadata-bazel-9 [test] · ⏱ 37.1s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-e2e-interpreter-toolchain-settings-bazel-8 [test] · ⏱ 23.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-interpreter-toolchain-settings-bazel-9 [test] · ⏱ 49s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-rules-python-interop-bazel-8 [test] · ⏱ 50.2s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (16/16 passed)
  • ✅ test-e2e-rules-python-interop-bazel-9 [test] · ⏱ 2m 48s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (16/16 passed)
  • ✅ test-e2e-rules-python-protobuf-bazel-8 [test] · ⏱ 1m 58s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-rules-python-protobuf-bazel-9 [test] · ⏱ 1m 15s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-e2e-rules-python-provider-compat-bazel-8 [test] · ⏱ 24.3s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (4/4 passed)
  • ✅ test-e2e-rules-python-provider-compat-bazel-9 [test] · ⏱ 38s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (4/4 passed)
  • ✅ test-examples-debugger-bazel-8 [test] · ⏱ 33.6s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-debugger-bazel-9 [test] · ⏱ 43.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-dev_deps-bazel-8 [test] · ⏱ 25.6s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-dev_deps-bazel-9 [test] · ⏱ 1m 45s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-django-bazel-8 [test] · ⏱ 26.6s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-django-bazel-9 [test] · ⏱ 53.8s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-multi_version-bazel-8 [test] · ⏱ 24.7s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-examples-multi_version-bazel-9 [test] · ⏱ 32.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (2/2 passed)
  • ✅ test-examples-protobuf-bazel-8 [test] · ⏱ 1m 40s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-protobuf-bazel-9 [test] · ⏱ 2m 27s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-py_binary-bazel-8 [test] · ⏱ 17.2s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-py_binary-bazel-9 [test] · ⏱ 52.5s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-py_pex_binary-bazel-8 [test] · ⏱ 27.9s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-py_pex_binary-bazel-9 [test] · ⏱ 1m 32s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed · 1 cached)
  • ✅ test-examples-py_venv-bazel-8 [test] · ⏱ 29.8s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (3/3 passed)
  • ✅ test-examples-py_venv-bazel-9 [test] · ⏱ 39.7s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (3/3 passed)
  • ✅ test-examples-pytest-bazel-8 [test] · ⏱ 38s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (11/11 passed)
  • ✅ test-examples-pytest-bazel-9 [test] · ⏱ 1m 12s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (11/11 passed)
  • ✅ test-examples-uv_pip_compile-bazel-8 [test] · ⏱ 26.6s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-uv_pip_compile-bazel-9 [test] · ⏱ 44.9s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-virtual_deps-bazel-8 [test] · ⏱ 21.1s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-examples-virtual_deps-bazel-9 [test] · ⏱ 1m 28s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (1/1 passed)
  • ✅ test-root-bazel-8 [test] · ⏱ 14m 18s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (373/373 passed)
  • ✅ test-root-bazel-9 [test] · ⏱ 16m 12s · 🐙 GitHub Actions · ☑️ Check
    💬 Bazel test complete (372/372 passed)

⏱ Last updated Wed Sep 9 18:59:33 UTC 2026 · 📊 GitHub API quota 0/15,000 (0% used, resets in 1h 0m)
🚀 Powered by Aspect CLI (v2026.28.2)  |  Aspect Build · X · LinkedIn · YouTube

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

py_binary startup benchmark

Version Time (ms) vs BCR vs main Build (s)
BCR 1.11.7 166.7/166.2 ±3.2 1.77
main 59.0/58.8 ±1.3 -64.6% 1.86
PR 57.9/57.9 ±0.9 -65.1% -1.6% 1.66

Time = mean/median ±stddev.
Measured with hyperfine --warmup 5 --runs 50 --shell=none on Linux
Gate: PR vs HEAD main median (threshold: 10%, and must exceed the 2×SE noise floor, here 0.8%). BCR is shown only as a historical baseline.
Build time: cold bazel build //:bench with isolated output base, no disk cache; external repos prefetched so network is excluded.

sys.path quality

Version entries sp roots dupes
BCR 1.11.7 6 1 0
main 7 2 0
PR 7 2 0

sys.path quality measured by bench_syspath inside the assembled venv: sys.path entries, distinct site-packages roots, duplicate realpaths. Duplicates indicate symlink redundancy; many roots suggest an inefficient venv layout.

Bazel analysis benchmark

Version Time (ms) vs BCR vs main Targets Actions Configured (ext) Cfg/target Actions/cfg
BCR 2.0.0-alpha.6 1432/1387 ±259 404 20613 7380 (6675) 18.3 2.79
main 1377/1303 ±289 -6.0% 404 18363 7379 (6674) 18.3 2.49
PR 1435/1365 ±263 -1.6% +4.7% 404 18363 7379 (6674) 18.3 2.49

Measured with hyperfine --warmup 1 --runs 10 on Linux
Time = mean/median ±stddev. Cfg = configured targets; (ext) = the count in external repos (the @pypi hub machinery).
Gate: PR vs HEAD main median (threshold: 10%, and must exceed the 2×SE noise floor, here 18.9%). BCR is shown only as a historical baseline.
Command: warm-server bazel build --nobuild //workspace/..., analysis cache discarded each run via a fresh --action_env value; no disk cache.
Workload: 100 py_test targets — 2 test files/package in file generation mode (one venv per test file); a single dep_group.

py_image_layer benchmark

Scenario Version Time (s) Actions vs BCR vs main
analysis BCR 2.0.0-alpha.6 0.62/0.60 ±0.11 747
analysis main 0.61/0.58 ±0.11 668 -3.2%
analysis PR 0.60/0.57 ±0.11 668 -4.8% -1.6%
1p source BCR 2.0.0-alpha.6 0.65/0.64 ±0.10 4
1p source main 0.62/0.59 ±0.09 5 -8.4%
1p source PR 0.62/0.61 ±0.11 5 -5.1% +3.7%
3p wheel BCR 2.0.0-alpha.6 0.72/0.72 ±0.07 5
3p wheel main 0.74/0.73 ±0.07 6 +2.2%
3p wheel PR 0.75/0.74 ±0.08 6 +3.1% +0.8%

Time = mean/median ±stddev.
Measured with hyperfine on Linux, building //workspace:image_layers (10 binaries, ~30-wheel dep pool, grouped first-party/pip/interpreter tier) with isolated output base, no disk cache.
Scenarios: analysis = warm-server bazel build --nobuild, re-analyzed each run via a fresh --action_env value; incrementals run against a built state with warm analysis: source = append to the last package's lib.py, wheel = rewrite click post_install_patches content.
Actions: for Analysis, the total action count behind the image target from aquery deps(...); for incrementals, actions re-executed for the mutation, from a single instrumented run's BEP build metrics (deterministic; per-mnemonic breakdown in the *-actions.json artifacts). Informational only, not gated.
Gate: PR vs HEAD main median per scenario (threshold: 10%, and must exceed the 2×SE noise floor). BCR is shown only as a historical baseline.

@xangcastle
xangcastle requested a review from jbedard August 31, 2026 13:06
@xangcastle

Copy link
Copy Markdown
Member Author

@codex

@xangcastle
xangcastle marked this pull request as ready for review August 31, 2026 13:11

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ba3f3ec85

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci-workflows.yaml Outdated
Comment thread uv/private/pep517_whl/tools/build_helper.py Outdated
Comment thread e2e/crossbuild/pycross-bcrypt/setup.MODULE.bazel Outdated
Comment thread uv/private/pep517_whl/tools/build_helper.py Outdated
Comment thread .github/workflows/ci-workflows.yaml
Comment thread .github/workflows/ci-workflows.yaml Outdated
@xangcastle
xangcastle force-pushed the xangcastle/crossbuild-exec-verify-macos branch from 5ba3f3e to 81175ed Compare September 9, 2026 02:47
@xangcastle
xangcastle changed the base branch from main to xangcastle/pep517-rust-toolchain September 9, 2026 02:47
@xangcastle
xangcastle force-pushed the xangcastle/crossbuild-exec-verify-macos branch from 81175ed to bda720b Compare September 9, 2026 02:51
@xangcastle
xangcastle force-pushed the xangcastle/pep517-rust-toolchain branch from 454282e to b2164a9 Compare September 9, 2026 13:19
Each non-Rust crossbuild case exports <case>_{amd64,arm64}_bundle tarballs
(wheel plus its standalone test); CI cross-builds them on one runner,
uploads them, and pip-installs and runs them on a native runner of the
target architecture. Three pipelines: darwin -> linux/amd64+arm64 (gated to
main and *macos* branches), amd64 -> arm64, arm64 -> amd64. test-all gates
on the Linux pair. The bundle steps pass --remote_download_outputs=toplevel:
--config=ci builds without the bytes, and a warm cache otherwise leaves the
tarballs this job uploads undownloaded.

New cases, all cross-built for linux/amd64 and linux/arm64 and imported
natively: pycross-geohash (C++ with no upstream wheel, plus a manual macOS
amd64 cross collection driven by test.sh on macOS hosts), pycross-msgpack
(Cython via setuptools), pycross-numpy (meson-python with config_settings
for no BLAS), pycross-psutil, and pycross-compile_pyc (cross-arch pyc
precompilation regression guard). collect_wheels gains tags and check_elf
for the macOS collection; pycross-setuptools shares its setproctitle wheel
label between collect_wheels and the export.

pycross-rust keeps building as before and exports nothing, so the pipelines
do not depend on the Rust toolchain work. No change under uv/.
@xangcastle
xangcastle force-pushed the xangcastle/crossbuild-exec-verify-macos branch from bda720b to ffdedc6 Compare September 9, 2026 13:37
@xangcastle
xangcastle changed the base branch from xangcastle/pep517-rust-toolchain to main September 9, 2026 13:37
@xangcastle xangcastle closed this Sep 9, 2026
@xangcastle xangcastle reopened this Sep 9, 2026
The root and e2e/cases suites already run on Linux for every PR, and the
crossbuild cases are exercised by the darwin cross-build pipeline. The job
tripled macOS minutes without covering anything new.
test-all iterates over toJSON(needs) instead of spelling each job out, so
a job added to needs cannot be forgotten by the gate. The darwin run job
may be skipped only where the darwin pipeline is gated off; on main and
*macos* branches a skipped run means its upstream build failed or was
cancelled, and fails the gate.
@xangcastle
xangcastle requested a review from jbedard September 9, 2026 14:40
…-exec-verify-macos

# Conflicts:
#	e2e/README.md
@xangcastle
xangcastle enabled auto-merge (squash) September 9, 2026 18:44
@jbedard
jbedard disabled auto-merge September 9, 2026 20:10
@jbedard
jbedard merged commit c978b80 into main Sep 9, 2026
104 checks passed
@jbedard
jbedard deleted the xangcastle/crossbuild-exec-verify-macos branch September 9, 2026 20:10
xangcastle added a commit that referenced this pull request Sep 9, 2026
pycross-rust, pycross-bcrypt and pycross-tiktoken get the pycross_wheel_export
from #1507, so CI installs and runs their cross-built wheels on native amd64
and arm64 runners like every other crossbuild case.
xangcastle added a commit that referenced this pull request Sep 10, 2026
pycross-rust, pycross-rust-rs and pycross-tiktoken get the pycross_wheel_export
from #1507, so CI installs and runs their cross-built wheels on native amd64
and arm64 runners like every other crossbuild case. The rules_rs case's test
file takes the bundle's name so the runner finds it.
xangcastle added a commit that referenced this pull request Sep 10, 2026
pycross-rust, pycross-rust-rs and pycross-tiktoken get the pycross_wheel_export
from #1507, so CI installs and runs their cross-built wheels on native amd64
and arm64 runners like every other crossbuild case. The rules_rs case's test
file takes the bundle's name so the runner finds it.
xangcastle added a commit that referenced this pull request Sep 10, 2026
pycross-rust, pycross-rust-rs and pycross-tiktoken get the pycross_wheel_export
from #1507, so CI installs and runs their cross-built wheels on native amd64
and arm64 runners like every other crossbuild case. The rules_rs case's test
file takes the bundle's name so the runner finds it. The crossbuild README
lists the Rust cases alongside the other backends.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants