Add `wasm32-wasip1` target support for `ruby-rbs-sys` (cross-platform bindgen issues)
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 2.2k
- Forks
- 256
- Avg merge
- 6d 17h
- Merged PRs (30d)
- 37
Description
Summary
ruby-rbs-sys 0.3.0's build.rs has no wasm32-wasi* target awareness — there's no way to point cc at a wasi-targeting compiler or propagate the same flags to bindgen. I drafted a build refactor mirroring ruby-prism-sys's pattern (split build.rs into build/main.rs + build/vendored.rs, detect the wasm target, route everything through wasi-sdk). The C compile of vendored librbs to wasm32-wasip1 then succeeds cleanly. However, the bindgen step fails differently across three different libclang implementations, and the failure modes suggest a deeper bindgen-on-wasm issue that may also affect ruby-prism-sys — but isn't surfacing there because its wasm CI doesn't actually run.
This issue documents what I found in case maintainers (or anyone with deeper bindgen-on-wasm experience) can spot the underlying cause. I'm happy to send a PR for the build-refactor portion regardless — that part is mechanical and parallels prism cleanly. The bindgen question is what I'd like guidance on before proceeding.
Goal
Build ruby-rbs (and consumer crates) for wasm32-wasip1 so it can run in browser environments via wasi-libc shims. Used in a downstream project that ingests RBS signatures alongside Ruby source for a transpile-time type-resolution pipeline.
What works after the build refactor
- Vendored librbs C compiles to
wasm32-wasip1cleanly withWASI_SDK_PATHset, using-D_WASI_EMULATED_MMAN=1(defensive<sys/mman.h>includes inrbs_allocator.c; no actual mmap calls, so no link againstlibwasi-emulated-mmanneeded). - Native build path is unaffected; the wasm code is gated on
target.contains("wasm32") && target.contains("wasi"). - Bindgen is invoked with the same
-I,-D, and--sysrootflags asccviaBINDGEN_EXTRA_CLANG_ARGS_<target>propagation — same conventionruby-prism-sysuses.
Three libclangs, three failure modes
| Platform / libclang | Failure |
|---|---|
| macOS, Apple CommandLineTools libclang (default) | All 18 FFI function declarations dropped from generated bindings. bindings.rs contains struct/typedef/enum definitions but no extern "C" blocks for functions. Downstream ruby-rbs then fails with 90 "cannot find function" errors. |
macOS, brew llvm 22 libclang (LIBCLANG_PATH=/opt/homebrew/opt/llvm/lib) |
Functions emit. But 5 typedef-after-forward-declared structs (rbs_ast_symbol, rbs_namespace, rbs_type_name, rbs_types_block, rbs_ast_declarations_class_super) emit as opaque (pub _address: u8) despite the full typedef struct foo { ... } foo_t; definition appearing later in vendor/rbs/include/rbs/ast.h. Bindgen's own layout-test assertions then fail at rustc time (size_of::<rbs_ast_symbol>() - 24usize overflows because actual size is 1, expected size is 24). |
Ubuntu 24.04, distribution libclang (apt install clang libclang-dev) |
Different missing types — at minimum rbs_parser_t is absent, breaking ruby-rbs's codegen-from-config.yml output (pub struct AnnotationNode<'a> { parser: NonNull<rbs_parser_t>, ... }). |
The pattern looks like bindgen handles forward-decl-then-typedef structs differently across libclang versions when running with --target=wasm32-wasip1 --sysroot=<wasi-sysroot>. The native build (no wasm target, no sysroot override) handles them correctly across all three libclangs.
Why prism's pattern alone may not be a sufficient precedent
I initially assumed ruby-prism-sys had wasm support figured out and copying its build pattern would suffice. The build-script pattern transferred cleanly, but the bindgen issues surfaced anyway. On checking: ruby-prism's wasm CI doesn't appear to actually run. The repo has rust/ruby-prism/examples/wasm/{main.rs,run.sh} and the rake cargo:examples task that invokes them, but I don't see a GHA workflow job that exercises the wasm path end-to-end — rust-bindings.yml adds targets: wasm32-wasip1 to the rustup-action but I couldn't find a job that actually runs cargo build --target=wasm32-wasip1 on prism. Maintainers can correct me if I'm reading this wrong.
If that's right, prism may share a similar issue; it just hasn't been forced to surface. If it's wrong, the prism-specific differences (e.g., PRISM_EXPORT_SYMBOLS macro, .size_t_is_usize(true), .layout_tests(true) configuration) are clues for what RBS may need.
What might help
I'm not sure what the right fix is. Plausible directions:
- Disable
layout_testsfor the affected struct names. Workaround for the brew-libclang failure mode; doesn't address the Apple/Ubuntu modes. - Investigate the typedef-after-forward-decl handling — possibly an ordering or
__attribute__((visibility))interaction with wasm targets that bindgen's libclang sees differently across versions. May require a minimal repro filed against rust-bindgen. - Generate bindings on the host platform, vendor them into the crate, skip bindgen entirely for wasm builds. Ugly but unblocks downstream.
If maintainers have a preferred direction, I'd be glad to send a PR. The build-script refactor (the wasm-detection block alone, mirroring prism) is independently useful and could land first.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with build.rs and the proposed build/main.rs and build/vendored.rs split, comparing the referenced ruby-prism-sys pattern. Reproduce the wasm32-wasip1 build with WASI_SDK_PATH and the reported libclang variants, then determine whether bindgen flags or layout handling resolve the generated bindings; done means native builds remain unaffected and wasm consumer crates compile.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ruby, rust, wasm
- Domain
- build-system, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100