fix(shell-native): cfg-gate desktop GL stack so iOS/Android cargo check passes
Spec v19 §11 invariant 1 requires shell-native to compile on iOS / Android cargo check, with the `GlContextProvider` trait (invariant 2) importable on every non-wasm target. Previously the desktop GL stack (glutin / winit / skia-safe) was referenced unconditionally in src/, so mobile cargo check broke the moment the Cargo.toml target-gated those deps to macOS / Linux / Windows. This change cfg-gates the desktop-only modules and items so the mobile cargo check builds only the cross-platform surface: - src/lib.rs: gate `backend` + `canvas_view_stub` modules and their re-exports to desktop OS targets; add `EaglProvider` / `AndroidEglProvider` re-exports under `target_os = "ios"` / `"android"`. `GlContextProvider`, `ProviderError`, `ProviderResult` stay always-on (per §11 invariant 2). - src/context/mod.rs: split into a cross-platform trait surface + per-platform provider re-exports; gate `shared` (depends on `skia_safe` + `winit`) to desktop only. - src/context/provider.rs: cfg-gate `GlutinProvider` struct + impls + the `pick_display_api` helper to desktop OS only; localize `CString` / `NonZeroU32` imports inside fn bodies; gate `from_error` to desktop to silence dead_code on mobile (the only caller is `GlutinProvider`). - Cargo.toml: split deps into a cross-platform `cfg(not(wasm32))` block (jian-core + glow + raw-window-handle, all required by the trait signature on every non-wasm target) and a desktop-only block (skia-safe, glutin, glutin-winit, winit, scopeguard, jian-skia, jian-host-desktop). Merges the previously duplicate desktop `[target...]` table headers that cargo rejected. - ci: rust-multiplatform.yml mobile-check job now runs cargo check on shell-native too (per the comment update there). Verification: - cargo check -p openpencil-shell-native --target aarch64-apple-darwin: PASS - cargo check -p openpencil-shell-native --target aarch64-apple-ios: PASS - cargo check -p openpencil-shell-native --target aarch64-linux-android: PASS - cargo check -p openpencil-shell-native --target wasm32-unknown-unknown: FAILS with the spec §1.2 `compile_error!` (intended). - cargo test -p openpencil-shell-native: 14/14 PASS. - cargo clippy -p openpencil-shell-native --all-targets -- -D warnings: clean on macOS, iOS, Android targets. - cargo fmt --check: clean.
This commit is contained in:
parent
e35174ff00
commit
968f1fda88
15
.github/workflows/rust-multiplatform.yml
vendored
15
.github/workflows/rust-multiplatform.yml
vendored
|
|
@ -156,11 +156,12 @@ jobs:
|
|||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: mobile-${{ matrix.target }}
|
||||
# Step 1a kill-spike scope:
|
||||
# - shell-core MUST compile on iOS / Android / WASM (spec §11 mobile invariant 1).
|
||||
# - shell-native is desktop-only until Step 1f wires real EaglProvider /
|
||||
# AndroidEglProvider impls + sdk linking. Mobile cargo check on shell-native
|
||||
# is deferred to Step 1f; the spec §11 contract is verified via shell-core
|
||||
# wasm/ios/android compile + API surface (GlContextProvider trait public,
|
||||
# on_pause cfg(android) surface.take(), TouchForce in ShellEvent — Phase B).
|
||||
# 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 }}
|
||||
|
|
|
|||
|
|
@ -28,47 +28,60 @@ openpencil-shell-core = { path = "../openpencil-shell-core", version = "0.1.0" }
|
|||
# OP runs its own GPU event loop and does not call jian_host_desktop::run (softbuffer
|
||||
# raster present is not needed).
|
||||
#
|
||||
# winit features note: on Linux you MUST explicitly enable `x11` and/or `wayland`,
|
||||
# otherwise `platform_impl/mod.rs` triggers `compile_error!`. macOS / Windows backends
|
||||
# are auto-enabled via cfg(target_os) and need no feature flag. Step 1a runs CI on all
|
||||
# three desktop OSes, so enabling both x11 + wayland Linux backends is sufficient.
|
||||
# Desktop GL stack — target-gated to macOS / Linux / Windows. iOS / Android
|
||||
# pull EaglProvider / AndroidEglProvider stubs (Step 1f) which don't need
|
||||
# glutin / winit / desktop skia-safe gl bindings; spec §11 invariant 1 says
|
||||
# shell-native must compile on mobile cargo check (verified by CI mobile-check
|
||||
# job in rust-multiplatform.yml). winit features note: on Linux you MUST
|
||||
# explicitly enable `x11` and/or `wayland`, otherwise `platform_impl/mod.rs`
|
||||
# triggers `compile_error!`. macOS / Windows backends are auto-enabled via
|
||||
# cfg(target_os) and need no feature flag.
|
||||
# Cross-platform abstraction deps — pulled for ALL non-wasm targets including
|
||||
# iOS / Android. The `GlContextProvider` trait (spec §3.1) references
|
||||
# `glow::Context` in its method signatures and Step 1f Eagl / AndroidEgl
|
||||
# stubs reference `raw_window_handle` for `on_resume`; both must be importable
|
||||
# on mobile per spec §11 invariant 2. `glow` and `raw-window-handle` are
|
||||
# pure-Rust thin bindings — no native build steps on iOS / Android.
|
||||
# `jian-core` is wasm32-clean per P0.5 and platform-neutral on mobile.
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
jian-core = { path = "../../vendor/jian/crates/jian-core", version = "0.0.1" }
|
||||
glow = "0.17.0"
|
||||
raw-window-handle = "0.6.2"
|
||||
|
||||
# Desktop GL stack — target-gated to macOS / Linux / Windows. iOS / Android
|
||||
# pull only the cross-platform `glow` + `raw-window-handle` above for the
|
||||
# `GlContextProvider` trait surface; the actual `GlutinProvider` desktop
|
||||
# implementation, `SharedSkiaContext`, `NativeBackend`, and
|
||||
# `CanvasViewportStub` are cfg-gated out of the mobile build (see
|
||||
# src/lib.rs module-level `#[cfg(...)]`). spec §11 invariant 1 says
|
||||
# shell-native must compile on mobile cargo check (verified by CI
|
||||
# mobile-check job in rust-multiplatform.yml). winit features note: on
|
||||
# Linux you MUST explicitly enable `x11` and/or `wayland`, otherwise
|
||||
# `platform_impl/mod.rs` triggers `compile_error!`. macOS / Windows
|
||||
# backends are auto-enabled via cfg(target_os) and need no feature flag.
|
||||
[target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies]
|
||||
skia-safe = { version = "0.97.0", features = ["gl"] }
|
||||
glutin = "0.32.3"
|
||||
glutin-winit = "0.5.0"
|
||||
glow = "0.17.0"
|
||||
winit = { version = "0.30.13", default-features = false, features = [
|
||||
"x11",
|
||||
"wayland",
|
||||
"wayland-csd-adwaita",
|
||||
"rwh_06",
|
||||
] }
|
||||
raw-window-handle = "0.6.2"
|
||||
scopeguard = "1.2"
|
||||
|
||||
# Jian path deps — both path + version per spec §12.2.
|
||||
# - jian-core: exposes DrawOp / Paint / TextRun / geometry / scene::Color. `shell-core`
|
||||
# also pulls jian-core; shell-native uses it directly so the NativeBackend translation
|
||||
# path can construct `jian_core::render::DrawOp::*` without bouncing through the
|
||||
# shell-core re-export each frame.
|
||||
# - jian-skia: provides SkiaBackend (RenderBackend impl) + skia textlayout (the textlayout
|
||||
# feature pulls ICU + harfbuzz, ~15MB; P0.5 already bumped skia-safe 0.78→0.97 and
|
||||
# added a public draw_on_canvas).
|
||||
jian-core = { path = "../../vendor/jian/crates/jian-core", version = "0.0.1" }
|
||||
jian-skia = { path = "../../vendor/jian/crates/jian-skia", version = "0.0.1", features = [
|
||||
"textlayout",
|
||||
] }
|
||||
|
||||
# jian-host-desktop: target-gated desktop only (Linux/macOS/Windows); not pulled into
|
||||
# android/ios metadata (verified by Task 1 Step 26 boundary check).
|
||||
# - default-features = false: Jian's default features include `run = ["dep:softbuffer"]`
|
||||
# for raster present; OP runs its own GPU event loop and doesn't need softbuffer.
|
||||
# - features = ["textlayout"]: aligns the text path with jian-skia.
|
||||
[target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies]
|
||||
jian-host-desktop = { path = "../../vendor/jian/crates/jian-host-desktop", version = "0.0.1", default-features = false, features = [
|
||||
"textlayout",
|
||||
] }
|
||||
|
||||
# jian-skia + jian-host-desktop are now in the desktop-only `[target...]`
|
||||
# block above (merged to avoid duplicate table headers). Per spec §11 +
|
||||
# §12.3 boundary invariants 2 & 3 they're not pulled into iOS / Android
|
||||
# cargo check (verified by check-jian-boundaries.sh).
|
||||
|
||||
# tracing for span instrumentation across SharedSkiaContext / NativeBackend
|
||||
# (spec §3.3 / §5.2.1; Task 2 Step 15 dictates per-method spans).
|
||||
[dependencies.tracing]
|
||||
|
|
|
|||
|
|
@ -1,13 +1,32 @@
|
|||
//! Shared GL + Skia context module (spec v19 §3).
|
||||
//!
|
||||
//! - [`provider`] — `GlContextProvider` trait + `GlutinProvider` (desktop)
|
||||
//! + iOS / Android stubs (Step 1f).
|
||||
//! - [`provider`] — `GlContextProvider` trait (cross-platform) +
|
||||
//! `GlutinProvider` (desktop) + iOS / Android stubs (Step 1f). The
|
||||
//! trait is exposed on every non-wasm target per spec §11 invariant 2.
|
||||
//! - [`shared`] — `SharedSkiaContext` owning the GL stack +
|
||||
//! `skia_safe::DirectContext` + `skia_safe::Surface`. Frame-scoped
|
||||
//! `with_frame` callback + idempotent teardown.
|
||||
//! `with_frame` callback + idempotent teardown. Desktop-only — the
|
||||
//! skia-safe / jian-skia deps that back it aren't fetched on
|
||||
//! iOS / Android (Cargo.toml target-gates them).
|
||||
|
||||
pub mod provider;
|
||||
pub mod shared;
|
||||
|
||||
pub use provider::{GlContextProvider, GlutinProvider, ProviderError, ProviderResult};
|
||||
// Cross-platform: trait + error types, importable on every non-wasm target.
|
||||
pub use provider::{GlContextProvider, ProviderError, ProviderResult};
|
||||
|
||||
// Per-platform provider implementations.
|
||||
#[cfg(target_os = "android")]
|
||||
pub use provider::AndroidEglProvider;
|
||||
#[cfg(target_os = "ios")]
|
||||
pub use provider::EaglProvider;
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
pub use provider::GlutinProvider;
|
||||
|
||||
// `SharedSkiaContext` is desktop-only — depends on `skia_safe` + `winit`
|
||||
// (cfg-gated dependencies). Step 1f mobile wiring will introduce a mobile
|
||||
// twin (or generalize this one) once iOS / Android providers go from
|
||||
// `unimplemented!()` placeholders to real GL contexts.
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
mod shared;
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
pub use shared::{SharedSkiaContext, SharedSkiaError, SharedSkiaResult, SurfaceConfig};
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@
|
|||
//! exist as compile-time placeholders so the public API surface is frozen
|
||||
//! before Step 1f real implementations land.
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
use std::error::Error;
|
||||
use std::ffi::CString;
|
||||
use std::num::NonZeroU32;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Errors raised by GL context providers.
|
||||
|
|
@ -25,6 +24,11 @@ pub enum ProviderError {
|
|||
}
|
||||
|
||||
impl ProviderError {
|
||||
/// Wrap any `Error` impl into a `ProviderError::Failure`. Used by the
|
||||
/// desktop `GlutinProvider` to convert glutin / glutin-winit errors;
|
||||
/// `cfg(any(...))`-gated to silence `dead_code` on iOS / Android where
|
||||
/// no in-tree caller exists yet (Step 1f mobile providers will use it).
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
pub(crate) fn from_error<E: Error>(err: E) -> Self {
|
||||
Self::Failure(err.to_string())
|
||||
}
|
||||
|
|
@ -98,7 +102,9 @@ pub trait GlContextProvider {
|
|||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────
|
||||
// Desktop: GlutinProvider
|
||||
// Desktop: GlutinProvider (cfg-gated to macOS / Linux / Windows; the
|
||||
// glutin / winit / skia-safe dep stack is desktop-only per spec §11
|
||||
// invariant 1).
|
||||
// ────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Desktop GL provider built on top of `glutin 0.32` + `glutin-winit 0.5`.
|
||||
|
|
@ -106,6 +112,7 @@ pub trait GlContextProvider {
|
|||
/// All non-`Send` glutin handles live in [`Option`]s so `release` can
|
||||
/// drop them in a defined order (surface → context → display) without
|
||||
/// requiring `&mut self` to consume `self`.
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
pub struct GlutinProvider {
|
||||
/// Currently-current context. `None` after `release`.
|
||||
context: Option<glutin::context::PossiblyCurrentContext>,
|
||||
|
|
@ -117,6 +124,7 @@ pub struct GlutinProvider {
|
|||
glow: Arc<glow::Context>,
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
impl GlutinProvider {
|
||||
/// Construct from an existing winit window. Builds a glutin display
|
||||
/// directly from the window's display handle (bypassing the sealed
|
||||
|
|
@ -134,6 +142,7 @@ impl GlutinProvider {
|
|||
use glutin::prelude::*;
|
||||
use glutin_winit::GlWindow;
|
||||
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
||||
use std::ffi::CString;
|
||||
|
||||
let raw_window_handle = window
|
||||
.window_handle()
|
||||
|
|
@ -235,13 +244,7 @@ fn pick_display_api(
|
|||
glutin::display::DisplayApiPreference::Egl
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
|
||||
fn pick_display_api(
|
||||
_raw: raw_window_handle::RawWindowHandle,
|
||||
) -> glutin::display::DisplayApiPreference {
|
||||
glutin::display::DisplayApiPreference::Egl
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
impl GlContextProvider for GlutinProvider {
|
||||
#[tracing::instrument(skip(self))]
|
||||
fn make_current(&mut self) -> ProviderResult<()> {
|
||||
|
|
@ -285,6 +288,7 @@ impl GlContextProvider for GlutinProvider {
|
|||
#[tracing::instrument(skip(self))]
|
||||
fn resize(&mut self, width: u32, height: u32) -> ProviderResult<()> {
|
||||
use glutin::prelude::*;
|
||||
use std::num::NonZeroU32;
|
||||
let ctx = self
|
||||
.context
|
||||
.as_ref()
|
||||
|
|
|
|||
|
|
@ -26,17 +26,44 @@ compile_error!(
|
|||
Use openpencil-shell-web for browser builds (spec v19 §1.2)."
|
||||
);
|
||||
|
||||
pub mod backend;
|
||||
pub mod canvas_view_stub;
|
||||
// Cross-platform context module: re-exports the `GlContextProvider` trait
|
||||
// + `ProviderError` / `ProviderResult` on every (non-wasm) target so spec
|
||||
// §11 invariant 2 holds — mobile callers can name the trait. Internal
|
||||
// cfg-gates select between `GlutinProvider` (desktop), `EaglProvider` (iOS)
|
||||
// and `AndroidEglProvider` (Android), and `SharedSkiaContext` is only
|
||||
// compiled in on desktop where the GL + Skia stack is available.
|
||||
pub mod context;
|
||||
|
||||
// Desktop-only modules — pull `skia_safe` / `jian_skia` / `glutin` types
|
||||
// that aren't fetched on iOS / Android (see Cargo.toml target-gated deps).
|
||||
// Spec §11 invariants 1 & 3: mobile builds compile shell-native without
|
||||
// these modules at all; mobile widget rendering lands in Step 1f.
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
pub mod backend;
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
pub mod canvas_view_stub;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
pub use backend::{to_jian_color, to_jian_rect, NativeBackend};
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
pub use canvas_view_stub::CanvasViewportStub;
|
||||
|
||||
// Cross-platform re-exports — visible on every (non-wasm) target.
|
||||
pub use context::{GlContextProvider, ProviderError, ProviderResult};
|
||||
|
||||
// Desktop-only re-exports.
|
||||
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
|
||||
pub use context::{
|
||||
GlContextProvider, GlutinProvider, ProviderError, ProviderResult, SharedSkiaContext,
|
||||
SharedSkiaError, SharedSkiaResult, SurfaceConfig,
|
||||
GlutinProvider, SharedSkiaContext, SharedSkiaError, SharedSkiaResult, SurfaceConfig,
|
||||
};
|
||||
|
||||
// Mobile stub re-exports — Step 1f real impls; today they're zero-sized
|
||||
// placeholder structs whose `GlContextProvider` impls `unimplemented!()`.
|
||||
#[cfg(target_os = "android")]
|
||||
pub use context::AndroidEglProvider;
|
||||
#[cfg(target_os = "ios")]
|
||||
pub use context::EaglProvider;
|
||||
|
||||
// `placeholder()` from Task 1 was removed by Codex Phase A Gate round 1
|
||||
// NIT 1 — Task 2's full re-export chain (`SharedSkiaContext`,
|
||||
// `NativeBackend`, etc.) already proves the shell-core ↔ shell-native
|
||||
|
|
|
|||
Loading…
Reference in a new issue