Skip to content

ci: add 32-bit Linux and Windows builds - #121

Merged
AloysHF merged 6 commits into
AloysHF:masterfrom
WizzardSK:ci-32bit
Sep 23, 2026
Merged

AloysHF merged 6 commits into
AloysHF:masterfrom
WizzardSK:ci-32bit

Conversation

@WizzardSK

@WizzardSK WizzardSK commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

The buildbot currently gets x86_64 and aarch64 Linux, x86_64 Windows, macOS, iOS/tvOS and all four Android ABIs — the two 32-bit desktop targets are the gap, and RetroArch still ships i686 builds for both platforms. Same change as AloysHF/BBKEmu#19, with the GitHub workflows kept in sync as you asked for there.

GitLab — every job is a one-line extends of the official ci-templates: the two new 32-bit ones use rust-linux-i686.yml and rust-windows-i686.yml, and the existing aarch64 job moves to rust-linux-aarch64.yml too. The file matches AloysHF/BBKEmu after #27, and nothing is installed from the recipe itself; whatever a target needs comes from the rust build image.

GitHub — release.yml and nightly-build.yml carry the same target list, so they get the two targets as well: i686-unknown-linux-gnu on ubuntu-22.04 (with gcc-multilib) and i686-pc-windows-msvc on windows-latest, where MSVC cross-compiles to x86 out of the box. Both entries are marked libretro_only: true and the standalone build/package steps skip them — the standalone frontend would need a full set of 32-bit desktop dev packages on the runner, while the GitLab jobs build the core alone anyway. The release notes mention that.

Built both targets on GitHub runners against the current tree:

  • i686-unknown-linux-gnu → ELF 32-bit LSB shared object, Intel 80386, exporting retro_run
  • i686-pc-windows-msvc → PE32 executable (DLL), Intel i386

The buildbot gets x86_64 and aarch64 Linux, x86_64 Windows, macOS,
iOS/tvOS and all four Android ABIs; the two 32-bit desktop targets are the
gap, and RetroArch still ships i686 builds for both platforms.

Nothing new is needed on GitLab: both rust templates are generic over
TARGET_ARCH and the image already carries gcc-multilib and the 32-bit mingw
toolchain, so each job is its 64-bit sibling with a different target (plus,
on Windows, the linker rustc would otherwise guess wrong).

The GitHub release and nightly workflows carry the same target list, so the
two targets go in there as well. Both are libretro-core-only: the standalone
frontend would need a full set of 32-bit desktop dev packages on the runner,
while the GitLab jobs build the core alone anyway.
The buildbot's mingw (gcc 7.3-win32, from the bionic base image) uses SJLJ
exceptions on 32-bit x86, but rustc's i686-pc-windows-gnu expects DWARF-2
unwinding, so linking the precompiled std fails with undefined references to
_Unwind_Resume and _Unwind_RaiseException. The 64-bit job is unaffected because
it uses SEH.

Compiling std from source with panic=abort drops the unwinding machinery, and
the link goes through unchanged otherwise. It is also the sounder behaviour for
a cdylib: a Rust panic unwinding across the libretro C boundary is undefined.
@WizzardSK

Copy link
Copy Markdown
Contributor Author

