-
Notifications
You must be signed in to change notification settings - Fork 187
Re-Export Generated Messages in rclrs #556
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Re-Export Generated Messages in rclrs #556
Conversation
…nd on the AMENT_PREFIX_PATH env var. Able to colcon build the entire workspace.
|
@esteve I believe this is essentially an implementation of the thought you had back in #394. We could also use feature flags instead of embedding metadata in the generated crates like I did. That may cut down on some rebuilds? EDIT: Future me, we can only use feature flags for base ROS 2 messages (i.e. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very interesting approach! And that's the kind of diff that gets me excited 😄
I gave this a try and I have some comments.
First of all, removing colcon-ros-cargo to fix CI is not a fix and actually breaks CI, so it should be reverted and the CI issues should be solved.
Removing colcon-ros-cargo will actually skip all the ros-cargo packages, including rclrs, if you look at the logs from the green CI you will notice that these warnings are printed:
[0.461s] WARNING:colcon.colcon_core.verb:No task extension to 'build' a 'ros.ament_cargo' package
And the build log shows that rclrs was actually not built and not tested, so that change effectively made the CI a noop.
There are two test failures, one is a very underwhelming cargo fmt run, the other is a failure to compile a unit test (seems to me that line was added by accident?):
error[E0423]: expected value, found struct `crate::rcl_interfaces::msg::IntegerRange`
--> rclrs/src/subscription.rs:537:17
|
537 | let _ = crate::rcl_interfaces::msg::IntegerRange;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `crate::rcl_interfaces::msg::IntegerRange { from_value: val, to_value: val, step: val }`
|
::: ~/ws_rclrs/install/rcl_interfaces/share/rcl_interfaces/rust/src/msg.rs:52:1
|
52 | / pub struct IntegerRange {
53 | | pub from_value: i64,
54 | | pub to_value: i64,
55 | | pub step: u64,
56 | | }
| |_- `crate::rcl_interfaces::msg::IntegerRange` defined here
For more information about this error, try `rustc --explain E0423`.
error: could not compile `rclrs` (lib test) due to 1 previous error
Removing the line and running cargo fmt makes colcon test succeed on my machine.
Now to the questions
Open questions
- How does
AMENT_PREFIX_PATHget 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.
It's... Interesting... I gave this a try and it seems that the content on the variable contains (together with the environment variable) the prefixes of the package that the current package depends on. My guess (and what I hope colcon would do) is that behind the scenes it looks for all the packages specified in package.xml and sources them before building the required package, so the prefix contains all the required dependencies but not more than that, which is pretty good behavior.
- Should this actually go into
rclrsor a separate crate (i.e. ros_msgs?) By going into rclrs we eliminate the need for vendoring.
I'm very partial about red diffs but what is imho even more great about bundling is removing these confusing vendoring issues.
A question still stands though, I thought some of the reason for vendoring was distributing the crate standalone on crates.io. How would we go about that with this approach?
- Its important to know that if we re-build rclrs, and the
AMENT_PREFIX_PATHenv var has changed, then the build.rs will be re-built, which will then re-build all of the generated code.
Yeah this is not ideal, other people might disagree but personally I don't feel it is a definitive blocker, if the behavior is robust and developer friendly we can accept less than ideal compile times for now.
Probably a noob question but how does the script detect that the environment variable changed? It seems to work and I expected to see a rerun-if-env-changed directive but I couldn't find any.
Other general comments
I also tried to build the message_demo example and noticed that this doesn't work. I think the issue is that you are not including feature flags for message packages.
error[E0277]: the trait bound `rclrs::rclrs_example_msgs::msg::rmw::VariousTypes: serde::Deserialize<'de>` is not satisfied
--> src/message_demo.rs:110:28
|
110 | let rmw_deserialized = serde_json::from_str(&rmw_serialized)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `serde_core::de::Deserialize<'_>` is not implemented for `rclrs::rclrs_example_msgs::msg::rmw::VariousTypes`
|
= note: for local types consider adding `#[derive(serde::Deserialize)]` to your `rclrs::rclrs_example_msgs::msg::rmw::VariousTypes` type
And finally, I noticed that you implemented parsing of the environment variable into paths / splitting manually, have you considered using ament_rs to cut down on that boilerplate?
| // Find all dependencies for this crate that have a `*` version requirement. | ||
| // We will assume that these are other exported dependencies that need symbols | ||
| // exposed in their module. | ||
| let dependencies: String = Manifest::from_path(package_dir.clone().join("Cargo.toml")) | ||
| .iter() | ||
| .map(star_deps_to_use) | ||
| .collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like the main hairy part to me, does this mean that we will require message packages to always have a * version requirement? Would specifying message versions like we do in our examples not work?
On the other hand, how do we make sure we are only re-exporting message packages? What happens if a user has a Rust library installed instead? Is there a risk that if I have a foo library that is:
- A
colcon-ros-cargopackage. - Installed and in the prefix.
- Dependency (possibly with a
*version).
Then rclrs will end up bundling it and exporting rclrs::foo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should answer the last question: ros2-rust/rosidl_rust@main...maspe36:rosidl_rust:feature/relative_symbol_resolution#diff-bd0ce76b9774a1e4b569a95ae620dfb3422798d60bcf4c01ed6f501c978e3426
There is a meta tag added to the packages generated by rosidl, that we filter for.
Regarding versioning: I don't think using anything else than a * dependency make sense here.
Since the dependency resolution is managed by colcon and should be specified in the package.xml.
In the context of standalone rclrs. Maybe we could work with feature flags, to only include vendored messages given the standalone feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
require message packages to always have a * version requirement?
Not necessarily, this is just what I did in the first pass as this is actually how we're specifying dependencies in the generated crates. We could evolve this to be an intersection of dependencies found in both the Cargo.toml and package.xml, or perhaps some other mechanism like metadata in the Cargo.toml.
On the other hand, how do we make sure we are only re-exporting message packages?
I don't think we really can fully prevent this because we do want users to be able to generate and install their own rust crates representing their custom message packages.
What happens if a user has a Rust library installed instead?
I have gone ahead and removed the logic to create the marker file for crates with the expected metadata. colcon-ros-cargo won't find the generated rust crates for message packages anymore.
ros2-rust/cargo-ament-build#35
If we decide on another approach then this will need to be revisited.
- Change the CI to use a WIP version of cargo-ament-build - Remove extra line from subscription.rs which caused test failures
8b55304 to
9c309c4
Compare
…rehsed instead of completely refreshed.
Yup, fixed this by making a change to Verified that
Ah yeah, whoops. Fixed!
This is an extremely valid point, thank you for bringing this up. I would propose supplying the absolute bare minimum required to satisfy compilation and having that be conditionally included if we're doing a build for docs.rs. I'll see how feasible this really is.
Not a noob question! In my early changes I was using
https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-env-changed
We need the
I didn't find much value from this crate honestly, but I have gone ahead and used it. I could see improvements being made to it in the future. |
…brought one of my comments back.
The unused imports are because we do not necessarily use all dependencies in all modules. For example, action_msgs is often used only in the action module, but we have no way of knowing that ahead of time. I don't think it's valuable to document local modules that mock file structure.
|
I have enabled cargo docs builds in 1ff04a1 I believe all outstanding comments have been fully addressed now |
|
I haven't given this a test run yet, but conceptually the more I think about this approach the more I like it. It resolves every technical issue that I can think of, and doesn't introduce any new problems besides just being unconventional (or you could also say creative or resourceful).
This would be my only feedback at this time. I believe very strongly that this should go into its own crate, maybe named As a separate crate it would be feasible for r2r or other Rust-based ROS client library implementations to share the same message package pipeline. I think that would be a huge win for the ecosystem. As far as I can figure this would still fix the need for vendoring messages within rclrs. |
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::Stringis how you'd access the String type of the generated rust code for the std_msgs package.This depends on
The way we find the generated code is as follows
AMENT_PREFIX_PATH, look for ashare/directory.share/look for<package>/rust/Cargo.tomlwithin that entry, with the metadata to re-export symbols,include!all source files withinsrc/such that all symbols resolve the same.i.e.
std_msgs::msg::Stringfor a standalone crate build, is effectively the same symbol asrclrs::std_msgs::msg::String.I believe this to be a viable alternative to patching for message packages, and (could) effectively eliminate the need for vendoring some messages.
Open questions
How does
AMENT_PREFIX_PATHget 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.Should this actually go into
rclrsor a separate crate (i.e. ros_msgs?) By going into rclrs we eliminate the need for vendoring.Its important to know that if we re-build rclrs, and the
AMENT_PREFIX_PATHenv 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 hasextern cratecalls to force rustc to find the dependencies, but this will likely not work with any intellisense, and its overall pretty fragile IMO.