Skip to content

Conversation

@maspe36
Copy link
Collaborator

@maspe36 maspe36 commented Dec 9, 2025

Summary

Working example of re-exporting of the generated interface crates found on the AMENT_PREFIX_PATH env var. With this change generated interface symbols are available directly from rclrs::, so for example, rclrs::std_msgs::msg::String is how you'd access the String type of the generated rust code for the std_msgs package.

This depends on ros2-rust/rosidl_rust#12 for fully relocatable symbols.

The way we find the generated code is as follows

  1. For all entries in AMENT_PREFIX_PATH, look for a share/ directory.
  2. For all entries in share/ look for <package>/rust/
  3. If there is a Cargo.toml within that entry, with the metadata to re-export symbols, include! all source files within src/ such that all symbols resolve the same.

i.e. std_msgs::msg::String for a standalone crate build, is effectively the same symbol as rclrs::std_msgs::msg::String.

There is still some work needed here, namely I do not have tests fully passing, but I wanted to get this out there early for folks to see it as an option. I believe it to be a viable alternative to patching, and also effectively eliminates the need for vendoring some messages.

Open questions

  1. How does AMENT_PREFIX_PATH get populated? If its populated as colcon is building packages (which I believe to be the case), then this will also re-export symbols in your own workspace as well as sourced overlays.

  2. Should this actually go into rclrs or a separate crate (i.e. ros_msgs?) By going into rclrs we eliminate the need for vendoring.

  3. Its important to know that if we re-build rclrs, and the AMENT_PREFIX_PATH env var has changed, then the build.rs will be re-built, which will then re-build all of the generated code.

Honestly, I'm not sure if we can really get around this. Rust just really wants you to recompile the code you're using each time you invoke cargo. We can and should have everything in a colcon workspace share a target directory, but this won't stop all rebuilds. Its possible we expect compiled .rlibs in the share directory and we generate a file which has extern crate calls to force rustc to find the dependencies, but this will likely not work with any intellisense, and its overall pretty fragile IMO.

  1. Can we get better error messages? If a user forgot to source something or expects an interface to exists but one doesn't, they are met with errors like this
  error[E0433]: failed to resolve: could not find `action_msgs` in the crate root
   --> rclrs/src/action/action_client.rs:5:6
    |
  5 |     {action_msgs::srv::CancelGoal_Response, builtin_interfaces::msg::Time},
    |      ^^^^^^^^^^^ could not find `action_msgs` in the crate root

…nd on the AMENT_PREFIX_PATH env var.

Able to colcon build the entire workspace.
let dest_path = PathBuf::from(out_dir).join("interfaces.rs");

// TODO I would like to run rustfmt on this generated code, similar to how bindgen does it
fs::write(dest_path, content.clone()).expect("Failed to write interfaces.rs");
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

interface.rs ends up looking like this

pub mod builtin_interfaces { ... };

pub mod std_msgs {
  pub mod msg {
    use crate::builtin_interfaces;
    // use crate::...; etc
    
    include!("path/to/generated/code");
    
    pub mod rmw {
      use crate::builtin_interfaces;
      // use crate::...; etc
    
      include!("path/to/generated/rmw/code");
    }
  }
}

Where all generated interface code is placed into a module based on the paths found in the AMENT_PREFIX_PATH share directories.


Interestingly enough, I also learned that the #[path = "..."] attribute for modules works with absolute paths. So that also could work, but I didn't have proper intellisense with that approach so I stuck with include!()


mod rcl_bindings;

include!(concat!(env!("OUT_DIR"), "/interfaces.rs"));
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This generates a lot of warnings right now. Will need to look at how to hygienically do that (or move to another crate and suppress there)

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.

1 participant