Updated with the fix for the GitLab pipeline failure BBKEmu hit after merging (AloysHF/BBKEmu#20 carries the same change there).

The 32-bit Windows job could not link: the buildbot's rust image inherits its mingw from the bionic base image (i686-w64-mingw32-gcc 7.3-win32), which uses SJLJ exceptions on 32-bit x86, while rustc's i686-pc-windows-gnu expects DWARF-2 unwinding — so its libgcc_eh has no _Unwind_Resume and the precompiled std fails to link. The 64-bit job is fine because x86_64 mingw uses SEH, and no newer mingw can be installed in that image (bionic ships only 7.3, and newer Ubuntu/Debian packages need a newer glibc).

The job now compiles std from source with panic=abort, which removes the unwinding machinery entirely:

variables:
  RUSTFLAGS: "-Cpanic=abort"
before_script:
  - rustup toolchain install nightly --component rust-src
  - rustup +nightly target add i686-pc-windows-gnu
script:
  - cargo +nightly build --release --target ${TARGET_ARCH} -Z build-std=std,panic_abort

-Z build-std needs nightly, which the image already carries (its Dockerfile installs rust-src for it). Aborting on panic is also the sounder behaviour for a cdylib — a Rust panic unwinding across the libretro C boundary is undefined behaviour — and none of these cores use catch_unwind.

Verified in a ubuntu:bionic container with that exact mingw: without the change the link fails exactly as on the buildbot, with it the DLL comes out as pei-i386 exporting the full retro_* set. The GitHub Actions workflows are unchanged: their 32-bit Windows target is i686-pc-windows-msvc, which never had this problem.

The build image already ships nightly with rust-src. Reinstalling it makes
rustup replace a component that lives in an image layer, which fails with
"Invalid cross-device link".

The rustup target stays: -Z build-std does not build rsbegin.o/rsend.o,
which come from library/rtstartup and are produced by the compiler's own
bootstrap, so they ship only with the precompiled target. Without it the
windows-gnu link fails with "rsbegin.o: No such file or directory".
@WizzardSK

Copy link
Copy Markdown
Contributor Author

Pushed a third commit to this branch, carrying over what the same change turned out to need in BBKEmu (AloysHF/BBKEmu#22).

Two things were wrong with the before_script here:

  1. rustup toolchain install nightly --component rust-src fails on the buildbot. The image already ships nightly with rust-src, so rustup ends up replacing a component that lives in an image layer and dies with Invalid cross-device link before the build even starts.
  2. The rustup target add must stay. -Z build-std does not build rsbegin.o/rsend.o — they come from library/rtstartup and are produced by the compiler's own bootstrap rather than by cargo (wg-cargo-std-aware#46), so they ship only inside the precompiled target. Without them every *-pc-windows-gnu link fails with rsbegin.o: No such file or directory.

So the before_script is now just:

  before_script:
    - rustup +nightly target add ${TARGET_ARCH}

Only those two object files come from the precompiled target; the std that gets linked is still the one built from source with panic=abort, so the SJLJ-vs-DWARF-2 fix is unaffected.

Verified in a ubuntu:bionic container with the buildbot's mingw (i686-w64-mingw32-gcc (GCC) 7.3-win32 20180312): the core links and the DLL comes out as pei-i386 with the full retro_* export set.

The libretro-build-rust image was rebuilt on 2026-09-21: gcc-multilib is
gone and a gcc 14 i686-linux-gnu cross compiler took its place, and the
nightly it carries now links std against -lsynchronization. Both 32-bit
jobs as written here stopped linking (seen on BBKEmu, which carries the
same jobs).

linux-i686 uses the official rust-linux-i686.yml template, which names
that cross compiler. The multilib-based job now ends in crtbeginS.o being
incompatible with elf32-i386.

windows-i686 switches to llvm-mingw's libunwind, the recipe that is green
on holani-retro against the current image. The nightly build-std job
fails with "cannot find -lsynchronization" on the image's old mingw-w64
runtime, and the official template still hits _Unwind_Resume.
@WizzardSK

Copy link
Copy Markdown
Contributor Author

Pushed a fourth commit: the two 32-bit jobs here are the ones that went red on BBKEmu after libretro-build-rust was rebuilt on 2026-09-21 (details in AloysHF/BBKEmu#27).

  • linux-i686 now uses the official rust-linux-i686.yml template. The image dropped gcc-multilib for a gcc 14 i686-linux-gnu-gcc cross compiler. The multilib job would now fail with crtbeginS.o is incompatible with elf32-i386, while the template was green on BBKEmu 43adb4a.
  • windows-i686 switches from nightly -Z build-std to llvm-mingw's libunwind, used as libgcc_eh, on stable. The rebuilt nightly links std against -lsynchronization, which the image's mingw-w64 runtime does not ship. The official template still ends in _Unwind_Resume. This recipe is green on holani-retro against the current image.

The GitHub Actions part of this PR is unchanged.

rust-windows-i686.yml now links with llvm-mingw, which the rebuilt
libretro-build-rust image carries together with the libgcc stand-ins
and the linker variables, so the job is a one-line extends like the
others.
@WizzardSK

Copy link
Copy Markdown
Contributor Author

Pushed a fifth commit. The 32-bit Windows job is now a one-line extends of the official rust-windows-i686.yml, which links with llvm-mingw. The rebuilt libretro-build-rust image (9d70f681) provides llvm-mingw, the libgcc stand-ins and the linker variables. That's what the previous commit set up inline, so the core no longer needs to. linux-i686 already used the official template. Both 32-bit jobs now match the rest of the file, as requested on AloysHF/BBKEmu#27. The Windows job needs that image to be published first.

The aarch64 job cross-compiled from the x64 image and installed
gcc-aarch64-linux-gnu and the rustup target in before_script. The
rust-linux-aarch64.yml template exists now, so the job is a plain extends
like the others and nothing is installed from the script.
@AloysHF
AloysHF merged commit eab0c96 into AloysHF:master Sep 23, 2026
5 checks passed
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