Skip to content

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

Merged
christophebedard merged 1 commit into
ros2:rollingfrom
techtasie:rolling
Apr 21, 2026
Merged

rosidl_cmake: install interface files to same folder as idl#935
christophebedard merged 1 commit into
ros2:rollingfrom
techtasie:rolling

Conversation

@techtasie
Copy link
Copy Markdown
Contributor

@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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Contributor 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
Copy Markdown
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
Copy Markdown
Contributor 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
Copy Markdown
Contributor

@christophebedard friendly ping.

@techtasie
Copy link
Copy Markdown
Contributor 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
Copy Markdown
Contributor Author

@christophebedard @fujitatomoya friendly ping.

@fujitatomoya
Copy link
Copy Markdown
Contributor

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

@techtasie
Copy link
Copy Markdown
Contributor Author

@fujitatomoya @christophebedard friendly reminder. I would like to merge it before Lyrical since otherwise we will carry this issue over.

@christophebedard
Copy link
Copy Markdown
Member

@Mergifyio rebase

@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Apr 19, 2026

Deprecation notice: This pull request comes from a fork and was rebased using bot_account impersonation. This capability will be removed on July 1, 2026. After this date, the rebase action will no longer be able to rebase fork pull requests with this configuration. Please switch to the update action/command to ensure compatibility going forward.

@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Apr 19, 2026

rebase

✅ Branch has been successfully rebased

@christophebedard
Copy link
Copy Markdown
Member

christophebedard commented Apr 19, 2026

Pulls: #935
Gist: https://gist.githubusercontent.com/christophebedard/51ab26fbc51cb3aac2ec3a69a79eb3b6/raw/f387bd64e1658020c64eabb121cfc06b27c2107a/ros2.repos
BUILD args:
TEST args:
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/19002

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

@christophebedard
Copy link
Copy Markdown
Member

christophebedard commented Apr 19, 2026

@techtasie thanks for the overview.

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

Why not change the IDL file generated for a .msg file to be created/installed next to that .msg file (and so on) instead? Then we keep whatever directory the original .msg file was placed in. That makes much more sense to me.

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

@techtasie
Copy link
Copy Markdown
Contributor Author

@christophebedard What would be the advantage of that? There cannot be two interfaces with the same name anyway, and it would break the hardcoded paths some packages use, as I explained in my comment. Furthermore, I find it a bit confusing that it’s only the name of the parent directory and not the same relative path as the source file; this feels unintuitive to me.

In the end, it doesn’t matter that much. We just have to make a decision. I prefer my implementation, but the final decision lies with the maintainers.

@christophebedard
Copy link
Copy Markdown
Member

I agree with you that some flexibility with interface file locations would be nice (and I've actually implemented that in the past), but I'm just concerned about where this is going (not strictly this PR, but others that will probably follow), since the potential for breaking things downstream is relatively high. However, like Tomoya said above, we actually do support "non-standard" interface file paths; it just breaks tools like ros2 interface show.

I won't block this PR if @fujitatomoya is fine with it. I might recommend adding tests if there is still some time left, though. We add test interface files to test_interface_files and then use them (i.e., generate interfaces) in test_msgs. The test interfaces can then be used downstream, e.g., in rosidl_runtime_py to test the functions in the get_interfaces.py file.

@christophebedard
Copy link
Copy Markdown
Member

I think the Windows build failure is unrelated and should be resolved when ros2/rmw_zenoh#969 gets merged

@techtasie
Copy link
Copy Markdown
Contributor Author

techtasie commented Apr 19, 2026

@christophebedard robag is already testing for these kind of Interface. We could probably steal the test from there. https://github.com/ros2/rosbag2/tree/rolling/rosbag2_test_msgdefs/nested_sub_dir/action

I don't get why the aarch build is failing. It looks like the collection of test results failed.

But if this pr gets merged there is no difference anymore between these types of messages and they should be indistinguishable. I could add a test to makes sure that an nested interface looks the same as normal ones.

@christophebedard
Copy link
Copy Markdown
Member

There was another Windows build failure, which has now been fixed by ros2/rmw_connextdds#225, so I retriggered the Windows job.

@christophebedard christophebedard merged commit a0f6d46 into ros2:rolling Apr 21, 2026
4 checks passed
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