Skip to content

rosidl_cmake: install interface files to same folder as idl#935

Open
techtasie wants to merge 1 commit intoros2:rollingfrom
techtasie:rolling
Open

rosidl_cmake: install interface files to same folder as idl#935
techtasie wants to merge 1 commit intoros2:rollingfrom
techtasie:rolling

Conversation

@techtasie
Copy link

@techtasie techtasie commented Feb 12, 2026

Description

Changes the install location for the interface files to share/${PROJECT_NAME}/<msg/srv/action>. The current logic of installing to share/${PROJECT_NAME}/${_parent_folder} separates idl and interface files into separate folders which breaks ros2 interface show. Furthermore there can't be two interfaces with the same name anyways, therefore separating them into sub folders is not providing any advantages and is just confusing.

Fixes #933

Is this user-facing behavior change?

Did you use Generative AI?

ChatGPT 5.2 has been used to explain regex.

Additional Information

Also affects ros2/ros2cli#1186 and RobotWebTools/rclnodejs#1388

Copy link
Contributor

@fujitatomoya fujitatomoya left a comment

Choose a reason for hiding this comment

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

i think this is reasonable fix and consistent behavior.

IMO, keeping message files under msg/ is NOT a hard requirement for the development.the rosidl_generate_interfaces() CMake macro technically accepts paths with subfolders (like msg/ros2/ldmrs/SickLdmrsObject.msg), so it doesn't reject them at build time. (packages build and the messages work at runtime for publishing/subscribing.) however, the ros2interface tooling assumes the standard layout.

the PR fixes real root cause and provides more consistency that two interfaces can't have the same name within a package anyway, so the subfolder separation provides no real benefit.

lgtm with green CI.
i would like to have another approval from other maintainers before merge.

@fujitatomoya
Copy link
Contributor

Pulls: #935
Gist: https://gist.githubusercontent.com/fujitatomoya/e903fdf0723d4fe98bd33d5ce64564ae/raw/b166df6f379ad7d693ec7a9eebc3025beff213a8/ros2.repos
BUILD args: --packages-above-and-dependencies rosidl_cmake
TEST args: --packages-above rosidl_cmake
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/18178

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

@techtasie
Copy link
Author

I assume this cannot be back-ported since it changes the file layout.

I would work on a patch for ros2 interface show. Since there is only rosidl_interfaces and no ament resource for interface files, I would need to recursively travel through the share/ folder and look for the correctly named interface file. Or is there any way I am not aware of?

@christophebedard
Copy link
Member

I'm a bit concerned about this change.

A lot of downstream tools (e.g., rqt plugins) rely on the location of these files. Some of them do it through rosidl_runtime_py's get_interfaces.py functions (which should answer your question above), but I've personally found that there are a lot of hardcoded assumptions about paths of interface files based on their names (i.e., not always using these functions). Also, it's not always very well tested, especially in rqt plugins.

Could you take a look at some rqt plugins like rqt_topic to better understand the potential impact of this?

@asymingt asymingt added the more-information-needed Further information is required label Mar 5, 2026
@techtasie
Copy link
Author

techtasie commented Mar 6, 2026

@christophebedard I think there is a misunderstanding here.

To clarify, I am using the term Interface file to refer to the .msg/.srv/.action files that are created by the user. I use the term IDL file to describe the generated interface definition files. Sometimes these terms get mixed up, but for this issue it is important to keep them separate.

get_interfaces provides a dump of the AMENT resource rosidl_interfaces, but it filters for interface names and does not return the files.

If you want to retrieve the files, you can use get_interface_path, but it only returns the IDL files and not the Interface files.

I will provide an example:

test_interfaces
├── CMakeLists.txt
├── msg
│   └── A.msg
├── package.xml
└── ros
    └── B.msg

CMakeLists.txt :

cmake_minimum_required(VERSION 3.8)
project(test_interfaces)

find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/A.msg"
  "ros/B.msg"
)

ament_export_dependencies(rosidl_default_runtime)
ament_package()

The following AMENT resources are generated:

❯ ls install/test_interfaces/share/ament_index/resource_index
package_run_dependencies  parent_prefix_path
packages                  rosidl_interfaces

The contents of rosidl_interfaces:

❯ cat install/test_interfaces/share/ament_index/resource_index/rosidl_interfaces/test_interfaces
msg/A.idl
msg/A.msg
msg/B.idl
ros/B.msg

The files are installed as following:

install/test_interfaces/share/test_interfaces/msg
├── A.idl
├── A.msg
└── B.idl
install/test_interfaces/share/test_interfaces/ros
└── B.msg

On my humble machine I get the following output from python:

❯ python
Python 3.10.12 (main, Jan 26 2026, 14:55:28) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from rosidl_runtime_py import get_interfaces, get_interface_path
>>> get_interfaces(["test_interfaces"])
{'test_interfaces': ['ros/B', 'msg/A', 'msg/B']}
>>> get_interface_path("test_interfaces/msg/A")
'/home/tim/example_ws/install/test_interfaces/share/test_interfaces/msg/A.msg'
>>> get_interface_path("test_interfaces/msg/B")
'/home/tim/example_ws/install/test_interfaces/share/test_interfaces/msg/B.idl'
>>> get_interface_path("test_interfaces/ros/B")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/ros/humble/lib/python3.10/site-packages/rosidl_runtime_py/get_interfaces.py", line 192, in get_interface_path
    raise LookupError(f"Could not find the interface '{interface_path}'")
LookupError: Could not find the interface '/home/tim/example_ws/install/test_interfaces/share/test_interfaces/ros/B.idl'

This shows that installing the interface files into a different folder than the IDL files causes a problem when using these functions.

Regarding hard-coding, I assume that packages simply do package_share_path + interface_name + ".idl". This is a correct assumption, even with the current diverging install paths, and it remains a valid assumption. However, if they intend to retrieve the Interface file, as ros2 interface show does, this causes a problem. Since the correct path is not package_share_path + interface_name + ".msg". but instead package_share_path + parent_folder_name_during_generation + ".msg" which can not be hard coded.

This pr simply moves the Interface file into the same folder as the IDL file.

Thanks for the tip. I realized that the AMENT rosidl_interfaces resource contains the Interface file locations, and I can use it directly to find the Interface files.

@fujitatomoya
Copy link
Contributor

@christophebedard friendly ping.

Signed-off-by: Tim Wendt <techtasie@gmail.com>
@techtasie
Copy link
Author

@fujitatomoya, I fixed the CI. I forgot to change the logic at the generation for the AMENT resource. Could you trigger the CI Build again?

@techtasie
Copy link
Author

@christophebedard @fujitatomoya friendly ping.

@fujitatomoya
Copy link
Contributor

@christophebedard can you check the comment on #935 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

more-information-needed Further information is required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rosidl does not support "ros2 interface show <actual srv name>" while using sub folder for srv sort

4 participants