Skip to content

Stop linking the Python bindings library into C++ consumers of interface packages (fixes dyld _PyExc_RuntimeError / ros-kilted#76) - #51

Merged
traversaro merged 1 commit into
RoboStack:codex/cross-distro-syncfrom
mini-1235:fix-rosidl-generator-py-python-lib
Sep 28, 2026
Merged

traversaro merged 1 commit into
RoboStack:codex/cross-distro-syncfrom
mini-1235:fix-rosidl-generator-py-python-lib

Conversation

@mini-1235

@mini-1235 mini-1235 commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Problem

rosidl_generator_py exports each interface package's Python C bindings library, lib<pkg>__rosidl_generator_py, in <pkg>_TARGETS (through ament_export_targets). So a plain C++ node doing target_link_libraries(node ${std_msgs_TARGETS}) links it. That library uses CPython symbols and is only meant to be loaded by the Python extension modules:

  • macOS: the C++ node aborts at startup with
    dyld[...]: symbol not found in flat namespace '_PyExc_RuntimeError'
    
    This used to be hidden by -Wl,-dead_strip_dylibs from the clang_osx-* compiler activation. Environments without it (e.g. compilers/cxx-compiler 2.x, which set no LDFLAGS) hit it.
  • Linux with Python3::Module: undefined references at link time, or symbol lookup error at load time (Static linking issue with _rosidl_generator_py.so in build _17 ros-kilted#76). Linking Python3::Python hides it, but then every C++ node loads libpython. Ubuntu's GCC passes --as-needed by default, which also hides it.

Root cause

As @traversaro found, upstream intended a separate <pkg>_TARGETS__rosidl_generator_py list for exactly this: ros2/rosidl_python#149 introduced rosidl_generator_py_suffix for it. ros2/rosidl_python#140 accidentally removed the set(rosidl_generator_py_suffix ...) while keeping its uses, so they silently fell back to <pkg>_TARGETS. In addition, ament_export_targets() always appends exported targets to <pkg>_TARGETS, so the library leaked there even with the suffix set.

Fix

This PR targets the full rebuild in #41, which already contains an earlier version of this work (the patch that compiled the Python bindings into the extension modules). It replaces that ros-rolling-rosidl-generator-py.patch with a smaller, CMake-only one (+24/−3 against upstream), closer to upstream's original design:

  1. Restore set(rosidl_generator_py_suffix "__rosidl_generator_py"). The existing dependency loop then links other interface packages' Python bindings through <pkg>_TARGETS__rosidl_generator_py again.
  2. Export the library without ament_export_targets(), which always appends exported targets to <pkg>_TARGETS: install the export set and include it from a small config extras file (creating the imported target), and keep the existing rosidl_export_typesupport_targets() call, which then adds it to <pkg>_TARGETS__rosidl_generator_py only. (Upstream, a cleaner option would be an EXCLUDE_FROM_PACKAGE_TARGETS option for ament_export_targets(), as suggested in review; this PR avoids requiring an ament_cmake change.)
  3. Link Python3::Module instead of Python3::Python on all platforms, since the library is only linked by Python extension modules. (rosidl_generator_py_generate_interfaces: link Python3::Module instead of Python3::Python ros2/rosidl_python#253's Linux CI failure came from the global --no-undefined that rosidl_generator_rs used to set, removed in fix(rosidl_generator_rs_generate_interfaces): Remove poisoning of global CMAKE_SHARED_LINKER_FLAGS variable ros2-rust/rosidl_rust#22.)

Changes (one commit on top of #41)

Note for #41: its commit 80acc94 ("[DO NOT MERGE] CI: drop cached std_msgs build 26 …") came from the earlier version of this PR and should be dropped before merging.

Results

  • Against main (earlier revision of this PR): the regression test failed on macOS with dyld: symbol not found in flat namespace '_PyExc_RuntimeError' before the patch (run), and the patch was validated locally with rattler-build on osx-arm64 and linux-64 (both tests pass; std_msgs_TARGETS no longer lists the Python library).
  • Plain ros:rolling container (Ubuntu 26.04, apt, GCC 15) with the same change on upstream rosidl_python: 8 interface packages rebuilt, the C++ consumer doesn't link the Python library, 134/134 Python round-trips pass.

Notes


This PR description was AI-generated with Claude Opus 5.5 (claude-opus-5-5).

🤖 Generated with Claude Code

@mini-1235
mini-1235 force-pushed the fix-rosidl-generator-py-python-lib branch from a084fe4 to 2ed71d4 Compare September 23, 2026 16:26
@mini-1235

mini-1235 commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Macos failing with:

 -- Build files have been written to: $SRC_DIR/build
 │ [1/2] Building CXX object CMakeFiles/std_msgs_cpp_consumer.dir/main.cpp.o
 │ [2/2] Linking CXX executable std_msgs_cpp_consumer
 │ ld: warning: dylib ($PREFIX/lib/libstd_msgs__rosidl_typesupport_fastrtps_cpp.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libstd_msgs__rosidl_typesupport_introspection_c.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libstd_msgs__rosidl_typesupport_introspection_cpp.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libstd_msgs__rosidl_typesupport_cpp.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libstd_msgs__rosidl_typesupport_fastrtps_c.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libstd_msgs__rosidl_generator_py.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libstd_msgs__rosidl_typesupport_c.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libstd_msgs__rosidl_generator_c.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libbuiltin_interfaces__rosidl_typesupport_fastrtps_cpp.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libbuiltin_interfaces__rosidl_typesupport_fastrtps_c.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/librosidl_typesupport_fastrtps_cpp.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/librosidl_typesupport_fastrtps_c.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libfastcdr.2.3.6.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/librmw.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/librosidl_dynamic_typesupport.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libbuiltin_interfaces__rosidl_typesupport_introspection_cpp.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/librosidl_typesupport_introspection_cpp.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libbuiltin_interfaces__rosidl_typesupport_introspection_c.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/librosidl_typesupport_introspection_c.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libbuiltin_interfaces__rosidl_generator_py.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libbuiltin_interfaces__rosidl_typesupport_cpp.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libbuiltin_interfaces__rosidl_typesupport_c.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/libbuiltin_interfaces__rosidl_generator_c.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/librosidl_runtime_c.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/librcutils.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ ld: warning: dylib ($PREFIX/lib/librosidl_buffer.dylib) was built for newer macOS version (14.0) than being linked (11.0)
 │ dyld[5227]: symbol not found in flat namespace '_PyExc_RuntimeError'
 │ $SRC_DIR/conda_build.sh: line 11:  5227 Abort trap: 6           ./build/std_msgs_cpp_consumer
 │ × error Script failed with status 134
 │ × error 
 │ × error Script execution failed.
 │ × error 
 │ × error   Work directory: /Users/runner/work/ros-rolling/ros-rolling/output/test/test_ros2-std-msgsdiVrKB/test_run_env/etc/conda/test-files/ros2-std-msgs/0
 │ × error   Prefix: /Users/runner/work/ros-rolling/ros-rolling/output/test/test_ros2-std-msgsdiVrKB/test_run_env
 │ × error   Build prefix: /Users/runner/work/ros-rolling/ros-rolling/output/test/test_ros2-std-msgsdiVrKB/test_build_env
 │ × error 
 │ × error To run the script manually, use the following command:
 │ × error 
 │ × error   cd "/Users/runner/work/ros-rolling/ros-rolling/output/test/test_ros2-std-msgsdiVrKB/test_run_env/etc/conda/test-files/ros2-std-msgs/0" && ./conda_build.sh
 │ × error 
 │ × error To run commands interactively in the build environment:
 │ × error 
 │ × error   cd "/Users/runner/work/ros-rolling/ros-rolling/output/test/test_ros2-std-msgsdiVrKB/test_run_env/etc/conda/test-files/ros2-std-msgs/0" && source build_env.sh
 │
 ╰─────────────────── (took 8 seconds)
Error:   × Test failed: failed to run test: IO Error: Script failed

After adding the tests (1st commit)

@mini-1235
mini-1235 force-pushed the fix-rosidl-generator-py-python-lib branch from 73971ba to 7d891c3 Compare September 23, 2026 16:53
@mini-1235

Copy link
Copy Markdown
Contributor Author

All green after adding the patch (2nd commit)

@mini-1235

mini-1235 commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor Author

@traversaro I added a few tests related to RoboStack/ros-kilted#76, ros2/rosidl_python#253, and a longstanding issue I have encountered when developing ROS 2 on macOS.

Let me first describe the macOS issue.

So far I have been using ROS Humble, Lyrical, and Rolling on macOS for ROS 2 development, for example compiling Nav2 from source and working on personal projects. Across all of these distros, whenever I start a new project I eventually run into errors involving symbols such as _PyExc_RuntimeError, similar to what can be seen in CI after the first commit in this PR.

My workaround has usually been to add something like this where needed:

component_container_env = {}
if sys.platform == 'darwin':
    python_library = f'libpython{sysconfig.get_python_version()}.dylib'
    component_container_env['DYLD_INSERT_LIBRARIES'] = os.path.join(
        sys.prefix, 'lib', python_library
    )

I believe something similar could also be added through the Pixi activation environment, but either way this feels more like a workaround than a good development experience.

Based on my investigation, this problem does not normally show up in the RoboStack prebuilt binaries. The reason seems to be that those binaries are built in an environment where the linker drops unused libraries, so the Python bindings library does not remain as a runtime dependency.

A more detailed explanation from my agent is:

Why the prebuilt binaries are clean:

CMake does add the Python library to the link. When nav2 was built, target_link_libraries(... ${geometry_msgs_TARGETS}) included libgeometry_msgs__rosidl_generator_py, just like in your own build.

The linker then drops it. RoboStack pins clang 19 (c_compiler_version: 19 in conda_build_config.yaml), and that compiler's activation exports LDFLAGS containing:

macOS: -Wl,-dead_strip_dylibs, which removes linked dylibs nothing uses. A C++ node never calls the Python conversion functions, so the library is dropped.

Linux: -Wl,--as-needed, which does the same for .so files.

So the published binary has no reference to the Python library. dyld never loads it, and the unresolved _PyExc_RuntimeError never matters.

Why your own build crashed: your project uses compilers >=2 (clang 21). The newer activation no longer exports LDFLAGS or CMAKE_ARGS, so nothing strips the unused library. Your executable keeps a load command for libstd_msgs__rosidl_generator_py.dylib, and dyld aborts. We showed this earlier: with compilers 1.x LDFLAGS contained -dead_strip_dylibs and the executable linked 2 dylibs. With 2.x LDFLAGS was empty, the executable linked 28 dylibs including the Python one, and adding -Wl,-dead_strip_dylibs back fixed it.

So you'll see the error when you:

  • build C++ code against ROS messages yourself, and

  • your toolchain doesn't strip unused libraries: compilers 2.x, plain Xcode clang, or Linux without --as-needed.

On Linux with Python3::Module that gives kilted #76's link errors.

After investigating this, I ended up with two possible fixes.

The larger fix is the one currently proposed in this PR. It makes the generated conversion code part of the Python extension directly and performs cross-package converter lookup at runtime through the Python message classes. This also means everything uses Python3::Module.

A smaller alternative would be:

diff --git a/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake b/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake
index 2810984..f815eab 100644
--- a/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake
+++ b/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake
@@ -38,6 +38,11 @@ if(NOT TARGET Python3::Module OR NOT TARGET Python3::NumPy)
   find_package(Python3 REQUIRED COMPONENTS Interpreter Development NumPy)
 endif()
 
+# The Python C bindings library of an interface package is exported in
+# <pkg>_TARGETS__rosidl_generator_py instead of <pkg>_TARGETS, so that C/C++
+# consumers of the interface package don't link it.
+set(rosidl_generator_py_suffix "__rosidl_generator_py")
+
 # Get a list of typesupport implementations from valid rmw implementations.
 rosidl_generator_py_get_typesupports(_typesupport_impls)
 
@@ -165,10 +170,18 @@ add_dependencies(
   ${rosidl_generate_interfaces_TARGET}__rosidl_typesupport_c
 )
 
+# On macOS, link Python3::Module (-undefined dynamic_lookup): linking libpython
+# would load a second interpreter into a python executable that links it
+# statically (e.g. conda-forge), which crashes.
+if(APPLE)
+  set(_python_target Python3::Module)
+else()
+  set(_python_target Python3::Python)
+endif()
 target_link_libraries(
   ${_target_name_lib} PRIVATE
   Python3::NumPy
-  Python3::Python
+  ${_python_target}
 )
 target_include_directories(${_target_name_lib}
   PRIVATE
@@ -260,9 +273,20 @@ if(NOT rosidl_generate_interfaces_SKIP_INSTALL)
     LIBRARY DESTINATION lib
     RUNTIME DESTINATION bin)
 
-  # Export this target so downstream interface packages can depend on it
-  rosidl_export_typesupport_targets("${rosidl_generator_py_suffix}" "${_target_name_lib}")
-  ament_export_targets(export_${_target_name_lib})
+  # Export this target so downstream interface packages can depend on it.
+  # Not with ament_export_targets(), which would add it to <pkg>_TARGETS.
+  install(
+    EXPORT export_${_target_name_lib}
+    DESTINATION share/${PROJECT_NAME}/cmake
+    NAMESPACE "${PROJECT_NAME}::"
+    FILE "export_${_target_name_lib}Export.cmake")
+  set(_py_extras_file
+    "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_py/${_target_name_lib}-extras.cmake")
+  file(WRITE "${_py_extras_file}"
+    "include(\"\${${PROJECT_NAME}_DIR}/export_${_target_name_lib}Export.cmake\")\n"
+    "list(APPEND ${PROJECT_NAME}_TARGETS${rosidl_generator_py_suffix}\n"
+    "  \"${PROJECT_NAME}::${_target_name_lib}\")\n")
+  list(APPEND ${PROJECT_NAME}_CONFIG_EXTRAS "${_py_extras_file}")
 endif()
 
 if(BUILD_TESTING AND rosidl_generate_interfaces_ADD_LINTER_TESTS)

I tested this smaller version as well, and it appears to work.

The main difference is:

  • In the larger fix currently proposed in this PR, the generated Python conversion code is treated as part of the Python extension and uses Python3::Module everywhere.
  • In the smaller fix, the existing shared-library design is kept, but the Python bindings target is separated from the normal _TARGETS list so C/C++ consumers do not link it accidentally. It uses Python3::Module on macOS and keeps Python3::Python on other platforms.

Since I do not have much experience with Empy and the code-generation side of rosidl_generator_py, I would appreciate it if you could take a look before I spend more time validating them. In particular, I would be interested to know which of these two directions you would prefer

@traversaro

Copy link
Copy Markdown
Member

Thanks a lot for the clear tests and PR description, finally I fully understand the problem behind ros2/rosidl_python#253 . It is a bit late now here in Europe, I will post my thought on this tomorrow. Interestingly, I think the problem is quite similar to PixarAnimationStudios/OpenUSD#3577 .

Tobias-Fischer pushed a commit that referenced this pull request Sep 23, 2026
…on bindings

Every interface package exports its Python C bindings library
(lib<pkg>__rosidl_generator_py) in <pkg>_TARGETS, so plain C++ consumers
link it. That library has unresolved CPython symbols, so the consumer
aborts at startup on macOS ("symbol not found in flat namespace
'_PyExc_RuntimeError'") and fails to link or load on Linux when the
library links Python3::Module (RoboStack/ros-kilted#76).

The test builds a C++ executable against ${std_msgs_TARGETS} and runs it
(unix only), and checks that the Python bindings still work from Python.
std_msgs is bumped to build 26 so CI rebuilds it and runs the test.

This commit is expected to fail the new test on macOS. On Linux the
library currently links libpython (Python3::Python), which hides the
problem: the consumer runs, but loads libpython.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Maurice <mauricepurnawan@gmail.com>

(cherry picked from commit 2ed71d4 of #51, without the pkg_additional_info.yaml build-number bump)
Tobias-Fischer pushed a commit that referenced this pull request Sep 23, 2026
…odules

Replace the macOS-only patch with a cross-platform one that removes the
shared lib<pkg>__rosidl_generator_py library:

- The generated C conversion code is compiled (as an OBJECT library) into
  each <pkg>_s__rosidl_typesupport_* Python extension module, which links
  Python3::Module. Nothing is installed or exported, so <pkg>_TARGETS only
  contains C/C++ libraries and no library has unresolved CPython symbols.
- Conversion functions of nested types from other packages are looked up
  (and cached) from those packages' Python message classes
  (_CONVERT_FROM_PY / _CONVERT_TO_PY capsules, already used by rclpy)
  instead of linking the other package's library.

rosidl_generator_py is bumped to build 26 so CI rebuilds it. So are
rosidl_core_generators and rosidl_default_generators: std_msgs only
depends on the generator through them, and without rebuilding them
rattler-build does not know to build the generator before std_msgs. The
regression test added in the previous commit now passes.

Message packages built by the old generator link their dependencies'
lib<dep>__rosidl_generator_py, so all interface packages must be rebuilt
together (e.g. in the next full rebuild).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Maurice <mauricepurnawan@gmail.com>

(cherry picked from commit 7d891c3 of #51, without the pkg_additional_info.yaml build-number bump)
Tobias-Fischer pushed a commit that referenced this pull request Sep 23, 2026
The earlier CI runs of this PR cached a std_msgs build 26 built with the
old generator; with --skip-existing it would be reused instead of being
rebuilt with the patched generator. Drop this commit before merging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Maurice <mauricepurnawan@gmail.com>

(cherry picked from commit 82d0021 of #51)
Tobias-Fischer added a commit to RoboStack/ros-jazzy that referenced this pull request Sep 23, 2026
…odules

Port of RoboStack/ros-rolling#51 (without its pkg_additional_info.yaml
build-number bumps): stop exporting the lib<pkg>__rosidl_generator_py
library, which has unresolved CPython symbols, to C++ consumers of
interface packages. The generated conversion code is compiled as an
OBJECT library into each Python extension module, and conversion
functions of nested types from other packages are looked up from those
packages' Python message classes (_CONVERT_FROM_PY/_CONVERT_TO_PY).

Adds the std_msgs C++-consumer regression test and a CI cache eviction
for std_msgs so it is rebuilt and tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tobias-Fischer added a commit to RoboStack/ros-humble that referenced this pull request Sep 23, 2026
…odules

Port of RoboStack/ros-rolling#51 (without its pkg_additional_info.yaml
build-number bumps): stop exporting the lib<pkg>__rosidl_generator_py
library, which has unresolved CPython symbols, to C++ consumers of
interface packages. The generated conversion code is compiled as an
OBJECT library into each Python extension module, and conversion
functions of nested types from other packages are looked up from those
packages' Python message classes (_CONVERT_FROM_PY/_CONVERT_TO_PY).

Adds the std_msgs C++-consumer regression test and a CI cache eviction
for std_msgs so it is rebuilt and tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@traversaro

Copy link
Copy Markdown
Member

First of all, thanks a lot for working on this and writing so clearly about the problem. I really like the solution in this PR, but that is quite a departure (and an ABI break) w.r.t. to upstream, so it is something that I think it make sense to discuss upstream and just for ROS Rolling (to avoid the long term divergence of upstream and RoboStack), and once there is an indication that upstream is interesting in merging it, we can adopt it in RoboStack.

To have something that can is not so impactful and we can use in earlier distro without diverging too much w.r.t. to upstream, I like your proposal of having a separate <pkg>_TARGETS__rosidl_generator_py (bikeshedding: a more consistent name would may be <pkg>_rosidl_generator_py_TARGETS). With the <pkg>_TARGETS__rosidl_generator_py list approach, if a downstream consumer really needs to link with <pkg>__rosidl_generator_py, it just needs to also link ${_TARGETS__rosidl_generator_py} on top of ${<pkg>_TARGETS}, that is a change of limited impact. However, I am missing something in the underlying logic of your change, shouldn't the part of generator that generates the nested messages (that from what I understood, is the only external consumer of the <pkg>__rosidl_generator_py) also be modified to link ${<pkg>_TARGETS__rosidl_generator_py} or directly <pkg>__rosidl_generator_py?

An alternative I also thought of but I now think it is worse then your options, is to add a:

    set_property(TARGET ${_target_name_lib} APPEND PROPERTY INTERFACE_LINK_LIBRARIES
        "$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:Python3::Python>"
    )

in this way, any executable that link ${<something>_msgs_TARGETS} will correctly link Python3::Python, while the libraries that link ${<something>_msgs_TARGETS} will only link Python3::Module.

However, this fails when ${<something>_msgs_TARGETS} is linked privately in a library, that then is linked in an executable:

add_library(libA)
target_link_libraries(libA PRIVATE ${<something>_msgs_TARGETS})
add_executable(execA)
target_link_libraries(execA PRIVATE libA)

in thise case, the $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:Python3::Python> will never reach execA, and so the missing symbols problem will arise again.

@traversaro

Copy link
Copy Markdown
Member

TL;DR (this is just my opinion):

Comment thread tests/ros-rolling-std-msgs.yaml Outdated
- python -c "from rclpy.serialization import deserialize_message, serialize_message; from std_msgs.msg import Header; m = deserialize_message(serialize_message(Header(frame_id='map')), Header); assert m.frame_id == 'map'"
requirements:
run:
- ros-rolling-rclpy

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- ros-rolling-rclpy
- ros2-rclpy

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 4f02e5d

Comment thread tests/ros-rolling-std-msgs.yaml Outdated
- ./build/std_msgs_cpp_consumer
requirements:
build:
- ${{ compiler('cxx') }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the problem emerges with minimally activated compilers, to easily reproduce it without the need for env -u LDFLAGS, you can just use cxx-compiler here directly. The ${{ compiler('cxx') }} macro is important for cross-compiling, but in the case of tests and specificaly for this I guess using cxx-compiler make sense.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 4f02e5d

class_module = '%s.%s' % ('.'.join(message.structure.namespaced_type.namespaces), module_name)
namespaced_type = message.structure.namespaced_type.name
}@
-ROSIDL_GENERATOR_C_EXPORT

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you will need this for Windows.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a Windows machine to test this, but claude says:

In the refactor, the conversion functions are only called from inside the same extension DLL, and other packages reach them through capsules, not exported symbols.

Also it looks like it is working fine in the CI (?) I am fine to bring it back though

set(_target_name_lib "${rosidl_generate_interfaces_TARGET}__rosidl_generator_py")
-add_library(${_target_name_lib} SHARED ${_generated_c_files})
+add_library(${_target_name_lib} OBJECT ${_generated_c_files})
+set_target_properties(${_target_name_lib} PROPERTIES POSITION_INDEPENDENT_CODE ON)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this POSITION_INDEPENDENT_CODE ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to #51 (comment), if I drop the object library, I can also drop the PIC here. But if I go with the object library, I think we need PIC, otherwise I think I get something like: relocation R_X86_64_PC32 … can not be used when making a shared object on my machine

+# runtime, so no library needs to be exported or linked across packages.
set(_target_name_lib "${rosidl_generate_interfaces_TARGET}__rosidl_generator_py")
-add_library(${_target_name_lib} SHARED ${_generated_c_files})
+add_library(${_target_name_lib} OBJECT ${_generated_c_files})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are not going to install this, can't we just include the generated source files in the Python extension, instead of having an intermediate OBJECT library?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a tradeoff here, if I drop the Object library, then we will end up compiling each _s.c once per module

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in #51 (comment), I think we should go in another direction (i.e. removing the ${rosidl_generate_interfaces_TARGET}__rosidl_generator_py from ${<pkgname>_TARGETS} and just leave it in ${<pkgname>_TARGETS__rosidl_generator_py}), so this discussion would be stale. Anyhow, just for completeness, as after the changes the _s.c are only compiled in one module, what is the problem? It would still be compiled only once in both cases, unless I am missing something.

@traversaro

traversaro commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

However, I am missing something in the underlying logic of your change, shouldn't the part of generator that generates the nested messages (that from what I understood, is the only external consumer of the <pkg>__rosidl_generator_py) also be modified to link ${<pkg>_TARGETS__rosidl_generator_py} or directly <pkg>__rosidl_generator_py?

Oh boy, trying to answer this question, I found that ${${_pkg_name}_TARGETS${rosidl_generator_py_suffix}} was already used in https://github.com/ros2/rosidl_python/blob/184673fabdc9fad7c685cabac2e91e1eec497677/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake#L251C53-L251C105, but probably rosidl_generator_py_suffix was never set? In that case, this suggests that the inclusion <pkg>__rosidl_generator_py in ${<pkg>_TARGETS} may be a regression, and originally rosidl_python was indeed intended to work as you propose in your smaller patch.

@traversaro

traversaro commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

In that case, this suggests that the inclusion <pkg>__rosidl_generator_py in ${<pkg>_TARGETS} may be a regression, and originally rosidl_python was indeed intended to work as you propose in your smaller patch.

That is indeed the case. As GPT5.6 summarizes better then me:

I looked at the history of rosidl_generator_py_suffix, and it seems that the current situation is actually the result of a regression.

In January 2022, [ros2/rosidl_python#149](https://github.com/ros2/rosidl_python/pull/149) introduced:

# Export target so downstream interface packages can link to it
set(rosidl_generator_py_suffix "__rosidl_generator_py")

set(_target_name_lib
  "${rosidl_generate_interfaces_TARGET}${rosidl_generator_py_suffix}")

The same variable was then deliberately used when linking the generated Python library against the Python-generator libraries of dependent interface packages:

foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES})
  target_link_libraries(
    ${_target_name_lib}
    ${${_pkg_name}_TARGETS${rosidl_generator_py_suffix}})
endforeach()

and when exporting the target:

rosidl_export_typesupport_targets(
  "${rosidl_generator_py_suffix}"
  "${_target_name_lib}")

The commit message of [0f43ffc](https://github.com/ros2/rosidl_python/commit/0f43ffc490b7e4ec08022361bced5fa274246dfd) is quite explicit about the intent:

It works by adding a variable ${PROJECT_NAME}_TARGETS__rosidl_generator_py which is set when the interface package is find_package()d. That variable contains the targets generated by rosidl_generator_py so that downstream interface packages can depend on it.

So the original design was indeed to have a dedicated:

<pkg>_TARGETS__rosidl_generator_py

containing the rosidl_generator_py target needed by downstream interface packages.

The interesting part is what happened later in [ros2/rosidl_python#140](https://github.com/ros2/rosidl_python/pull/140). This PR was originally opened in 2021, before #149 existed, and was eventually merged in February 2024 after being revived and updated.

The final merge removed:

set(rosidl_generator_py_suffix "__rosidl_generator_py")

and replaced:

set(_target_name_lib
  "${rosidl_generate_interfaces_TARGET}${rosidl_generator_py_suffix}")

with:

set(_target_name_lib
  "${rosidl_generate_interfaces_TARGET}__rosidl_generator_py")

However, the later uses of rosidl_generator_py_suffix were left unchanged:

${${_pkg_name}_TARGETS${rosidl_generator_py_suffix}}

and:

rosidl_export_typesupport_targets(
  "${rosidl_generator_py_suffix}"
  "${_target_name_lib}")

This can be seen in the merge commit [1505ac0](https://github.com/ros2/rosidl_python/commit/1505ac07675800c8259ae40df3421177ce0eb17a).

There is also an interesting clue in the review history of #140. Shane noticed that the PR was changing the target name from __rosidl_generator_py to __python and asked whether that was intentional. The target name was restored in [b195abb](https://github.com/ros2/rosidl_python/commit/b195abbf146b4186d360949bec66d6668d6f97fd), but only as a literal:

set(_target_name_lib
  "${rosidl_generate_interfaces_TARGET}__rosidl_generator_py")

The set(rosidl_generator_py_suffix "__rosidl_generator_py") assignment was not restored.

Since an undefined CMake variable expands to the empty string, current code such as:

${${_pkg_name}_TARGETS${rosidl_generator_py_suffix}}

effectively becomes:

${${_pkg_name}_TARGETS}

Likewise:

rosidl_export_typesupport_targets(
  "${rosidl_generator_py_suffix}"
  "${_target_name_lib}")

is effectively called with an empty suffix.

This explains why the problem does not produce an obvious CMake error: the undefined variable silently changes the semantics instead.

The historical behavior therefore seems to be:

#149, 2022
  ↓
Introduce <pkg>_TARGETS__rosidl_generator_py
specifically for rosidl_generator_py dependencies
  ↓
#140, 2024
  ↓
rosidl_generator_py_suffix assignment accidentally disappears
  ↓
${pkg_TARGETS__rosidl_generator_py}
silently degenerates into
${pkg_TARGETS}

There is one additional wrinkle: ament_export_targets() itself adds exported targets to the generic ${PROJECT_NAME}_TARGETS, so simply restoring:

set(rosidl_generator_py_suffix "__rosidl_generator_py")

would restore the specialized dependency mechanism, but would not necessarily by itself stop __rosidl_generator_py from also appearing in the generic ${pkg_TARGETS}.

So I think there are two related issues here:

  1. rosidl_generator_py_suffix becoming undefined looks like an actual regression introduced by #140.
  2. Independently, exporting __rosidl_generator_py through ament_export_targets() causes it to appear in the generic ${pkg_TARGETS}, which is the issue we are trying to solve here.

This also makes the separate target-list approach much less of a new design than I initially thought. <pkg>_TARGETS__rosidl_generator_py was explicitly introduced for exactly this purpose in #149. A relatively conservative solution could therefore restore/preserve that specialized list for interface-generation dependencies while preventing the Python generator library from leaking into the generic ${pkg_TARGETS} used by arbitrary downstream C/C++ consumers.

AI;DR: The separate list for the generator_py was introduced in ros2/rosidl_python#149, but the definition of rosidl_generator_py_suffix was accidentally removed in ros2/rosidl_python#140 . At this point, the separate target lists seems indeed a much more promising path, as it follow more closely what upstream has been doing. I think the upstream issue ros2/rosidl_python#213 is basically about this.

@mini-1235

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed reply and review.

TL;DR (this is just my opinion):

Change in this PR ok for being proposed upstream and just for ROS Rolling, assuming that we use it in a full rebuild. I also some minor comments that I will do inline.
For older distros, the removing the __rosidl_generator_py from the default ${<>_TARGETS} list make more sense to me, but please see the comments in #51 (comment) .

Given that, what if we go with the minimal patch in the short term, including for Rolling?

To be honest, the two fixes plus the test are something I started iterating on last month. I couldn't get a promising result initially, but eventually got to something a few days ago that, IMO, is complete enough to put up for discussion. That said, I can't say I understand every part of the code 100%; quite a bit of it was assisted by Claude, while I mainly provided the direction based on what I learned from my previous iterations.

If we go with the minimal patch for all distros, I think I can keep this PR open so we can continue iterating on the patch here. That would also give me some more time to review it myself and run a few additional tests when I have time. Once we are more confident about the approach, I can open the upstream PR and then apply it to ros-rolling.

In the meantime, I can create another PR with just the minimal patch targeting the full-rebuild branch, and then "backport" it to the other branches that are also doing rebuilds. That feels less disruptive and should still solve the issue I originally reported.

Looking through your comments, it seems like you have already responded yourself to most of the items raised in Stop linking the Python bindings library into C++ consumers of interface packages (fixes dyld _PyExc_RuntimeError / ros-kilted#76) #51 (comment). Let me know if there is anything there that you would still like me to respond to :)

@traversaro

traversaro commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Change in this PR ok for being proposed upstream and just for ROS Rolling, assuming that we use it in a full rebuild. I also some minor comments that I will do inline.

@mini-1235 to be honest this was my opinion before going more in deep in the history of the problem, as reported #51 (comment) (sorry for the AI slop quote, but you can probably just ignore that and read the AI;DR at the end. After looking at the history of the repo, the solution that just changes the exported ${<pkg>_TARGETS} variable seems much less impactful and more promising to upstream.

@mini-1235

mini-1235 commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Change in this PR ok for being proposed upstream and just for ROS Rolling, assuming that we use it in a full rebuild. I also some minor comments that I will do inline.

@mini-1235 to be honest this was my opinion before going more in deep in the history of the problem, as reported #51 (comment) (sorry for the AI slop quote, but you can probably just ignore that and read the AI;DR at the end. After looking at the history of the repo, the solution that just changes the exported ${<pkg>_TARGETS} variable seems much less impactful and more promising to upstream.

Ah ok. Just to confirm, you agree to link to Python3::Module on MacOS, but Python3::Python on other platform, correct?

@traversaro

Copy link
Copy Markdown
Member

Change in this PR ok for being proposed upstream and just for ROS Rolling, assuming that we use it in a full rebuild. I also some minor comments that I will do inline.

@mini-1235 to be honest this was my opinion before going more in deep in the history of the problem, as reported #51 (comment) (sorry for the AI slop quote, but you can probably just ignore that and read the AI;DR at the end. After looking at the history of the repo, the solution that just changes the exported ${<pkg>_TARGETS} variable seems much less impactful and more promising to upstream.

Ah ok. Just to confirm, we should link to Python3::Module on MacOS, but Python3::Python on other platform, correct?

After reading the history of commits, I think ${rosidl_generate_interfaces_TARGET}__rosidl_generator_py was only meant to be linked by Python extensions, and that would be the case if it is excluded from <pkgname>_TARGETS. Assuming that is the case, I think Python3::Module on all platform is the correct solution.

@mini-1235

Copy link
Copy Markdown
Contributor Author

Change in this PR ok for being proposed upstream and just for ROS Rolling, assuming that we use it in a full rebuild. I also some minor comments that I will do inline.

@mini-1235 to be honest this was my opinion before going more in deep in the history of the problem, as reported #51 (comment) (sorry for the AI slop quote, but you can probably just ignore that and read the AI;DR at the end. After looking at the history of the repo, the solution that just changes the exported ${<pkg>_TARGETS} variable seems much less impactful and more promising to upstream.

Ah ok. Just to confirm, we should link to Python3::Module on MacOS, but Python3::Python on other platform, correct?

After reading the history of commits, I think ${rosidl_generate_interfaces_TARGET}__rosidl_generator_py was only meant to be linked by Python extensions, and that would be the case if it is excluded from <pkgname>_TARGETS. Assuming that is the case, I think Python3::Module on all platform is the correct solution.

I vaguely remember running into a build-time error when I tried that before, but maybe I did something wrong. Let me give it another try, and if it works, I will update the PR

@traversaro

Copy link
Copy Markdown
Member

Change in this PR ok for being proposed upstream and just for ROS Rolling, assuming that we use it in a full rebuild. I also some minor comments that I will do inline.

@mini-1235 to be honest this was my opinion before going more in deep in the history of the problem, as reported #51 (comment) (sorry for the AI slop quote, but you can probably just ignore that and read the AI;DR at the end. After looking at the history of the repo, the solution that just changes the exported ${<pkg>_TARGETS} variable seems much less impactful and more promising to upstream.

Ah ok. Just to confirm, we should link to Python3::Module on MacOS, but Python3::Python on other platform, correct?

After reading the history of commits, I think ${rosidl_generate_interfaces_TARGET}__rosidl_generator_py was only meant to be linked by Python extensions, and that would be the case if it is excluded from <pkgname>_TARGETS. Assuming that is the case, I think Python3::Module on all platform is the correct solution.

I vaguely remember running into a build-time error when I tried that before, but maybe I did something wrong. Let me give it another try, and if it works, I will update the PR

In that case, I would be curious to see the build-time error you are getting in that case!

@mini-1235
mini-1235 force-pushed the fix-rosidl-generator-py-python-lib branch from 4f02e5d to 8ab3c6a Compare September 27, 2026 13:19
@mini-1235

Copy link
Copy Markdown
Contributor Author

Change in this PR ok for being proposed upstream and just for ROS Rolling, assuming that we use it in a full rebuild. I also some minor comments that I will do inline.

@mini-1235 to be honest this was my opinion before going more in deep in the history of the problem, as reported #51 (comment) (sorry for the AI slop quote, but you can probably just ignore that and read the AI;DR at the end. After looking at the history of the repo, the solution that just changes the exported ${<pkg>_TARGETS} variable seems much less impactful and more promising to upstream.

Ah ok. Just to confirm, we should link to Python3::Module on MacOS, but Python3::Python on other platform, correct?

After reading the history of commits, I think ${rosidl_generate_interfaces_TARGET}__rosidl_generator_py was only meant to be linked by Python extensions, and that would be the case if it is excluded from <pkgname>_TARGETS. Assuming that is the case, I think Python3::Module on all platform is the correct solution.

I vaguely remember running into a build-time error when I tried that before, but maybe I did something wrong. Let me give it another try, and if it works, I will update the PR

In that case, I would be curious to see the build-time error you are getting in that case!

I can't reproduce that error anymore. I think it was something I ran into last month, and I have updated my dependencies several times since then. My guess now is that it may have been related to ros2-rust/rosidl_rust#22, which could have caused the build error(?) Although I haven't rebuilt the older setup to verify that.

As far as I remember, I didn't change anything other than the dependency versions, so I am also convinced that we should link to Module on all platforms. I will update the patch now. If that looks good to you, I can then retarget my branch to the full-rebuild one

@mini-1235
mini-1235 force-pushed the fix-rosidl-generator-py-python-lib branch from 8f7f9a4 to e3e61ef Compare September 27, 2026 13:53
@mini-1235
mini-1235 changed the base branch from main to codex/cross-distro-sync September 27, 2026 13:53
@traversaro

traversaro commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

Thanks a lot! I really like this version much more. What I think it is confusing now and risky of upstream rejection, is the fact that we do not use anymore ament_export_targets and rosidl_export_typesupport_targets . I think a possible way forward is to add an option to ament_export_targets so that it can be used also in this context, and continue to use rosidl_export_typesupport_targets. AI recap (but please ask if you need more human details):

After looking more deeply at how ament_export_targets() and rosidl_export_typesupport_targets() interact, I think the clean final solution would be the following.

The intended result is:

<pkg>_TARGETS
# Must not contain <pkg>::<target>__rosidl_generator_py

<pkg>_TARGETS__rosidl_generator_py
# Must contain <pkg>::<target>__rosidl_generator_py

rosidl_export_typesupport_targets() already provides the second part. It classifies an exported target under a generator/type-support-specific suffix, allowing downstream interface packages to use:

${<dependency>_TARGETS__rosidl_generator_py}

The problem is that rosidl_export_typesupport_targets() does not install or include the CMake export set. It expects the corresponding namespaced imported target to have already been created.

Normally, that is done by:

ament_export_targets(export_<target>)

However, ament_export_targets() currently combines two distinct operations:

  1. It installs and includes the CMake export set, creating the namespaced imported target.
  2. It unconditionally appends the imported target to ${PROJECT_NAME}_TARGETS.

Both operations are normally desirable, but rosidl_generator_py needs only the first one. Its helper library is required by other generated Python interface libraries, but it must not be linked by ordinary C or C++ consumers through ${<pkg>_TARGETS}.

Therefore, I think the clean upstream solution is to add an option to ament_export_targets(), tentatively named EXCLUDE_FROM_PACKAGE_TARGETS:

ament_export_targets(
  export_${_target_name_lib}
  EXCLUDE_FROM_PACKAGE_TARGETS)

rosidl_export_typesupport_targets(
  "${rosidl_generator_py_suffix}"
  "${_target_name_lib}")

With this option, ament_export_targets() would still:

  • install the export set;
  • install and include export_<target>Export.cmake;
  • create the namespaced imported target;
  • preserve the normal namespace and HAS_LIBRARY_TARGET behavior.

It would only skip:

list(APPEND ${PROJECT_NAME}_TARGETS ...)

rosidl_export_typesupport_targets() would then populate the generator-specific list in the usual way:

list(APPEND
  ${PROJECT_NAME}_TARGETS__rosidl_generator_py
  "${PROJECT_NAME}::${_target_name_lib}")

This keeps the responsibilities well separated:

  • ament_export_targets() owns installation and creation of imported CMake targets;
  • rosidl_export_typesupport_targets() owns the rosidl generator/type-support classification;
  • the new ament option controls whether an exported target belongs to the general package aggregate.

The corresponding change in ament_cmake should be backward-compatible: without the option, ament_export_targets() would retain its current behavior. The option would apply to the export sets passed in that invocation.

Tests in ament_cmake should verify that an excluded target:

  • still exists as a namespaced imported target after find_package();
  • can still be passed directly to target_link_libraries();
  • is not present in ${PROJECT_NAME}_TARGETS;
  • does not affect ordinary export sets from the same package.

The rosidl_generator_py change would then become very small:

set(rosidl_generator_py_suffix "__rosidl_generator_py")

set(_target_name_lib
  "${rosidl_generate_interfaces_TARGET}${rosidl_generator_py_suffix}")

# ...

target_link_libraries(
  ${_target_name_lib} PRIVATE
  Python3::NumPy
  Python3::Module)

# ...

ament_export_targets(
  export_${_target_name_lib}
  EXCLUDE_FROM_PACKAGE_TARGETS)

rosidl_export_typesupport_targets(
  "${rosidl_generator_py_suffix}"
  "${_target_name_lib}")

This also restores the relationship originally introduced by ros2/rosidl_python#149: the same suffix identifies both the generated target and the package-specific target list used by downstream interface generation.

I think this is preferable to manually writing an extras file that both includes the export file and appends to the suffixed variable, because that duplicates behavior already implemented and validated by the two public macros.

If changing ament_cmake is considered too large for the immediate RoboStack fix, the smaller fallback would be:

  • manually install/include the export file;
  • retain rosidl_export_typesupport_targets() for the suffixed list;
  • avoid manually reimplementing the suffixed-list registration.

However, for ROS Rolling and for an eventual upstream solution, I think adding EXCLUDE_FROM_PACKAGE_TARGETS to ament_export_targets() is the clearest and most maintainable design.

Since the patched rosidl_generator_py would require the new ament_export_targets() option, the changes would need to be coordinated:

  1. merge/release or patch the ament_cmake change;
  2. update rosidl_generator_py to use the option;
  3. rebuild the interface packages together, so that all dependencies publish the restored ${<pkg>_TARGETS__rosidl_generator_py} list.

The existing Python3::Module change should remain: the helper library is loaded through Python extension modules, while Python3::Python is intended for embedding Python into an executable.

@traversaro

Copy link
Copy Markdown
Member

fyi @eholum @sea-bass , I think here we finally figured out the proper solution to RoboStack/ros-kilted#76 (comment) .

…RGETS

Replace the patch that compiled the Python bindings into the extension
modules with a smaller one that is closer to upstream's original design
(ros2/rosidl_python#149):

- Restore rosidl_generator_py_suffix ("__rosidl_generator_py"), which
  ros2/rosidl_python#140 accidentally dropped. The existing dependency
  loop then links other interface packages' Python bindings through the
  dedicated <pkg>_TARGETS__rosidl_generator_py list again.
- Export the library without ament_export_targets(), which always adds it
  to <pkg>_TARGETS: install the export set and include it from a config
  extras file, and keep rosidl_export_typesupport_targets(), which adds the
  imported target to <pkg>_TARGETS__rosidl_generator_py only. C/C++
  consumers of an interface package no longer link it.
- Link Python3::Module instead of Python3::Python on all platforms: the
  library is only linked by Python extension modules, which get the
  CPython symbols from the interpreter that loads them.

Also update the std_msgs regression test as suggested in review: build
the C++ consumer with cxx-compiler (no LDFLAGS, like a user environment)
instead of compiler('cxx') + clearing LDFLAGS, and depend on ros2-rclpy.

No build number bumps: this goes into the full rebuild, which rebuilds
all interface packages with the new generator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Maurice <mauricepurnawan@gmail.com>
@mini-1235
mini-1235 force-pushed the fix-rosidl-generator-py-python-lib branch from e3e61ef to be116c6 Compare September 27, 2026 14:29
@mini-1235

Copy link
Copy Markdown
Contributor Author

If changing ament_cmake is considered too large for the immediate RoboStack fix, the smaller fallback would be:

manually install/include the export file;
retain rosidl_export_typesupport_targets() for the suffixed list;
avoid manually reimplementing the suffixed-list registration.

Updated in be116c6

However, for ROS Rolling and for an eventual upstream solution, I think adding EXCLUDE_FROM_PACKAGE_TARGETS to ament_export_targets() is the clearest and most maintainable design.

I will tag you once I am open the PR

@mini-1235

Copy link
Copy Markdown
Contributor Author

I open two PRs: ament/ament_cmake#640 and ros2/rosidl_python#269

@traversaro

Copy link
Copy Markdown
Member

Thanks @mini-1235 ! As this PR targets the branch of #41, let's at least wait for Linux CI to complete here before merging this PR, so we can then merge #41 safely.

@traversaro

Copy link
Copy Markdown
Member

CI is happy and on Windows/macOS we reached the maximum time, I think we can merge.

@traversaro
traversaro merged commit 3e79362 into RoboStack:codex/cross-distro-sync Sep 28, 2026
3 of 6 checks passed
Tobias-Fischer added a commit to RoboStack/ros-jazzy that referenced this pull request Sep 29, 2026
…RGETS

Mirror the simplified fix from RoboStack/ros-rolling#51 (be116c63),
replacing the earlier approach that compiled the Python bindings into the
extension modules. The library is again a shared library linked through
<pkg>_TARGETS__rosidl_generator_py, but it is exported via an installed
export set included from a config extras file instead of
ament_export_targets(), so it no longer ends up in <pkg>_TARGETS and C/C++
consumers of interface packages don't link it. It links Python::Module
rather than libpython, since only Python extension modules link it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tobias-Fischer added a commit to RoboStack/ros-humble that referenced this pull request Sep 29, 2026
…RGETS

Mirror the simplified fix from RoboStack/ros-rolling#51 (be116c63),
replacing the earlier approach that compiled the Python bindings into the
extension modules. The library is again a shared library linked through
<pkg>_TARGETS__rosidl_generator_py, but it is exported via an installed
export set included from a config extras file instead of
ament_export_targets(), so it no longer ends up in <pkg>_TARGETS and C/C++
consumers of interface packages don't link it. It links Python::Module
rather than libpython, since only Python extension modules link it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants