fix: emscripten-wasm32 build fixes for RoboStack/ros-rolling (Asyncify, not real pthreads) - #154
Open
Tobias-Fischer wants to merge 17 commits into
Open
Conversation
…n builds Emscripten 4.0.9 (the SDK version emscripten-forge/recipes currently publishes as emscripten_emscripten-wasm32) has removed the DEMANGLE_SUPPORT link setting entirely: passing it now hard-errors with `emcc: error: invalid command line setting -sDEMANGLE_SUPPORT=1: No longer supported`, rather than being ignored or warned about. This broke every emscripten-wasm32 ament_cmake package build past the first one or two (confirmed hitting it on ros-humble's rcutils, the first real C library in its selected package set) -- the flag was unconditionally injected into CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS, CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS, and CMAKE_EXE_LINKER_FLAGS for every wasm32 shared-library build via this template, both in the live code path and in the dead, already-commented-out per-package branch above it (fixed for consistency in case it's ever re-enabled). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
vinca_pinning.yaml's pinning_overrides are copied verbatim into the rendered conda_build_config.yaml by _overlay() -- but rattler-build's variant config loader does not evaluate v1-style `if: COND then: [...]` mappings as selectors the way recipe.yaml files do; it treats each one as an opaque literal value. Depending on how many if/then entries a key has, this surfaces as either "Could not parse version spec for variant key X: invalid channel" (single entry) or "multiple bracket sections not allowed" / a silent cartesian-product explosion across every branch (multiple entries), since rattler-build sees N distinct raw-dict "values" instead of one selector to resolve. The legacy `- VALUE # [selector]` comment-annotated list form, used throughout the rest of this file (c_compiler_version, cxx_compiler_version, etc.), *is* understood and evaluated lazily per --target-platform by rattler-build itself. Fixed by having _overlay() convert any v1-style if/then/else list entries in an override's value into that legacy form before writing it out, so overrides can be authored with the more readable if/then/else syntax (matching emscripten-forge/recipes' own variant.yaml style) while still producing a conda_build_config.yaml rattler-build actually parses as conditional. Also had to make sure existing `# [selector]` EOL comments on passthrough (non-if/then) list items survive the rebuild: ruamel keeps comments keyed by list position on the *source* CommentedSeq, not on the item value itself, so appending an item into a freshly created CommentedSeq silently drops its comment unless it's explicitly copied across -- confirmed by a first pass of this fix accidentally stripping the selectors off c_compiler/c_compiler_version's existing entries, which briefly turned every platform's compiler into a candidate for every other platform's build. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Flips USE_PTHREADS=0 to 1 (compile and link) for every emscripten-wasm32 ament_cmake build, plus adds compile-time -s USE_PTHREADS=1 globally via CMAKE_PROJECT_INCLUDE so every translation unit gets the atomics/bulk-memory wasm features the linker requires for shared memory. Without real threads, libc++'s condition_variable timed-wait (used by rclcpp's executor / rmw wait-set) never wakes up on its own -- there is no OS thread to run a real blocking wait, so a synchronous C++ wait blocks the single JS thread forever with no way for wall-clock time to advance underneath it. This was blocking any ROS2 wasm demo from running more than one executor iteration. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… for emscripten rattler-build resolves "build:" dependencies against build_platform, not target_platform, so listing the target-arch-named rosidl_default_generators package under build: for emscripten-wasm32 cross builds only works if a native (non-emscripten) copy of that exact package has also been built and published -- which is not generally the case. The host-prefix copy is already sufficient for CMake's find_package() during configure (confirmed: packages using only rosidl_core_generators in host, with no matching build: entry, already build fine), so this extra requirement was both unsatisfiable and unnecessary. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Hardcoding rmw_wasm_cpp as the default RMW for emscripten-wasm32 builds only works for repos that actually build that specific RMW. Read it from VINCA_EMSCRIPTEN_RMW_IMPLEMENTATION instead (falling back to rmw_wasm_cpp unchanged), so other emscripten-wasm32 experiments -- e.g. one building rmw_zenoh_pico instead -- can select their own default without forking this template again. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
STATIC_ROSIDL_TYPESUPPORT_C/CPP were hardcoded to rosidl_typesupport_introspection_c/cpp, which is what rmw_wasm_cpp expects. A micro-ROS-lineage RMW (e.g. rmw_zenoh_pico) instead requires messages' rosidl_typesupport_c dispatch table to register rosidl_typesupport_microxrcedds_c/cpp specifically -- confirmed via a live browser demo that publisher creation fails with "Type support not from this implementation" otherwise, since the dispatch table is filtered down to a single backend at build time and never even considers alternatives at runtime. Purely additive: defaults to the existing introspection backend when the new env vars are unset, so non-zenoh consumers of this template (e.g. ros-humble-emscripten's rmw_wasm_cpp pin) are unaffected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5 tasks
pybind11_add_module() (used by rclpy's _rclpy_pybind11 extension, and any other pybind11-based Python C extension) creates a CMake MODULE-type library, not SHARED -- a distinct target type with its own CMAKE_SHARED_MODULE_CREATE_*_FLAGS variables. Setting only the SHARED ones left every MODULE .so linked without USE_PTHREADS=1: its object files were compiled with atomics support (add_compile_options applies globally) and looked fine individually, but the final linked module was non-shared-memory, causing a load-time "mismatch in shared state of memory" failure alongside the rest of a real-pthreads build. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…pthreads # Conflicts: # vinca/main.py
Tobias-Fischer
added a commit
to Tobias-Fischer/ros-rolling
that referenced
this pull request
Sep 9, 2026
…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>
A message package's *Config.cmake only re-exports find_dependency() calls for what its own package.xml/CMakeLists.txt declares. It has no idea VINCA_EMSCRIPTEN_STATIC_TYPESUPPORT_C/_CPP named an extra typesupport backend, so it never propagates *that* as an exported dependency to its own downstream consumers -- a package that find_package()s only one message package at a time never notices (it already found the backend itself while configuring its own rosidl_generate_interfaces() call), but one that find_package()s several message packages together hits "the target was not found ... A find_package call is missing for an IMPORTED target" the first time a downstream *Export.cmake references rosidl_typesupport_microxrcedds_c(pp)::rosidl_typesupport_microxrcedds_c(pp) without anyone upstream having found it first. Pre-finding it in the same CMAKE_PROJECT_INCLUDE file already used for the pthreads/shared-lib flags (included right after every project() call, so it's in every target's CMake namespace before that project's own find_package() calls run) covers every consumer uniformly instead of needing a patch per affected package. Found via RoboStack/ros-rolling#46's test_msgs, the first package in that closure to find_package() enough message packages together to expose the gap. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This build:-side requirement asks for a *build-platform* (native) build of e.g. ros2-rosidl-default-generators for any emscripten cross build -- but nothing publishes a native build of these "ros2-"-prefixed packages anywhere (they only ever exist as emscripten-wasm32 cross targets), so this dependency was unsatisfiable from a genuinely clean state. The host-prefix copy vinca already adds elsewhere is enough on its own: CMake's find_package() resolves it via -DCMAKE_FIND_ROOT_PATH=$PREFIX regardless of section, and the actual codegen scripts run through the same -DCMAKE_CROSSCOMPILING_EMULATOR=node this whole pipeline already relies on for every other host-resolved build-time tool. Verified: with this and the previous commit's find_package() fix together, the full ~230-package ros-rolling closure (RoboStack/ ros-rolling#46) builds cleanly from a genuinely empty local channel -- no native/build-platform package ever needs to exist. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…erators dep" This reverts commit ff8ed10. Re-verifying this fix from a clean output/ directory surfaced a new, unresolved CMake configure failure in service_msgs ("builtin_interfaces::builtin_interfaces__rosidl_generator_cpp" reported as a missing imported target, despite builtin_interfaces' own exported .cmake file appearing well-formed). The previously-verified pre-find fix (b1960a5) plus the native-bootstrap mirror it was paired with is known-good end-to-end (both rclc and rclpy browser demos, verified against a native zenohd subscriber), so reverting this specific commit keeps the branch at that verified state pending further investigation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Pre-finding VINCA_EMSCRIPTEN_STATIC_TYPESUPPORT_C/_CPP via CMAKE_PROJECT_INCLUDE (b1960a5) fixed downstream consumers missing the plugin package's own CMake target, but for a package that calls rosidl_generate_interfaces() itself, it has a side effect: it makes the override backend "already a target" before that macro's own typesupport discovery runs, which bumps the override's ament_export_targets() entry to the front of the package's own _exported_targets list -- ahead of the generator target its own Export.cmake requires (INTERFACE_LINK_LIBRARIES references <pkg>::<pkg>__rosidl_generator_c(pp)). Every downstream find_package(<pkg>) then fails with "referenced, but are missing: <pkg>::<pkg>__rosidl_generator_c(pp)", reproducible regardless of whether the C or C++ (or both) override is active. Confirmed by inspecting the built package's own ament_cmake_export_targets-extras.cmake: with no override at all, the generator entry always precedes its typesupport entries (their natural, correct order); pre-finding the override moves it to the front instead. Without any pre-find, rosidl_generate_interfaces() still discovers and selects the same override backend on its own (via STATIC_ROSIDL_TYPESUPPORT_C/_CPP, already set unconditionally below), so skipping the pre-find for these packages loses nothing for their own typesupport selection -- confirmed against builtin_interfaces (self export ordering) and service_msgs (consuming the fixed builtin_interfaces while also generating its own interfaces). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ros2-gbp release tags look like "release/jazzy/foo_pkg/1.2.3-1" -- the short raw.githubusercontent.com <owner>/<repo>/<ref>/<path> form has to guess where a slash-containing ref ends and the path begins. That guess is inconsistently cached across CDN edges: the same URL 404s from some vantage points (including GitHub Actions runners, observed repeatedly and reproducibly on RoboStack/ros-jazzy CI) while resolving fine from others. The explicit refs/tags/<name> form removes the ambiguity and resolves reliably everywhere. A commit hash (rev) is already unambiguous and is left as-is. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Real pthreads (enabled in 7133b30/325d0af) fixed rcl/rmw's blocking condition_variable wait, but require wasm --shared-memory, which is viral across every dlopen'd module and deadlocks completely inside single-thread hosts (e.g. JupyterLite's xeus-python kernel never responds again once a pthreads rclpy blocks it -- confirmed by direct reproduction). Asyncify lets the one real blocking call (rmw_zenoh_pico's rmw_wait, via zenoh-pico's WS transport read) cooperatively yield instead, without needing OS threads at all. Follow-up commits patch rmw_zenoh_pico's wait/condition code to actually use this instead of a real condvar. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…h Asyncify
The emscripten-forge toolchain package's own activation script
(activate_emscripten_emscripten-wasm32.sh) sets
EMCC_CFLAGS="... -sSUPPORT_LONGJMP=wasm -fwasm-exceptions" globally, so
every em++/emcc invocation gets native wasm exception-handling by default
regardless of what CMake is told. Binaryen's Asyncify pass hard-crashes
("UNREACHABLE executed ... Asyncify.cpp") on any object using wasm EH
instructions -- confirmed via a real build failure on ros2-rosidl-buffer.
emcc.py only recognizes -fwasm-exceptions to *set* settings.WASM_EXCEPTIONS;
there's no -fno-wasm-exceptions counter-flag it honors, so the only place
this can actually be fixed is by overriding EMCC_CFLAGS itself, dropping
the exception-handling part while keeping the toolchain's own base flags
(EM_FORGE_CFLAGS_BASE).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Tobias-Fischer
added a commit
to Tobias-Fischer/ros2-emscripten-zenoh-demo
that referenced
this pull request
Sep 11, 2026
deploy.yml / build_ros_rolling_closure.sh / demo_env.md described the now-reverted pthreads approach (see RoboStack/ros-rolling#46 and RoboStack/vinca#154): - Drop the "build pthreads python + numpy" CI step entirely -- demo_env now pulls stock python/numpy straight from emscripten-forge-4x, since rcl/rclpy no longer need --shared-memory. Keep only the cross-python_emscripten-wasm32 toolchain package build ros-rolling's own recipes still need (unrelated to pthreads -- a build-time cross-compile tool, not the target python/numpy build), via the fork's dedicated build-cross-python-pkg task. - build_ros_rolling_closure.sh: ros-rolling's own build-emscripten task is now a deterministic two-pass sequence internally, so this script's external "loop sync+build until nothing new appears" is redundant -- simplified to a single build-emscripten call. Also fixes the same compgen -G false-positive bug found in local testing (returns success with zero matches in some shells) by switching to find. - Remove the EMSCRIPTEN_FORGE_OUTPUT env var (dropped from demo_env_build/build.sh already) and the now-deleted rosidl-typesupport-microxrcedds-cpp local patch reference. - Correct the COI-serviceworker comments that attributed the need for cross-origin isolation specifically to xeus-python's pthreads runtime -- xeus-python here was never rebuilt with pthreads. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Asyncify + dynamic linking (MAIN_MODULE/SIDE_MODULE) needs each individual module -- not just the final demo executable -- to declare which of its own outgoing cross-module calls might suspend. Binaryen's Asyncify pass runs once per module at that module's own build time, and a call it wasn't told about gets a hard `unreachable` trap right after the call site instead of letting the callee's state change propagate. Declaring ASYNCIFY_IMPORTS only on the browser_demo link line (matching upstream emscripten examples) satisfies a separate JS-side check but leaves every other module's own wasm-level guard armed. The list here is the closure discovered by running RoboStack/ros-rolling's rclc browser demo end-to-end and adding exactly the name each crash named next, until it published successfully and a real zenohd router confirmed receiving the messages. Applied to every package rather than gated per-package since an unused entry is a harmless no-op. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…eptions Asyncify + runtime dlopen() of a SIDE_MODULE is a confirmed, unresolved upstream Emscripten/Binaryen limitation (emscripten-core/emscripten#13049, #15594; pyodide/pyodide#4087 reports the identical crash against Pyodide's own CPython fork) -- discovered while getting rclpy dlopen()'d into a JupyterLite kernel. The real fix is architectural: rmw_wait's own zero-timeout fast path is already genuinely non-blocking, so callers poll it from a Python asyncio loop instead of relying on Asyncify to cooperatively yield. Also: EMCC_CFLAGS was being overridden (not appended to) to drop -fwasm-exceptions, which is now actively harmful now that Asyncify is gone -- it left every package compiling with neither explicit exception-handling flag, mismatching stock xeus-python's own -fwasm-exceptions default and causing "cannot resolve symbol invoke_i" dynamic-linking errors. Append to the toolchain's own EMCC_CFLAGS instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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 free
to 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.
Update: pthreads reverted, replaced with Asyncify
Everything below this section describes the branch's original approach
(real pthreads,
USE_PTHREADS=1) — since superseded. Real pthreadsrequires WebAssembly
--shared-memory, which is viral across everydlopen'd module and deadlocks completely inside single-thread hosts:confirmed by direct reproduction, a pthreads-enabled
rclpybuild loadedinto JupyterLite's
xeus-pythonkernel (itself not pthreads-enabledupstream) never responds again once it blocks.
Replaced with Asyncify:
vinca/templates/build_ament_cmake.sh.innowsets
-sASYNCIFYglobally instead ofUSE_PTHREADS=1, for everyemscripten-wasm32ament_cmakebuild. This lets the one genuinelyblocking call in the stack (
rmw_zenoh_pico'srmw_wait, viazenoh-pico's transport read) cooperatively yield instead of requiring a
real OS thread — see RoboStack/ros-rolling#46 for the
rmw_zenoh_pico-sidechanges that actually use this. A second small fix was needed alongside
it: the
emscripten-forgetoolchain's own activation script injects-fwasm-exceptionsinto everyem++/emcccall viaEMCC_CFLAGS, whichcrashes binaryen's Asyncify pass outright on any object using wasm
exception-handling instructions —
EMCC_CFLAGSnow gets overridden todrop that flag while keeping the toolchain's own base flags.
This removes the shared-memory requirement from the whole closure, which
is also what was blocking
rclpyfrom working inside JupyterLite in thefirst place —
xeus/xeus-pythonnever need to touch pthreads at all.The
CMAKE_SHARED_MODULE_*/pinning_overrides/VINCA_EMSCRIPTEN_*fixes described below are all still in effect and unrelated to this
pivot.
Summary (original, pthreads-based)
Flips
USE_PTHREADS0→1 (compile and link) for everyemscripten-wasm32ament_cmakebuild inbuild_ament_cmake.sh.in, plus several smaller emscripten-target fixes needed to get there and to support a second, independent emscripten-wasm32 pipeline (ROS 2 rolling +rmw_zenoh_pico, as opposed to the original ROS 2 Humble +rmw_wasm_cppone). All changes are confined to theif [[ $target_platform =~ emscripten.* ]]branch of the template (or are purely additive env-var opt-ins) — no effect on any other platform or on emscripten builds that don't set the new env vars.Built and validated against three full emscripten-wasm32 ROS 2 builds:
rmw_wasm_cpp, running a realrclcpptalker/listener continuously in the browser.rmw_zenoh_pico, running a realrclctalker in the browser that publishes to a nativezenohdrouter over WebSocket, verified end-to-end by an independent native process receiving the message.rmw_zenoh_picobuild running a realrclpytalker (CPython built with--enable-wasm-pthreads) in the browser, also verified end-to-end against a native subscriber — this is what surfaced theCMAKE_SHARED_MODULE_*gap below.Why real pthreads (superseded, see "Update" above)
Without real threads,
libc++'scondition_variabletimed-wait (used byrclcpp's executor and the RMW wait-set machinery) never wakes up on its own — there is no OS thread to run a genuine blocking wait, so a synchronous wait just blocks the single JS thread forever with no way for wall-clock time to advance underneath it. In practice this meant anyrclcpp::spin()-based program executed its first callback and then hung permanently.USE_PTHREADS=1has to be set at both compile and link time for every translation unit (not just the final link) — it bakes in the wasmatomics/bulk-memoryfeatureswasm-ldrequires when producing shared memory. This is applied globally viaadd_compile_options()in the generatedCMAKE_PROJECT_INCLUDEfile, so every package in the tree gets it consistently (mixing a pthread-enabled module with a non-pthread one is a hard ABI-level mismatch — a wasm module's shared-vs-non-shared linear memory is fixed at compile+link time).The other commits
-s DEMANGLE_SUPPORT=1emcc flag, which emscripten 4.0.9 (the SDK version currently published asemscripten_emscripten-wasm32) rejects outright ("No longer supported"), breaking everyemscripten-wasm32ament_cmakebuild past the first one or two._overlay()now converts v1if/then/elseselectors inpinning_overridesvalues into the legacy# [selector]comment form, since rattler-build's variant loader treats the raw if/then mapping as an opaque literal rather than evaluating it.RMW_IMPLEMENTATIONfor emscripten builds configurable viaVINCA_EMSCRIPTEN_RMW_IMPLEMENTATION(falls back to the existingrmw_wasm_cppdefault), so other emscripten-wasm32 pipelines can select their own RMW (e.g.rmw_zenoh_pico) without forking this template.STATIC_ROSIDL_TYPESUPPORT_C/_CPPconfigurable the same way viaVINCA_EMSCRIPTEN_STATIC_TYPESUPPORT_C/_CPP(falls back to the existingrosidl_typesupport_introspection_c/_cppdefault). A micro-ROS-lineage RMW likermw_zenoh_picorequires messages' typesupport dispatch table to registerrosidl_typesupport_microxrcedds_c/_cppspecifically — the previous hardcoded default left that dispatch table with no matching entry, sorcl_publisher_initfailed with "Type support not from this implementation" for every message.CMAKE_SHARED_MODULE_CREATE_C_FLAGS/CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS, alongside the existingCMAKE_SHARED_LIBRARY_CREATE_*_FLAGS. CMake'sMODULElibrary type (whatpybind11_add_module()uses for every Python C extension —rclpy's own_rclpy_pybind11, and every message package'srosidl_generator_pytypesupport accessor) is a distinct target type fromSHAREDand reads its own set of link-flag variables. Setting only theSHAREDones left everyMODULE-type.solinked withoutUSE_PTHREADS=1— its object files were compiled with atomics support fine (add_compile_optionsapplies globally regardless of target type), but the final linked module came out non-shared-memory, which fails to load next to the rest of a pthreads build with a "mismatch in shared state of memory" error. Invisible until something in the tree actually usedpybind11_add_module()— therclc/C-only pipelines never hit it.VINCA_EMSCRIPTEN_STATIC_TYPESUPPORT_C/_CPPoverride for a package that callsrosidl_generate_interfaces()itself (i.e. defines its own messages/services/actions). Pre-finding it viaCMAKE_PROJECT_INCLUDEwas needed so a consumer package'sExport.cmake(referencingrosidl_typesupport_microxrcedds_c(pp)::...) doesn't fail with "a find_package call is missing for an IMPORTED target" — but for a message-generating package, it has a side effect: it makes the override "already a target" before that package's own typesupport discovery runs, which — confirmed by inspecting the built package's ownament_cmake_export_targets-extras.cmake— bumps its typesupport entry to the front of its own_exported_targetslist, ahead of the generator target its ownExport.cmakerequires (INTERFACE_LINK_LIBRARIESreferences<pkg>::<pkg>__rosidl_generator_c(pp)). Every downstreamfind_package(<that package>)then fails with "referenced, but are missing:<pkg>::<pkg>__rosidl_generator_c(pp)", reproducible regardless of whether the C or C++ (or both) override is set. Without any pre-find,rosidl_generate_interfaces()still discovers the same override backend on its own (viaSTATIC_ROSIDL_TYPESUPPORT_C/_CPP, already set unconditionally), so skipping the pre-find for these packages loses nothing for their own typesupport selection. Found and fixed while re-verifying Add emscripten-wasm32 support with rmw_zenoh_pico ros-rolling#46 from a genuinely cleanoutput/directory — it had been masked until then because every message package already built (with an earlier, non-pre-finding vinca commit) predated this pre-find fix.Testing
Re-verified end-to-end from a genuinely clean
output/directory (not just incrementally, on top of already-built packages): full ~230-recipeemscripten-wasm32closure for rolling +rmw_zenoh_picobuilds green, both a realrclcand a realrclpytalker running in the browser, each independently confirmed delivering messages via a nativezenohdrouter's own REST API (not just reading the browser's own console).Companion changes:
rmw_wasm_cpppipeline this PR was built for).rmw_zenoh_picopipeline exercising the newer commits, including therclpyverification above.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