From a7a23549c2bcb441ed3e9ff686c263953e5e9c6b Mon Sep 17 00:00:00 2001 From: Quin Gillespie Date: Tue, 22 Sep 2026 11:40:47 -0600 Subject: [PATCH 1/4] track prism upstream for the Windows x86, ARM64 and macOS fixes --- crates/prism-sys/prism | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/prism-sys/prism b/crates/prism-sys/prism index 77f3f43..a2d405e 160000 --- a/crates/prism-sys/prism +++ b/crates/prism-sys/prism @@ -1 +1 @@ -Subproject commit 77f3f43fb459f585af00949d7e65390ed60c19d1 +Subproject commit a2d405ec52bd9dd4e1dff1710c6dd772f7613e99 From 5d1659bb61baf318c254ec39c92a7caa9dfbb356 Mon Sep 17 00:00:00 2001 From: Quin Gillespie Date: Tue, 22 Sep 2026 11:40:49 -0600 Subject: [PATCH 2/4] fix static builds missing the macOS frameworks and Linux libraries --- Cargo.lock | 7 +++++++ crates/prism-sys/Cargo.toml | 1 + crates/prism-sys/build.rs | 39 +++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index acdcd59..2af26aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,11 +27,18 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + [[package]] name = "prism-sys" version = "0.1.3" dependencies = [ "cmake", + "pkg-config", ] [[package]] diff --git a/crates/prism-sys/Cargo.toml b/crates/prism-sys/Cargo.toml index 5b4da7e..c7ce95e 100644 --- a/crates/prism-sys/Cargo.toml +++ b/crates/prism-sys/Cargo.toml @@ -16,6 +16,7 @@ workspace = true [build-dependencies] cmake = "0.1.58" +pkg-config = "0.3" [features] static = [] diff --git a/crates/prism-sys/build.rs b/crates/prism-sys/build.rs index d00930d..30466c2 100644 --- a/crates/prism-sys/build.rs +++ b/crates/prism-sys/build.rs @@ -11,6 +11,9 @@ use cmake::Config; /// otherwise it failed with unresolved UI Automation, RPC and Windows Runtime symbols. const WINDOWS_SYSTEM_LIBS: &[&str] = &["ole32", "onecore", "runtimeobject", "uiautomationcore", "rpcrt4", "powrprof"]; +/// The frameworks prism links itself on macOS, in `PrismPlatformApple.cmake`. +const MACOS_FRAMEWORKS: &[&str] = &["Foundation", "AVFoundation", "AppKit", "IOKit", "CoreFoundation"]; + fn main() { println!("cargo:rerun-if-env-changed=PRISM_LIB_DIR"); let static_link = env::var("CARGO_FEATURE_STATIC").is_ok(); @@ -24,6 +27,42 @@ fn main() { if static_link { link_cpp_runtime(); link_windows_import_libs(); + link_macos_frameworks(); + link_pkg_config_modules(); + } +} + +fn link_macos_frameworks() { + if env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("macos") { + return; + } + for framework in MACOS_FRAMEWORKS { + println!("cargo:rustc-link-lib=framework={framework}"); + } +} + +/// Links the pkg-config modules the Linux backends were built against. +/// +/// Which ones depends on what the build found, so the list comes from the +/// `prism-config.cmake` prism installs for its own static consumers. +fn link_pkg_config_modules() { + if env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("linux") { + return; + } + let Some(config) = env::var_os("OUT_DIR").map(PathBuf::from).map(|out| out.join("share/prism/prism-config.cmake")) + else { + return; + }; + let Ok(text) = std::fs::read_to_string(&config) else { + println!("cargo:warning=prism-config.cmake is missing; the Linux backends will not link"); + return; + }; + for line in text.lines().filter(|line| line.trim_start().starts_with("pkg_check_modules(")) { + let Some(module) = line.split('"').nth(1) else { continue }; + let (name, version) = module.split_once(">=").unwrap_or((module, "0")); + if let Err(error) = pkg_config::Config::new().atleast_version(version).probe(name) { + println!("cargo:warning={error}"); + } } } From ea698784f899ada30f2bbd512c48215baad56ede Mon Sep 17 00:00:00 2001 From: Quin Gillespie Date: Tue, 22 Sep 2026 11:40:50 -0600 Subject: [PATCH 3/4] Add CI for every desktop target --- .github/workflows/ci.yml | 59 ++++++++++++++++++++++++++++++++ crates/prismer/build.rs | 7 +++- crates/prismer/tests/platform.rs | 29 ++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml create mode 100644 crates/prismer/tests/platform.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b5e0be1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + test: + name: test (${{ matrix.target }}, ${{ matrix.link }}) + strategy: + fail-fast: false + matrix: + target: + - x86_64-pc-windows-msvc + - aarch64-pc-windows-msvc + - i686-pc-windows-msvc + - aarch64-apple-darwin + - x86_64-apple-darwin + - x86_64-unknown-linux-gnu + - aarch64-unknown-linux-gnu + link: [shared, static] + include: + - target: x86_64-pc-windows-msvc + os: windows-latest + - target: aarch64-pc-windows-msvc + os: windows-11-arm + - target: i686-pc-windows-msvc + os: windows-latest + - target: aarch64-apple-darwin + os: macos-latest + - target: x86_64-apple-darwin + os: macos-15-intel + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + - target: aarch64-unknown-linux-gnu + os: ubuntu-24.04-arm + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + # prism asks for C23, which older CMake cannot map to an MSVC flag. + - uses: lukka/get-cmake@latest + + - name: Install what the Linux backends build against + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y libspeechd-dev libglibmm-2.68-dev + + - name: Test + shell: bash + run: cargo test --target ${{ matrix.target }} ${{ matrix.link == 'static' && '--features static' || '' }} diff --git a/crates/prismer/build.rs b/crates/prismer/build.rs index 563f530..a5b052d 100644 --- a/crates/prismer/build.rs +++ b/crates/prismer/build.rs @@ -2,11 +2,16 @@ //! //! Cargo hands a `links` crate's metadata to its direct dependents only, so //! without this an application depending on prismer could not see what -//! prism-sys published. +//! prism-sys published. prismer's own tests and examples are applications +//! too, and get the same flags an application's build script would set. fn main() { println!("cargo:rerun-if-changed=build.rs"); if let Ok(dlls) = std::env::var("DEP_PRISM_DELAY_LOAD_DLLS") { println!("cargo:delay_load_dlls={dlls}"); + for dll in dlls.split(';').filter(|dll| !dll.is_empty()) { + println!("cargo:rustc-link-arg-tests=/DELAYLOAD:{dll}"); + println!("cargo:rustc-link-arg-examples=/DELAYLOAD:{dll}"); + } } } diff --git a/crates/prismer/tests/platform.rs b/crates/prismer/tests/platform.rs new file mode 100644 index 0000000..ef00d7e --- /dev/null +++ b/crates/prismer/tests/platform.rs @@ -0,0 +1,29 @@ +//! Checks that prism loads and registers the backends this platform should +//! have, which catches a static link that dropped them. + +use prismer::Prism; + +#[cfg(windows)] +const EXPECTED: &[&str] = &["SAPI", "NVDA", "JAWS", "OneCore", "ZoomText"]; +#[cfg(target_os = "macos")] +const EXPECTED: &[&str] = &["AVSpeech", "VoiceOver"]; +#[cfg(target_os = "linux")] +const EXPECTED: &[&str] = &["Speech Dispatcher", "Orca"]; + +#[test] +fn registers_the_platform_backends() { + let prism = Prism::new().expect("prism starts"); + let names: Vec = prism.backend_ids().into_iter().filter_map(|id| prism.backend_name(id)).collect(); + for expected in EXPECTED { + assert!(names.iter().any(|name| name == expected), "{expected} is not registered, only {names:?}"); + } +} + +/// Only these have a voice on a machine with nothing installed. +#[cfg(any(windows, target_os = "macos"))] +#[test] +fn speaks_through_the_best_backend() { + let prism = Prism::new().expect("prism starts"); + let backend = prism.create_best().expect("a backend starts"); + backend.speak("Hello from prismer.", true).expect("speech starts"); +} From 4147cddb6c7081026852b76872915e3d227a3682 Mon Sep 17 00:00:00 2001 From: Quin Gillespie Date: Tue, 22 Sep 2026 12:01:14 -0600 Subject: [PATCH 4/4] fix CI running doctests that cannot find prism --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5e0be1..8a35921 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,4 +56,6 @@ jobs: - name: Test shell: bash - run: cargo test --target ${{ matrix.target }} ${{ matrix.link == 'static' && '--features static' || '' }} + # Skips doctests, since rustdoc links its own binary without the + # delay-load flags or the library search path cargo sets. + run: cargo test --tests --target ${{ matrix.target }} ${{ matrix.link == 'static' && '--features static' || '' }}