From 5a67230d5e962224ca63003e23a86f265dd8dfe8 Mon Sep 17 00:00:00 2001 From: carter Date: Sun, 17 May 2026 17:03:57 -0600 Subject: [PATCH 1/3] Update package discovery to try to resolve ROS2 paths --- roslibrust_codegen/src/lib.rs | 3 +- roslibrust_codegen/src/utils.rs | 174 +++++++++++++++++++++++++--- roslibrust_codegen_macro/README.md | 4 +- roslibrust_codegen_macro/src/lib.rs | 5 +- 4 files changed, 164 insertions(+), 22 deletions(-) diff --git a/roslibrust_codegen/src/lib.rs b/roslibrust_codegen/src/lib.rs index a2e79d06..a4f35210 100644 --- a/roslibrust_codegen/src/lib.rs +++ b/roslibrust_codegen/src/lib.rs @@ -533,11 +533,12 @@ impl PartialEq for ConstantInfo { /// modified would trigger re-generation of the source. This function is designed to /// be used either in a build.rs file or via the roslibrust_codegen_macro crate. /// * `additional_search_paths` - A list of additional paths to search beyond those -/// found in ROS_PACKAGE_PATH environment variable. +/// found in ROS_PACKAGE_PATH, AMENT_PREFIX_PATH, and COLCON_PREFIX_PATH environment variables. pub fn find_and_generate_ros_messages( additional_search_paths: Vec, ) -> Result<(TokenStream, Vec), Error> { let mut ros_package_paths = utils::get_search_paths(); + ros_package_paths.extend(utils::get_ros2_search_paths()); ros_package_paths.extend(additional_search_paths); find_and_generate_ros_messages_without_ros_package_path(ros_package_paths) } diff --git a/roslibrust_codegen/src/utils.rs b/roslibrust_codegen/src/utils.rs index 5e318f41..876696de 100644 --- a/roslibrust_codegen/src/utils.rs +++ b/roslibrust_codegen/src/utils.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::io; use std::path::{Path, PathBuf}; @@ -25,26 +25,88 @@ pub enum RosVersion { } const CATKIN_IGNORE: &str = "CATKIN_IGNORE"; +const AMENT_IGNORE: &str = "AMENT_IGNORE"; +const COLCON_IGNORE: &str = "COLCON_IGNORE"; const PACKAGE_FILE_NAME: &str = "package.xml"; const ROS_PACKAGE_PATH_ENV_VAR: &str = "ROS_PACKAGE_PATH"; +const AMENT_PREFIX_PATH_ENV_VAR: &str = "AMENT_PREFIX_PATH"; +const COLCON_PREFIX_PATH_ENV_VAR: &str = "COLCON_PREFIX_PATH"; +const AMENT_INDEX_PATH: [&str; 4] = ["share", "ament_index", "resource_index", "packages"]; pub fn get_search_paths() -> Vec { - if let Ok(paths) = std::env::var(ROS_PACKAGE_PATH_ENV_VAR) { - #[cfg(not(windows))] - let separator = ":"; - #[cfg(windows)] - let separator = ";"; - - paths - .split(separator) - .map(PathBuf::from) - .collect::>() + if let Some(paths) = std::env::var_os(ROS_PACKAGE_PATH_ENV_VAR) { + std::env::split_paths(&paths).collect() } else { log::warn!("No ROS_PACKAGE_PATH defined."); vec![] } } +pub fn get_ros2_search_paths() -> Vec { + let prefixes = [AMENT_PREFIX_PATH_ENV_VAR, COLCON_PREFIX_PATH_ENV_VAR] + .into_iter() + .flat_map(|env_var| match std::env::var_os(env_var) { + Some(paths) => std::env::split_paths(&paths).collect::>(), + None => { + log::debug!("No {env_var} defined."); + vec![] + } + }) + .collect::>(); + + get_ros2_search_paths_from_prefixes(&prefixes) +} + +/// Finds ROS 2 package share directories by reading each prefix's ament resource index. +/// +/// A ROS 2 install prefix marks packages with files under +/// `share/ament_index/resource_index/packages/`. The corresponding +/// package share directory is expected at `share/`. +pub fn get_ros2_search_paths_from_prefixes>(prefixes: &[P]) -> Vec { + let mut package_paths = Vec::new(); + let mut seen = HashSet::new(); + + for prefix in prefixes { + let prefix = prefix.as_ref(); + let package_index = AMENT_INDEX_PATH + .into_iter() + .fold(prefix.to_path_buf(), |path, component| path.join(component)); + let Ok(entries) = std::fs::read_dir(&package_index) else { + log::debug!( + "No ament package resource index found at {}", + package_index.display() + ); + continue; + }; + + for entry in entries.flatten() { + let path = entry.path(); + if !path.is_file() { + continue; + } + + let Some(package_name) = path.file_name() else { + continue; + }; + let package_path = prefix.join("share").join(package_name); + if !package_path.join(PACKAGE_FILE_NAME).is_file() { + log::warn!( + "Ament package marker {} did not have a matching package.xml at {}", + path.display(), + package_path.display() + ); + continue; + } + + if seen.insert(package_path.clone()) { + package_paths.push(package_path); + } + } + } + + package_paths +} + /// Finds ROS packages within a list of search paths. /// /// This function may panic if it reaches a maximum search depth. If this function @@ -80,13 +142,12 @@ pub fn packages_from_path(mut path: PathBuf, depth: u16) -> io::Result { version = Some(RosVersion::ROS1); } - "ament_cmake" => { + "ament_cmake" | "ament_cmake_ros" | "ament_python" => { version = Some(RosVersion::ROS2); } _ => {} @@ -276,6 +337,34 @@ fn parse_ros_package_info( #[cfg(test)] mod test { use crate::utils; + use std::fs; + use std::path::PathBuf; + use std::time::{SystemTime, UNIX_EPOCH}; + + fn temp_test_dir(name: &str) -> PathBuf { + let nanos = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + std::env::temp_dir().join(format!("roslibrust_codegen_{name}_{nanos}")) + } + + fn write_package_xml(path: &std::path::Path, package_name: &str, build_tool: &str) { + fs::write( + path.join("package.xml"), + format!( + r#" + {package_name} + 0.0.0 + test + test + MIT + {build_tool} +"# + ), + ) + .unwrap(); + } #[test] fn verify_deduplicate_packages() { @@ -309,4 +398,53 @@ mod test { let deduplicated = utils::deduplicate_packages(packages); assert_eq!(deduplicated.len(), 3); } + + #[test] + fn discovers_ros2_packages_from_ament_resource_index() { + let prefix = temp_test_dir("ament_index"); + let index = prefix.join("share/ament_index/resource_index/packages"); + let package = prefix.join("share/example_interfaces"); + let malformed_package = prefix.join("share/missing_package"); + fs::create_dir_all(&index).unwrap(); + fs::create_dir_all(&package).unwrap(); + fs::write(index.join("example_interfaces"), "").unwrap(); + fs::write(index.join("missing_package"), "").unwrap(); + write_package_xml(&package, "example_interfaces", "ament_cmake"); + + let package_paths = utils::get_ros2_search_paths_from_prefixes(&[&prefix]); + assert_eq!(package_paths, vec![package]); + assert!(!package_paths.contains(&malformed_package)); + + fs::remove_dir_all(prefix).unwrap(); + } + + #[test] + fn crawl_respects_ros2_ignore_markers() { + for ignore_file in ["AMENT_IGNORE", "COLCON_IGNORE"] { + let root = temp_test_dir(ignore_file); + let ignored_package = root.join("ignored_package"); + fs::create_dir_all(&ignored_package).unwrap(); + fs::write(ignored_package.join(ignore_file), "").unwrap(); + write_package_xml(&ignored_package, "ignored_package", "ament_cmake"); + + let packages = utils::crawl(&[&root]); + assert!(packages.is_empty()); + + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn parses_ament_python_packages_as_ros2() { + let root = temp_test_dir("ament_python"); + let package = root.join("python_package"); + fs::create_dir_all(&package).unwrap(); + write_package_xml(&package, "python_package", "ament_python"); + + let packages = utils::crawl(&[&root]); + assert_eq!(packages.len(), 1); + assert_eq!(packages[0].version, Some(utils::RosVersion::ROS2)); + + fs::remove_dir_all(root).unwrap(); + } } diff --git a/roslibrust_codegen_macro/README.md b/roslibrust_codegen_macro/README.md index cf1de332..97b92ad7 100644 --- a/roslibrust_codegen_macro/README.md +++ b/roslibrust_codegen_macro/README.md @@ -6,6 +6,8 @@ This macro cannot detect if the message files it generates from have changed. If ## Usage If you're generating messages in an environment with ROS installed, no arguments need to be passed. +ROS1 packages are found through `ROS_PACKAGE_PATH`; ROS2 packages are found through +the ament resource indexes in `AMENT_PREFIX_PATH` and `COLCON_PREFIX_PATH`. ```rust use roslibrust_codegen_macro::find_and_generate_ros_messages; @@ -19,4 +21,4 @@ If you're generating without ROS installed or your environment can't depend on t use roslibrust_codegen_macro::find_and_generate_ros_messages; find_and_generate_ros_messages!("/path/to/my/msg/package", "/opt/ros/noetic"); -``` \ No newline at end of file +``` diff --git a/roslibrust_codegen_macro/src/lib.rs b/roslibrust_codegen_macro/src/lib.rs index 7484c630..217f38a8 100644 --- a/roslibrust_codegen_macro/src/lib.rs +++ b/roslibrust_codegen_macro/src/lib.rs @@ -26,8 +26,9 @@ impl Parse for RosLibRustMessagePaths { /// ros messages found within those paths. /// Paths are relative to where rustc is being invoked from your mileage may vary. /// -/// In addition to provided paths, this will search paths found in the environment -/// variable ROS_PACKAGE_PATH. +/// In addition to provided paths, this will search ROS1 paths found in +/// `ROS_PACKAGE_PATH` and ROS2 packages found through the ament resource indexes +/// in `AMENT_PREFIX_PATH` and `COLCON_PREFIX_PATH`. #[proc_macro] pub fn find_and_generate_ros_messages(input_stream: TokenStream) -> TokenStream { // Note: there is not currently a way for proc_macros to indicate that they need to be re-generated From 74d548895b8fe29889acdd6e1e96f7d80148d69d Mon Sep 17 00:00:00 2001 From: carter Date: Sun, 17 May 2026 17:30:33 -0600 Subject: [PATCH 2/3] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 189eaac5..840aa8ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - ~20% performance improvement on large images and strings with ROS1 and ROS1-zenoh backends by removing copies, and improving pre-allocation +- The macro for code generation will now search BOTH ROS1 and ROS2 package paths by default. ## 0.20.0 - March 2nd, 2026 From 9824650e6940598e991edfae50297b1eac0644a8 Mon Sep 17 00:00:00 2001 From: carter Date: Sun, 17 May 2026 18:05:28 -0600 Subject: [PATCH 3/3] Update examples to use explicit paths instead of Environment, update naming of our macros to be more sane and shorter --- CHANGELOG.md | 1 + README.md | 4 +- example_package_macro/src/main.rs | 7 +-- roslibrust/examples/generic_client.rs | 2 +- .../examples/generic_client_services.rs | 2 +- roslibrust/examples/generic_message.rs | 4 +- .../examples/ros1_async_service_server.rs | 2 +- roslibrust/examples/ros1_listener.rs | 2 +- roslibrust/examples/ros1_publish_any.rs | 2 +- roslibrust/examples/ros1_service_client.rs | 2 +- roslibrust/examples/ros1_service_server.rs | 2 +- roslibrust/examples/ros1_talker.rs | 2 +- .../examples/rosbridge_basic_publisher.rs | 2 +- .../examples/rosbridge_calling_service.rs | 2 +- .../rosbridge_ros1_ros2_bridge_example.rs | 6 +- .../examples/rosbridge_service_server.rs | 2 +- .../examples/rosbridge_subscribe_and_log.rs | 2 +- .../examples/tokio_stream_operations.rs | 2 +- roslibrust/src/lib.rs | 4 ++ roslibrust_codegen_macro/README.md | 11 ++-- roslibrust_codegen_macro/src/lib.rs | 55 +++++++++++++------ roslibrust_rosapi/src/lib.rs | 2 +- roslibrust_zenoh/examples/publisher.rs | 2 +- roslibrust_zenoh/examples/subscriber.rs | 2 +- .../examples/zenoh_service_call.rs | 2 +- .../examples/zenoh_service_server.rs | 2 +- 26 files changed, 75 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 840aa8ef..53e3081d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ~20% performance improvement on large images and strings with ROS1 and ROS1-zenoh backends by removing copies, and improving pre-allocation - The macro for code generation will now search BOTH ROS1 and ROS2 package paths by default. +- Added code generation proc macros with shorter names: `generate_ros_types!` searches only explicit paths, `generate_ros_types_with_env!` searches explicit paths plus ROS environment paths, and the original macro names are deprecated aliases. ## 0.20.0 - March 2nd, 2026 diff --git a/README.md b/README.md index 43c5cc5a..2e354561 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,8 @@ All of this is backed by common traits for ROS messages, topics, and services. ` `roslibrust_codegen_macro` provides a convenient macro for generating these types: ```rust,ignore -// Will generate types from all packages in ROS_PACKAGE_PATH -roslibrust_codegen_macro::find_and_generate_ros_messages!(); +// Will generate types from all packages in ROS_PACKAGE_PATH, AMENT_PREFIX_PATH, and COLCON_PREFIX_PATH +roslibrust_codegen_macro::generate_ros_types_with_env!(); ``` If you want to see what the generated code looks like checkout [our generated messages in our test crate](https://github.com/RosLibRust/roslibrust/blob/master/roslibrust_test/src/ros1.rs). diff --git a/example_package_macro/src/main.rs b/example_package_macro/src/main.rs index 0590c981..9f2b57ab 100644 --- a/example_package_macro/src/main.rs +++ b/example_package_macro/src/main.rs @@ -3,12 +3,9 @@ use roslibrust::Publish; // We're using the macro to generate our types -// We provide the macro with additional search paths beyond the ROS_PACKAGE_PATH +// We provide the macro with the exact paths to search, skipping environment discovery. // The paths are assumed to be relative to the crate (or workspace) root -roslibrust::find_and_generate_ros_messages!( - "assets/ros1_test_msgs", - "assets/ros1_common_interfaces" -); +roslibrust::generate_ros_types!("assets/ros1_test_msgs", "assets/ros1_common_interfaces"); // Writing a simple behavior that uses the generic traits from roslibrust // and the generated types from the macro above. diff --git a/roslibrust/examples/generic_client.rs b/roslibrust/examples/generic_client.rs index 59a74b30..531c3fc2 100644 --- a/roslibrust/examples/generic_client.rs +++ b/roslibrust/examples/generic_client.rs @@ -4,7 +4,7 @@ // Important to bring these traits into scope so we can use them #[cfg(all(feature = "rosbridge", feature = "ros1"))] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces/std_msgs"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces/std_msgs"); #[cfg(all(feature = "rosbridge", feature = "ros1"))] #[tokio::main] diff --git a/roslibrust/examples/generic_client_services.rs b/roslibrust/examples/generic_client_services.rs index b6e48021..7442d122 100644 --- a/roslibrust/examples/generic_client_services.rs +++ b/roslibrust/examples/generic_client_services.rs @@ -2,7 +2,7 @@ //! to create code that is generic of which communication backend it will use. #[cfg(all(feature = "rosbridge", feature = "ros1"))] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); #[cfg(all(feature = "rosbridge", feature = "ros1"))] #[tokio::main] diff --git a/roslibrust/examples/generic_message.rs b/roslibrust/examples/generic_message.rs index d66c18e0..429b5281 100644 --- a/roslibrust/examples/generic_message.rs +++ b/roslibrust/examples/generic_message.rs @@ -5,11 +5,11 @@ use roslibrust_common::RosMessageType; /// We place the ros1 generated code into a module to prevent name collisions with the identically /// named ros2 types. mod ros1 { - roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); + roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); } mod ros2 { - roslibrust_codegen_macro::find_and_generate_ros_messages_without_ros_package_path!( + roslibrust_codegen_macro::generate_ros_types!( "assets/ros2_common_interfaces", "assets/ros2_required_msgs/rcl_interfaces/builtin_interfaces" ); diff --git a/roslibrust/examples/ros1_async_service_server.rs b/roslibrust/examples/ros1_async_service_server.rs index 14dc1550..d700ff0c 100644 --- a/roslibrust/examples/ros1_async_service_server.rs +++ b/roslibrust/examples/ros1_async_service_server.rs @@ -1,5 +1,5 @@ #[cfg(feature = "ros1")] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); /// This example shows how to perform async actions correctly in a service callback. /// diff --git a/roslibrust/examples/ros1_listener.rs b/roslibrust/examples/ros1_listener.rs index 75e279bc..4aeb86cb 100644 --- a/roslibrust/examples/ros1_listener.rs +++ b/roslibrust/examples/ros1_listener.rs @@ -1,5 +1,5 @@ #[cfg(feature = "ros1")] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces/std_msgs"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces/std_msgs"); #[cfg(feature = "ros1")] #[tokio::main] diff --git a/roslibrust/examples/ros1_publish_any.rs b/roslibrust/examples/ros1_publish_any.rs index 29a7b3c2..e785293f 100644 --- a/roslibrust/examples/ros1_publish_any.rs +++ b/roslibrust/examples/ros1_publish_any.rs @@ -1,5 +1,5 @@ #[cfg(feature = "ros1")] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); /// This example demonstrates ths usage of the .advertise_any() function /// diff --git a/roslibrust/examples/ros1_service_client.rs b/roslibrust/examples/ros1_service_client.rs index f13fdd24..6421c28f 100644 --- a/roslibrust/examples/ros1_service_client.rs +++ b/roslibrust/examples/ros1_service_client.rs @@ -1,5 +1,5 @@ #[cfg(feature = "ros1")] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); #[cfg(feature = "ros1")] #[tokio::main] diff --git a/roslibrust/examples/ros1_service_server.rs b/roslibrust/examples/ros1_service_server.rs index 0b658dbd..9c8daa33 100644 --- a/roslibrust/examples/ros1_service_server.rs +++ b/roslibrust/examples/ros1_service_server.rs @@ -1,5 +1,5 @@ #[cfg(feature = "ros1")] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); #[cfg(feature = "ros1")] #[tokio::main] diff --git a/roslibrust/examples/ros1_talker.rs b/roslibrust/examples/ros1_talker.rs index 790d2ed1..51e1cf36 100644 --- a/roslibrust/examples/ros1_talker.rs +++ b/roslibrust/examples/ros1_talker.rs @@ -1,5 +1,5 @@ #[cfg(feature = "ros1")] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); #[cfg(feature = "ros1")] #[tokio::main] diff --git a/roslibrust/examples/rosbridge_basic_publisher.rs b/roslibrust/examples/rosbridge_basic_publisher.rs index 4981a06a..dfc193f1 100644 --- a/roslibrust/examples/rosbridge_basic_publisher.rs +++ b/roslibrust/examples/rosbridge_basic_publisher.rs @@ -1,5 +1,5 @@ #[cfg(feature = "rosbridge")] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces/std_msgs"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces/std_msgs"); /// This example creates a client, and publishes a message to the topic "talker" /// Running this example at the same time as subscribe_and_log will have the two examples diff --git a/roslibrust/examples/rosbridge_calling_service.rs b/roslibrust/examples/rosbridge_calling_service.rs index 6793db2d..9656a7ad 100644 --- a/roslibrust/examples/rosbridge_calling_service.rs +++ b/roslibrust/examples/rosbridge_calling_service.rs @@ -1,5 +1,5 @@ #[cfg(feature = "rosbridge")] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces/rosapi"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces/rosapi"); /// This example shows calling a service /// To run this example rosbridge and a roscore should be running diff --git a/roslibrust/examples/rosbridge_ros1_ros2_bridge_example.rs b/roslibrust/examples/rosbridge_ros1_ros2_bridge_example.rs index bbbaef06..a8a78574 100644 --- a/roslibrust/examples/rosbridge_ros1_ros2_bridge_example.rs +++ b/roslibrust/examples/rosbridge_ros1_ros2_bridge_example.rs @@ -5,13 +5,11 @@ /// We place the ros1 generate code into a module to prevent name collisions with the identically /// named ros2 types. mod ros1 { - roslibrust_codegen_macro::find_and_generate_ros_messages!( - "assets/ros1_common_interfaces/std_msgs" - ); + roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces/std_msgs"); } mod ros2 { - roslibrust_codegen_macro::find_and_generate_ros_messages_without_ros_package_path!( + roslibrust_codegen_macro::generate_ros_types!( "assets/ros2_common_interfaces/std_msgs", "assets/ros2_required_msgs/rcl_interfaces/builtin_interfaces" ); diff --git a/roslibrust/examples/rosbridge_service_server.rs b/roslibrust/examples/rosbridge_service_server.rs index b3e99d9b..7b9453d9 100644 --- a/roslibrust/examples/rosbridge_service_server.rs +++ b/roslibrust/examples/rosbridge_service_server.rs @@ -1,6 +1,6 @@ // One way to import message definitions: #[cfg(feature = "rosbridge")] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); // A basic service server exampple, that logs the request is recieves and returns // a canned response. diff --git a/roslibrust/examples/rosbridge_subscribe_and_log.rs b/roslibrust/examples/rosbridge_subscribe_and_log.rs index d2651037..a3a5751a 100644 --- a/roslibrust/examples/rosbridge_subscribe_and_log.rs +++ b/roslibrust/examples/rosbridge_subscribe_and_log.rs @@ -1,5 +1,5 @@ #[cfg(feature = "rosbridge")] -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); /// A basic example of connecting and subscribing to data. /// This example will log received messages if run at the same time as "basic_publisher". diff --git a/roslibrust/examples/tokio_stream_operations.rs b/roslibrust/examples/tokio_stream_operations.rs index 6cd75efb..451b8c05 100644 --- a/roslibrust/examples/tokio_stream_operations.rs +++ b/roslibrust/examples/tokio_stream_operations.rs @@ -2,7 +2,7 @@ use roslibrust_common::traits::*; use tokio_stream::StreamExt; -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); async fn example_publisher_task(publisher: impl roslibrust::Publish) { for seq in 0..50 { diff --git a/roslibrust/src/lib.rs b/roslibrust/src/lib.rs index aeba5516..9986f32f 100644 --- a/roslibrust/src/lib.rs +++ b/roslibrust/src/lib.rs @@ -29,3 +29,7 @@ pub use roslibrust_codegen as codegen; pub use roslibrust_codegen_macro::find_and_generate_ros_messages; #[cfg(feature = "macro")] pub use roslibrust_codegen_macro::find_and_generate_ros_messages_without_ros_package_path; +#[cfg(feature = "macro")] +pub use roslibrust_codegen_macro::generate_ros_types; +#[cfg(feature = "macro")] +pub use roslibrust_codegen_macro::generate_ros_types_with_env; diff --git a/roslibrust_codegen_macro/README.md b/roslibrust_codegen_macro/README.md index 97b92ad7..dbc7ca12 100644 --- a/roslibrust_codegen_macro/README.md +++ b/roslibrust_codegen_macro/README.md @@ -10,15 +10,16 @@ ROS1 packages are found through `ROS_PACKAGE_PATH`; ROS2 packages are found thro the ament resource indexes in `AMENT_PREFIX_PATH` and `COLCON_PREFIX_PATH`. ```rust -use roslibrust_codegen_macro::find_and_generate_ros_messages; +use roslibrust_codegen_macro::generate_ros_types_with_env; -find_and_generate_ros_messages!(); +generate_ros_types_with_env!(); ``` -If you're generating without ROS installed or your environment can't depend on the `ROS_PACKAGE_PATH` variable, you can specify additional paths to search: +If you're generating without ROS installed or your environment can't depend on ROS +environment variables, you can specify the exact paths to search: ```rust -use roslibrust_codegen_macro::find_and_generate_ros_messages; +use roslibrust_codegen_macro::generate_ros_types; -find_and_generate_ros_messages!("/path/to/my/msg/package", "/opt/ros/noetic"); +generate_ros_types!("/path/to/my/msg/package", "/opt/ros/noetic"); ``` diff --git a/roslibrust_codegen_macro/src/lib.rs b/roslibrust_codegen_macro/src/lib.rs index 217f38a8..921b0855 100644 --- a/roslibrust_codegen_macro/src/lib.rs +++ b/roslibrust_codegen_macro/src/lib.rs @@ -22,17 +22,7 @@ impl Parse for RosLibRustMessagePaths { } } -/// Given a list of paths, generates struct definitions and trait impls for any -/// ros messages found within those paths. -/// Paths are relative to where rustc is being invoked from your mileage may vary. -/// -/// In addition to provided paths, this will search ROS1 paths found in -/// `ROS_PACKAGE_PATH` and ROS2 packages found through the ament resource indexes -/// in `AMENT_PREFIX_PATH` and `COLCON_PREFIX_PATH`. -#[proc_macro] -pub fn find_and_generate_ros_messages(input_stream: TokenStream) -> TokenStream { - // Note: there is not currently a way for proc_macros to indicate that they need to be re-generated - // We discard the "dependent_paths" part of the response here... +fn generate_with_environment(input_stream: TokenStream) -> TokenStream { let RosLibRustMessagePaths { paths } = parse_macro_input!(input_stream as RosLibRustMessagePaths); match roslibrust_codegen::find_and_generate_ros_messages(paths) { @@ -44,12 +34,7 @@ pub fn find_and_generate_ros_messages(input_stream: TokenStream) -> TokenStream } } -/// Similar to `find_and_generate_ros_messages`, but does not search the -/// `ROS_PACKAGE_PATH` environment variable paths (useful in some situations). -#[proc_macro] -pub fn find_and_generate_ros_messages_without_ros_package_path( - input_stream: TokenStream, -) -> TokenStream { +fn generate_from_paths_only(input_stream: TokenStream) -> TokenStream { let RosLibRustMessagePaths { paths } = parse_macro_input!(input_stream as RosLibRustMessagePaths); match roslibrust_codegen::find_and_generate_ros_messages_without_ros_package_path(paths) { @@ -62,3 +47,39 @@ pub fn find_and_generate_ros_messages_without_ros_package_path( } } } + +/// Generates struct definitions and trait impls for ROS types found in the +/// paths provided to the macro, skipping ROS environment discovery. +#[proc_macro] +pub fn generate_ros_types(input_stream: TokenStream) -> TokenStream { + // Note: there is not currently a way for proc_macros to indicate that they need to be re-generated + // We discard the "dependent_paths" part of the response here... + generate_from_paths_only(input_stream) +} + +/// Generates struct definitions and trait impls for ROS types found in the +/// paths provided to the macro plus ROS1 paths from `ROS_PACKAGE_PATH` and ROS2 +/// packages found through ament resource indexes in `AMENT_PREFIX_PATH` and +/// `COLCON_PREFIX_PATH`. +#[proc_macro] +pub fn generate_ros_types_with_env(input_stream: TokenStream) -> TokenStream { + // Note: there is not currently a way for proc_macros to indicate that they need to be re-generated + // We discard the "dependent_paths" part of the response here... + generate_with_environment(input_stream) +} + +/// Deprecated alias for [`generate_ros_types_with_env`]. +#[deprecated(note = "use generate_ros_types_with_env! instead")] +#[proc_macro] +pub fn find_and_generate_ros_messages(input_stream: TokenStream) -> TokenStream { + generate_with_environment(input_stream) +} + +/// Deprecated alias for [`generate_ros_types`]. +#[deprecated(note = "use generate_ros_types! instead")] +#[proc_macro] +pub fn find_and_generate_ros_messages_without_ros_package_path( + input_stream: TokenStream, +) -> TokenStream { + generate_from_paths_only(input_stream) +} diff --git a/roslibrust_rosapi/src/lib.rs b/roslibrust_rosapi/src/lib.rs index 15158bb0..4652d152 100644 --- a/roslibrust_rosapi/src/lib.rs +++ b/roslibrust_rosapi/src/lib.rs @@ -9,7 +9,7 @@ use roslibrust::ServiceProvider; // This macro isn't going to expand correctly when not used from this crate's workspace // We almost certainly need to generate and commit the resulting messages, or // do some include_str!() hax to be able to ship these types with the crate... -roslibrust::find_and_generate_ros_messages!("assets/ros1_common_interfaces/rosapi"); +roslibrust::generate_ros_types!("assets/ros1_common_interfaces/rosapi"); /// Represents the ability to interact with the interfaces provided by the rosapi node. /// This trait is implemented for ClientHandle when the `rosapi` feature is enabled. diff --git a/roslibrust_zenoh/examples/publisher.rs b/roslibrust_zenoh/examples/publisher.rs index 53fc23d2..fe2a5948 100644 --- a/roslibrust_zenoh/examples/publisher.rs +++ b/roslibrust_zenoh/examples/publisher.rs @@ -8,7 +8,7 @@ use roslibrust::Publish; use roslibrust::TopicProvider; // Generate rust definitions for our messages -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); // The example expects a zenoh-ros1-bridge to be running see [here](https://github.com/eclipse-zenoh/zenoh-plugin-ros1) // for details on running the bridge. diff --git a/roslibrust_zenoh/examples/subscriber.rs b/roslibrust_zenoh/examples/subscriber.rs index fba700db..01f6d21d 100644 --- a/roslibrust_zenoh/examples/subscriber.rs +++ b/roslibrust_zenoh/examples/subscriber.rs @@ -8,7 +8,7 @@ use roslibrust::Subscribe; use roslibrust::TopicProvider; // Generate rust definitions for our messages -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); // The example expects a zenoh-ros1-bridge to be running see [here](https://github.com/eclipse-zenoh/zenoh-plugin-ros1) // for details on running the bridge. diff --git a/roslibrust_zenoh/examples/zenoh_service_call.rs b/roslibrust_zenoh/examples/zenoh_service_call.rs index 214f47c5..56f1cc70 100644 --- a/roslibrust_zenoh/examples/zenoh_service_call.rs +++ b/roslibrust_zenoh/examples/zenoh_service_call.rs @@ -12,7 +12,7 @@ use roslibrust::ServiceProvider; use roslibrust_zenoh::ZenohClient; // Generate rust definitions for our messages compatible with roslibrust -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); #[tokio::main] async fn main() { diff --git a/roslibrust_zenoh/examples/zenoh_service_server.rs b/roslibrust_zenoh/examples/zenoh_service_server.rs index 52d20719..f5a425ba 100644 --- a/roslibrust_zenoh/examples/zenoh_service_server.rs +++ b/roslibrust_zenoh/examples/zenoh_service_server.rs @@ -10,7 +10,7 @@ use roslibrust_zenoh::ZenohClient; // Otherwise the service will not be advertised to ROS1 master. // Generate rust definitions for our messages -roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces"); +roslibrust_codegen_macro::generate_ros_types!("assets/ros1_common_interfaces"); #[tokio::main] async fn main() {