openpencil/.github/workflows/rust-multiplatform.yml
Kayshen-X adff3c4351 ci: wrap Linux strict-GPU test in xvfb + force Mesa llvmpipe
Hosted Ubuntu runner has libegl1-mesa-dev installed but no X display, so
`eglInitialize` fails with 'EGL is not initialized, or could not be
initialized, for the specified EGL display connection'.

xvfb gives EGL_DEFAULT_DISPLAY a real X11 connection so eglInitialize
succeeds; LIBGL_ALWAYS_SOFTWARE + llvmpipe + MESA_GL_VERSION_OVERRIDE
forces Mesa software pipe (no GPU on runner). Together these unblock
the EGL pbuffer GPU smoke + chrome+stub composition tests on Linux CI.
2026-05-05 22:27:00 +08:00

179 lines
6.4 KiB
YAML

name: Rust multi-platform build
# Builds the OpenPencil Rust workspace across all supported targets.
# Runs on every push/PR to verify the matrix stays green; release artifacts
# are produced by `rust-release.yml` on tag pushes.
on:
push:
branches: [main]
paths:
- 'Cargo.toml'
- 'Cargo.lock'
- 'crates/**'
- 'vendor/jian/**'
- 'rust-toolchain.toml'
- 'deny.toml'
- '.github/workflows/rust-multiplatform.yml'
pull_request:
paths:
- 'Cargo.toml'
- 'Cargo.lock'
- 'crates/**'
- 'vendor/jian/**'
- '.github/workflows/rust-multiplatform.yml'
workflow_dispatch:
jobs:
desktop:
name: ${{ matrix.label }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- label: macos-aarch64
runner: macos-latest
target: aarch64-apple-darwin
cross: false
- label: macos-x86_64
runner: macos-13
target: x86_64-apple-darwin
cross: false
- label: linux-x86_64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
cross: false
- label: linux-aarch64
runner: ubuntu-latest
target: aarch64-unknown-linux-gnu
cross: true
- label: windows-x86_64
runner: windows-latest
target: x86_64-pc-windows-msvc
cross: false
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: '1.85'
targets: ${{ matrix.target }}
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}
- name: Install Linux GL/EGL prereqs
if: runner.os == 'Linux' && matrix.cross == false
run: |
sudo apt-get update
sudo apt-get install -y \
libxkbcommon-dev libxkbcommon-x11-dev \
libwayland-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
libegl1-mesa-dev libgles2-mesa-dev libgbm-dev mesa-utils \
libfreetype-dev libfontconfig1-dev \
xvfb
- name: Install cross
if: matrix.cross == true
run: cargo install cross --locked --version 0.2.5
- name: Build (host)
if: matrix.cross == false
run: cargo build --workspace --target ${{ matrix.target }} --release
- name: Build (cross)
if: matrix.cross == true
run: cross build --workspace --target ${{ matrix.target }} --release
- name: Test (host, Linux strict-GPU under Xvfb)
if: matrix.cross == false && runner.os == 'Linux'
env:
STEP1A_REQUIRE_GPU: '1'
# Force Mesa software-rendered llvmpipe so EGL pbuffer surfaces work
# on hosted runners without a real GPU. See spec §9.2 + plan v7
# P0 platform gotcha #3 (Linux GPU smoke window-based deferred;
# EGL pbuffer non-ignored).
LIBGL_ALWAYS_SOFTWARE: '1'
GALLIUM_DRIVER: llvmpipe
MESA_GL_VERSION_OVERRIDE: '4.5'
# xvfb-run gives the Mesa EGL provider a real X11 display connection;
# EGL_DEFAULT_DISPLAY then resolves to an initialised display rather
# than the headless DRI fallback that fails `eglInitialize`.
run: xvfb-run -a cargo test --workspace --target ${{ matrix.target }}
- name: Test (host, macOS / Windows)
if: matrix.cross == false && runner.os != 'Linux'
run: cargo test --workspace --target ${{ matrix.target }}
- name: Upload openpencil-app binary
if: matrix.cross == false
uses: actions/upload-artifact@v4
with:
name: openpencil-app-${{ matrix.label }}
path: |
target/${{ matrix.target }}/release/openpencil-app
target/${{ matrix.target }}/release/openpencil-app.exe
if-no-files-found: ignore
retention-days: 14
wasm-web:
name: wasm32-unknown-unknown / openpencil-shell-web
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: '1.85'
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
with:
key: wasm32
- run: cargo build -p openpencil-shell-web --target wasm32-unknown-unknown --release
- name: Upload wasm bundle
uses: actions/upload-artifact@v4
with:
name: openpencil-shell-web-wasm
path: target/wasm32-unknown-unknown/release/openpencil_shell_web.wasm
if-no-files-found: warn
retention-days: 14
mobile-check:
name: ${{ matrix.label }} (cargo check only)
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
# iOS targets need Xcode SDK — macOS runner only.
- label: ios-aarch64
runner: macos-latest
target: aarch64-apple-ios
- label: ios-aarch64-sim
runner: macos-latest
target: aarch64-apple-ios-sim
# Android targets via NDK — Linux runner.
- label: android-aarch64
runner: ubuntu-latest
target: aarch64-linux-android
- label: android-x86_64
runner: ubuntu-latest
target: x86_64-linux-android
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: '1.85'
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
key: mobile-${{ matrix.target }}
# Step 1a spec §11 mobile invariants verify on iOS / Android cargo check:
# - shell-core wasm32/ios/android-clean (no platform deps).
# - shell-native compiles on mobile targets with EaglProvider /
# AndroidEglProvider stubs (`unimplemented!("Step 1f")`); desktop GL
# stack (glutin / winit) is target-gated to desktop in Cargo.toml +
# GlutinProvider source is cfg-gated to desktop OS only. Real SDK
# linking and iOS/Android runtime is Step 1f.
- run: cargo check -p openpencil-shell-core --target ${{ matrix.target }}
- run: cargo check -p openpencil-shell-native --target ${{ matrix.target }}