Skip to content

pyinstaller: add build-pyinstaller.yml for riscv64 wheels - #601

Merged
luhenry merged 3 commits into
mainfrom
pyinstaller
Aug 27, 2026
Merged

pyinstaller: add build-pyinstaller.yml for riscv64 wheels#601
luhenry merged 3 commits into
mainfrom
pyinstaller

Conversation

@luhenry

@luhenry luhenry commented Aug 27, 2026

Copy link
Copy Markdown
Member

The wheel ships a compiled C bootloader. Upstream publishes manylinux2014 and musllinux wheels for five architectures and no -any wheel, so riscv64 falls back to the sdist and compiles the bootloader locally.

Mirrors release/linux-bootloaders, release/build-wheels and ci.yml.

Differs from upstream

  • musllinux dropped — upstream builds it for x86_64 and aarch64 only.
  • manylinux_2_39_riscv64 wheel tag — manylinux2014 is not defined for riscv64.

Matrix: one py3-none wheel — the bootloader is an executable, not a CPython extension.

Testing

  • tests/requirements-libraries.txt omitted — Qt, numpy, scipy, pandas and matplotlib have no riscv64 wheels; the tests needing them skip themselves.
  • Per-test timeout raised to 1800s — each functional test freezes and runs an application.
  • Seven tests deselected on 3.13/3.14, which get a python-build-standalone interpreter that builds in _ctypes and OpenSSL and ships no Tcl/Tk; they assert on the interpreter build, not the wheel, and fail the same way on x86_64.

License: ✅

Wheel contains Linux-64bit-riscv/run and run_d, both ELF with e_machine == EM_RISCV. Built on cp312; 780 passed, 427 skipped.

PyInstaller publishes py3-none-<platform> wheels whose per-platform content
is the compiled bootloader (PyInstaller/bootloader/<PLATFORM>/{run,run_d}),
built from the C sources in bootloader/src via waf. There is no riscv64
wheel on PyPI, so riscv64 users fall back to the sdist and have to compile
the bootloader themselves.

The workflow mirrors upstream's own release path: release/linux-bootloaders
compiles the bootloader inside a per-architecture container, and
release/build-wheels then runs hatchling with PYI_PLATFORM/PYI_WHEEL_TAG set
for that architecture. PyInstaller already normalises riscv64 to the "riscv"
bootloader directory (PyInstaller/_shared_with_waf.py), so no patch is
needed. manylinux2014 is not defined for riscv64, hence the
manylinux_2_39_riscv64 tag from the image's glibc.

The wheel is interpreter-independent, so it is built once; the test job
mirrors upstream's ubuntu-24.04 CI job (locale-gen plus tests/unit and
tests/functional/test_basic.py, which freezes and runs 114 real
applications through the new bootloader) across cp312/cp313/cp314.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
python-build-standalone links _ctypes into the interpreter from 3.13 on, so
test_extension asserts a BuiltinModule is an Extension, and its Linux builds
ship no libtcl/libtk shared object, so PyInstaller's splash builder cannot
find one. Both reproduce on x86_64 with the same interpreters; 3.12 is
unaffected because its PBS build has _ctypes as a shared module and no
_tkinter at all, so it keeps running all three.
@luhenry

luhenry commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

https://github.com/pyinstaller/pyinstaller/blob/v6.22.2/.github/workflows/test.yml doesn't exist. What workflow did you use as reference?

Upstream's ci.yml runs `pytest tests/unit tests/functional`; the port was
narrowed to tests/functional/test_basic.py. The library-dependent modules
(Qt, numpy, scipy, PIL, gi, django, pywin32) all guard themselves with
importorskip and skip cleanly with only tests/requirements-base.txt
installed, so the rest of the suite runs unmodified.

Four more deselects on the 3.13/3.14 entries, all of them properties of the
python-build-standalone interpreter setup-uv provisions rather than of the
wheel, and all reproducing on aarch64:

  test_binary_vs_data_reclassification.py::test_automatic_reclassification_binary
      copies _ctypes.__file__, which PBS builds into the interpreter
  test_multipackage.py::test_spec_with_multipackage
      its TOC expects a shared libssl beside the frozen app; PBS links
      OpenSSL statically into the interpreter
  test_libraries.py::test_idlelib
  test_libraries.py::test_tkinter_tcl_library_dir
      guarded on can_import_module("tkinter"), which succeeds under PBS while
      the Tcl/Tk shared libraries are absent, so they fail where test_splash's
      stricter tkinter_fully_usable guard skips

Rehearsed in an arm64 ubuntu:24.04 container against a locally built wheel:
773 passed, 421 skipped, 20 xfailed, 1 xpassed, no failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@luhenry

luhenry commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

Sorry — that link was wrong, and test.yml does not exist at any tag. Fixed in the description. The reference is upstream's release/linux-bootloaders (the per-architecture waf --tests all loop), release/build-wheels (the PYI_PLATFORM/PYI_WHEEL_TAG pairs handed to hatchling) and .github/workflows/ci.yml (the locale setup and the pytest invocation). Those three are what the workflow header cites.

While checking that I also noticed the test job only ran tests/functional/test_basic.py where ci.yml runs all of tests/functional. Pushed a commit widening it — the library-dependent modules skip themselves without requirements-libraries.txt, so the rest runs unmodified. Locally that takes the run from 417 to 773 tests.

@luhenry

luhenry commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

CI is green on the widened suite: 780 passed on 3.12, 773 on 3.13, 759 on 3.14 (was 417 each). Publish dry-ran.

@luhenry
luhenry merged commit f563006 into main Aug 27, 2026
10 checks passed
@luhenry
luhenry deleted the pyinstaller branch August 27, 2026 19:37
@luhenry luhenry linked an issue Aug 27, 2026 that may be closed by this pull request
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.

pyinstaller riscv64 support

1 participant