Uh oh!
There was an error while loading. Please reload this page.
Add emscripten-wasm32 support with rmw_zenoh_pico - #46
Open
Tobias-Fischer wants to merge 7 commits into
Open
Conversation
Ports the emscripten-wasm32 build target (proven on ros-humble via Tobias-Fischer/ros-humble, see RoboStack/vinca#154) to rolling, using rmw_zenoh_pico instead of a custom RMW, since zenoh-pico has real upstream WASM/pthreads support. Verified end-to-end: a wasm32 rclc talker running in a browser publishes std_msgs/String over a real WebSocket to a native zenohd router, received by an independent native subscriber. - vinca.yaml/robostack.yaml/patch/dependencies.yaml/pkg_additional_info.yaml: gate the emscripten-wasm32 platform throughout, same pattern as the humble port (if: wasm32 / not wasm32 selectors, per-platform robostack.yaml dicts). - conda_build_config.yaml/vinca_pinning.yaml: emscripten toolchain + pin overrides (compiler, python 3.13, graphviz/libffi pins that only exist on emscripten-forge). - pixi.toml: point vinca at the fork branch backing vinca#154 until merged. - patch/ros-rolling-*.patch: per-package emscripten build fixes (rclpy narrowing/-latomic, rcutils, rosidl-generator-py, spdlog/yaml-cpp vendor, osrf-testing-tools-cpp, rmw_test_fixture_implementation). - patch/ros-rolling-rmw-zenoh-pico.patch, patch/ros-rolling-rosidl-typesupport- microxrcedds-{c,cpp}.patch: build rmw_zenoh_pico and its typesupport backend for wasm32 (upstream PRs open, see below). - extra_recipes/zenoh-pico, extra_recipes/microcdr: new recipes for rmw_zenoh_pico's dependencies (not otherwise packaged for RoboStack). Upstream bug fixes have been submitted separately: - eclipse-zenoh/zenoh-pico#1314 - esol-community/rmw_zenoh_pico#7 - micro-ROS/rosidl_typesupport_microxrcedds#83 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This was referenced Sep 9, 2026
- Bump the vinca pin to 325d0af, which also sets CMAKE_SHARED_MODULE_CREATE_C/CXX_FLAGS (not just the SHARED-library variants) -- CMake's MODULE library type, used by pybind11_add_module() for every Python C extension (rclpy's own _rclpy_pybind11, and each message package's rosidl_generator_py typesupport accessor), was linking without USE_PTHREADS=1 even though its objects compiled with atomics fine, producing a load-time "mismatch in shared state of memory" next to the rest of a pthreads build. Invisible until something used pybind11_add_module() -- found getting a live rclpy demo running. - Add two Emscripten guards to patch/ros-rolling-rcutils.emscripten.patch (upstreamed as ros2/rcutils#591) so rcl_logging_implementation's dlopen-by-name backend selection works: rcutils_get_platform_library_name() had no emscripten case (always "failed to format library name"), and the post-dlopen path resolution took a dlinfo(RTLD_DI_LINKMAP) branch that Emscripten's JS-backed dlopen doesn't support, treating a successful dlopen() as a failure. - Document a known gap (see pixi.toml comment): rosidl_typesupport_ microxrcedds_cpp's codegen doesn't handle service "_Event" messages, so action_msgs/lifecycle_msgs/rcl_interfaces/rosgraph_msgs/statistics_msgs/ type_description_interfaces/micro_ros_msgs need a second, C-only rebuild pass rather than the global STATIC_ROSIDL_TYPESUPPORT_CPP override this task sets -- a real rclpy talker (not just rclc) verified end-to-end through this same rmw_zenoh_pico pipeline surfaced this. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…shot vinca's PR (RoboStack/vinca#154) had a merge conflict against current master -- vinca/main.py has since been split into several modules (configuration.py, pipeline.py, recipes.py, sources.py, etc.) by an unrelated upstream refactor. Merged and verified: none of this branch's actual changes touch main.py in a way the refactor didn't already independently resolve (the one main.py hunk this branch touched -- dropping an unsatisfiable build-time rosidl_default_generators requirement for emscripten cross builds -- turned out to already be gone from the refactored file), and `pixi run generate-recipes-emscripten` against the merged vinca commit still produces the same 229-recipe closure with all of this repo's template customizations (real pthreads, configurable RMW_IMPLEMENTATION/typesupport backend) intact. The refactored vinca requires a newer rosdistro_snapshot.yaml schema (a per-package `dependencies:` list) that the snapshot committed here predates -- regenerated via the existing `create_snapshot` task. As a side effect this also re-syncs every package's pinned tag to current rolling (the prior snapshot was from 2026-08-24); spot-checked several packages and the version bumps are real upstream rolling releases, not the migration itself introducing drift. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ature/emscripten-wasm32-zenoh-pico # Conflicts: # patch/dependencies.yaml # pkg_additional_info.yaml
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…river patch The previous commit's full vinca-snapshot regeneration (needed for the new vinca's dependencies: schema) also silently bumped every package's pinned git tag to whatever's currently on rolling. That's far more blast radius than intended and broke at least one unrelated, already-passing patch (ros-rolling-mujoco-3d-lidar.patch, whose context assumed the older pinned mjtnum.h-vs-mjtype.h header layout). Replaced it with a merge: keep every already-tracked package's original tag/version/url exactly as they were, and only backfill the new dependencies: field from a fresh vinca-snapshot run. Packages genuinely new to rosdistro since the original snapshot (no old entry to preserve) still get their current tag -- one of those, sbg_driver, needed its own patch regenerated against upstream's newer release, which turned out to have already picked up 3 of our patch's 4 hunks itself (ament_target_dependencies -> target_link_libraries modernization, usleep -> sleep_for); only the maybe_unused parameter fix still applied. `pixi run check-patches` (the full non-emscripten closure) now passes clean, and `pixi run generate-recipes-emscripten` still produces the same 229-recipe closure as before. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ature/emscripten-wasm32-zenoh-pico # Conflicts: # conda_build_config.yaml # vinca_pinning.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
emscripten-wasm32as an opt-in target platform for rolling, usingrmw_zenoh_picoas theRMW implementation. This follows the same emscripten-wasm32 port already
proven on humble (Tobias-Fischer/ros-humble, backed by
RoboStack/vinca#154), but
swaps the custom hand-rolled
wasm_cpp/rmw_wasm_cppRMW used there forzenoh-pico, which has real, actively-maintained upstream WASM/pthreadssupport — significantly less custom code to maintain.
All changes are gated by
if: wasm32/if: not wasm32selectors (orper-platform
robostack.yamldicts) and are additive — no behavior changefor existing platforms.
Verified end-to-end, both a C and a Python talker:
rclctalker node running in a real browser publishesstd_msgs/Stringviarmw_zenoh_picoover a real WebSocket to a nativezenohdrouter, received and correctly decoded by an independent nativeprocess.
rclpytalker (CPython built with--enable-wasm-pthreads,including a real, non-stubbed
numpy) running the same way in the samebrowser, also verified against a native subscriber.
Both are genuine cross-process interop, not just "doesn't crash."
What's included
vinca.yaml/robostack.yaml/patch/dependencies.yaml/pkg_additional_info.yaml: thread theemscripten-wasm32platform throughthe recipe pipeline (selectors, per-platform dependency-name mappings,
rmw_implementation/typesupport wiring for the ~230-recipe closure).conda_build_config.yaml/vinca_pinning.yaml: emscripten toolchain pin(emscripten-forge's compiler/SDK) plus targeted pin overrides where
emscripten-forge's package set diverges from conda-forge (python 3.13 vs
3.14, graphviz/libffi versions) — base pins are preserved for every other
platform.
pixi.toml: points thevincapypi dependency atRoboStack/vinca#154's branch
until it merges upstream.
patch/ros-rolling-*.patch: per-package emscripten build fixes (rclpynarrowing warning +
-latomiclink condition, rcutils, rosidl-generator-py,spdlog/yaml-cpp vendor policy, osrf-testing-tools-cpp,
rmw_test_fixture_implementation).
patch/ros-rolling-rmw-zenoh-pico.patch,patch/ros-rolling-rosidl-typesupport-microxrcedds-{c,cpp}.patch: buildrmw_zenoh_picoand its typesupport backend for wasm32. The underlyingfixes have been submitted upstream separately (see below); these patches
apply them to the exact pinned commits until they merge.
extra_recipes/zenoh-pico,extra_recipes/microcdr: new recipes forrmw_zenoh_pico's own dependencies, not otherwise packaged for RoboStack.Upstream PRs
The genuine bugs found along the way (not RoboStack-specific) have been
submitted upstream rather than only patched here:
ament_cmake_ros_core+export-set/
BUILD_INTERFACEfixesrcl_logging_implementation'sdlopen-by-name backend selection (found getting
rclpy's logging initworking)
CMAKE_SHARED_MODULE_CREATE_*_FLAGS, not just theSHARED-libraryvariant. CMake's
MODULElibrary type — whatpybind11_add_module()usesfor every Python C extension, including
rclpy's own_rclpy_pybind11and each message package's
rosidl_generator_pytypesupport accessor —is distinct from
SHAREDand was linking withoutUSE_PTHREADS=1. Neverhit by the
rclc/C-only path; only surfaced once a realrclpydemolinked a pybind11 module into the same pthreads build.
Getting
rclpyrunning: what it actually tookEarlier revisions of this PR reported
rclpyas blocked by CPython forwasm32 not being pthread-enabled. That's fixed, and it turned out to be
one configure flag: CPython upstream has shipped
--enable-wasm-pthreadssince 3.11 (adds exactly
-pthread -sUSE_PTHREADS -sPROXY_TO_PTHREAD, thesame flags this whole pipeline already uses) —
emscripten-forge's ownpythonrecipe just doesn't turn it on. Building a custom variant withthat flag, plus registering
numpy's ~14 eagerly-imported compiledextension modules ahead of time (this build doesn't enable CPython's
--enable-wasm-dynamic-linking— its own docs flag that combination withpthreads as known-crashy — so nothing can rely on Python's normal
importdynamically
dlopen-ing a.so), got a realrclpytalker with realnumpyrunning end-to-end.Two more narrow gaps surfaced along the way, both documented in this repo
rather than silently patched around:
rosidl_typesupport_microxrcedds_cpp's codegen doesn't handle service"_Event" messages — specific to the C++ typesupport variant; the
C variant those same packages (
rcl_interfaces,type_description_interfaces,service_msgs) need forrmw_zenoh_picobuilds fine. See the comment on
pixi.toml'sbuild-emscriptentask.rmw_zenoh_picodoesn't support publisher/subscriber QoS event handlers(
RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOSetc.) orTypeDescriptionService— the former fails with a plain
RCLErrorinstead of theUnsupportedEventTypeErrorrclpyalready handles gracefully, and thelatter hangs rather than erroring. Worked around locally for the demo
(not upstreamed — didn't root-cause the
rmw_zenoh_picoside tonight);worth a closer look if
rclpysupport here gets formalized further.The
rclpydemo itself (build script, patchedrclpy/numpy, browserharness) is local-only exploratory tooling, same as the existing
rclcone — not part of this PR's recipe changes.
Test plan
emscripten-wasm32+ rmw_zenoh_pico.zenohd→independent native subscriber, verified receiving correctly-decoded
messages.
rclpytalker (realnumpy, not stubbed).tooling.
Full write-up
All the changes this required, across every repo, are documented together in Tobias-Fischer/ros2-emscripten-zenoh-demo — including a working
rclcandrclpybrowser demo verified end-to-end against a nativezenohdrouter.🤖 Generated with Claude Code