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/**' - 'vendor/skia-safe-op/**' - 'rust-toolchain.toml' - 'deny.toml' - '.github/workflows/rust-multiplatform.yml' pull_request: paths: - 'Cargo.toml' - 'Cargo.lock' - 'crates/**' - 'vendor/jian/**' - 'vendor/skia-safe-op/**' - '.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 # macos-13 (Intel runners) deprecated; build x86_64-apple-darwin via # cross-compile from Apple Silicon. cargo build/check run; no test # since binary arch ≠ host arch. - label: macos-x86_64 runner: macos-latest target: x86_64-apple-darwin cross: false check_only: true - 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 # Windows ARM64 — cargo cross-compile from x86_64 host (no Win11 # ARM hosted runner GA yet); cargo check only since binary arch # ≠ host arch. - label: windows-aarch64 runner: windows-latest target: aarch64-pc-windows-msvc cross: false check_only: true 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 && matrix.check_only != true run: cargo build --workspace --target ${{ matrix.target }} --release - name: Build examples (host, spec §1.2 acceptance #7) if: matrix.cross == false && matrix.check_only != true run: cargo build --examples --workspace --target ${{ matrix.target }} --release - name: Check (cross-arch host, e.g. macos-x86_64 from Apple Silicon) if: matrix.check_only == true run: cargo check --workspace --target ${{ matrix.target }} - name: Build (cross) if: matrix.cross == true run: cross build --workspace --target ${{ matrix.target }} --release - name: Test (host, Linux) if: matrix.cross == false && runner.os == 'Linux' # Linux GPU smoke + gpu_chrome_stub_composition are now `#[ignore]` # under LINUX_GPU_SKIA_LOADER_TBD (skia-safe Interface::new_native # cannot load GL syms from EGL pbuffer + llvmpipe; needs # `new_load_with(eglGetProcAddress)` loader, deferred to Step 1f or # spec §3.1 mini-patch). All other tests run normally. run: cargo test --workspace --target ${{ matrix.target }} - name: Test (host, macOS / Windows) if: matrix.cross == false && matrix.check_only != true && runner.os != 'Linux' run: cargo test --workspace --target ${{ matrix.target }} - name: Upload openpencil-app binary if: matrix.cross == false && matrix.check_only != true 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 (compile guard) 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 # Step 1b §1.2 wasm32-clean compile guard — without `skia` feature this # is a stub mount (validates host canvas, does not paint). The real # render artifact uses the SAME wasm32-unknown-unknown target plus # `--features skia` (post C-hard.2 lock-in 2026-05-09) and ships # via a separate Phase A job once the CI-side C-hard pipeline # (brew emscripten install + EMSDK + .wasm.a symlink hack + # wasm-bindgen + wasm-opt) lands. Until then this job only proves # the public surface compiles wasm32-clean. - run: cargo build -p openpencil-shell-web --target wasm32-unknown-unknown --release --no-default-features --features web # Artifact upload deliberately removed: the stub `.wasm` from this # path would mislead downstream consumers into thinking the shell can # render. Restore the upload step once the skia-enabled build runs on # this CI target. 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 }}