Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7555d27
Working example of re-exporting of the generated interface crates fou…
maspe36 Dec 5, 2025
998e618
Swap humble to custom rosidl branch
maspe36 Dec 9, 2025
5adf35c
Swap jazzy to custom rosidl branch
maspe36 Dec 9, 2025
2591d9d
Swap kilted to custom rosidl branch
maspe36 Dec 9, 2025
f6a532e
Swap rolling to custom rosidl branch
maspe36 Dec 9, 2025
2099c7d
- Streamline parts of the build script to get closer to docs.rs builds.
maspe36 Jan 4, 2026
3ce38ec
Lets use https instead of ssh?
maspe36 Jan 4, 2026
bb14a67
Make sure we can build rclrs for rust 1.75!
maspe36 Jan 4, 2026
670f71d
Be more specific about the pin
maspe36 Jan 4, 2026
d7d34af
Make sure we pass `cargo fmt`
maspe36 Jan 4, 2026
aeb476a
Refresh the build script to use ament_rs
maspe36 Jan 4, 2026
37cbeca
Okay, that was a bad idea, lets go back and make it slightly more ref…
maspe36 Jan 4, 2026
4251c98
Make sure the changes are formatted with the nightly formatter. Also …
maspe36 Jan 5, 2026
fe41df8
Turn off some lints locally for unused imports and missing docs.
maspe36 Jan 8, 2026
a2d9da0
Enable building docs with use_ros_shim feature!
maspe36 Jan 10, 2026
cfba217
cargo_toml 0.14 has been yanked. Let's try going to 0.22 now that we'…
maspe36 Mar 9, 2026
16750e8
Try to fix the unit tests
maspe36 Mar 9, 2026
c1e3b25
Fix cargo fmt
maspe36 Mar 9, 2026
a09f90c
Use the latest rosidl_rust!
maspe36 Mar 9, 2026
b101521
Move re-export logic from rclrs, to the new ros-env crate.
maspe36 Mar 15, 2026
97501ba
Update rosidl_rust version to main
maspe36 Apr 11, 2026
d145197
undo changes for cargo-ament-build now that upstream contains the mar…
esteve May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/writing-your-first-rclrs-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Instead, you can store the node as a regular member. Let's add a struct that con

```rust
use std::sync::Arc;
use std_msgs::msg::String as StringMsg;
use rclrs::std_msgs::msg::String as StringMsg;

struct RepublisherNode {
node: Arc<rclrs::Node>,
Expand Down Expand Up @@ -111,7 +111,7 @@ So, to store the received data in the struct, the following things have to chang

```rust
use std::sync::{Arc, Mutex}; // (1)
use std_msgs::msg::String as StringMsg;
use rclrs::std_msgs::msg::String as StringMsg;

struct RepublisherNode {
node: Arc<rclrs::Node>,
Expand Down
8 changes: 4 additions & 4 deletions docs/writing_a_simple_publisher_and_subscriber.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ To construct a node, replace the code in your `main.rs` file with the following:
/// methods to publish a simple "Hello World" message on a loop in separate threads.
use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT};
use std::{env, sync::Arc, thread, time::Duration};
use std_msgs::msg::String as StringMsg;
use rclrs::std_msgs::msg::String as StringMsg;
/// SimplePublisherNode struct contains node and publisher members.
/// Used to initialize a ROS 2 node and publisher, and publish messages.
struct SimplePublisherNode {
Expand Down Expand Up @@ -138,7 +138,7 @@ handling, iteration, threading, ROS 2 communication, and string message publishi
```rust
use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT};
use std::{env, sync::Arc, thread, time::Duration};
use std_msgs::msg::String as StringMsg;
use rclrs::std_msgs::msg::String as StringMsg;
```
* `use std::{sync::Arc, time::Duration, iter, thread};`: Imports specific features from the standard library:
- `Arc` is for thread-safe shared ownership of data.
Expand All @@ -149,7 +149,7 @@ use std_msgs::msg::String as StringMsg;
- `RclrsError` for handling errors.
- `QOS_PROFILE_DEFAULT` for default Quality of Service settings.
- `Context, create_node, Node, Publisher` are for ROS 2 node creation and publishing.
* `use std_msgs::msg::String as StringMsg;`: Imports the `StringMsg` type for publishing string messages.
* `use rclrs::std_msgs::msg::String as StringMsg;`: Imports the `StringMsg` type for publishing string messages.

#### `SimplePublisherNode`
Next, this structure defines a `SimplePublisherNode` which holds references to a ROS 2 node and a publisher for string messages.
Expand Down Expand Up @@ -291,7 +291,7 @@ use std::{
thread,
time::Duration,
};
use std_msgs::msg::String as StringMsg;
use rclrs::std_msgs::msg::String as StringMsg;
pub struct SimpleSubscriptionNode {
node: Arc<Node>,
_subscriber: Arc<Subscription<StringMsg>>,
Expand Down
11 changes: 9 additions & 2 deletions rclrs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ async-std = "1.13"
# Needed for dynamic messages
libloading = "0.8"

ros-env = "0.1"

# Needed for the Message trait, among others
rosidl_runtime_rs = "0.6"

Expand All @@ -54,6 +56,8 @@ tokio-stream = "0.1"
# Needed by action clients to generate UUID values for their goals
uuid = { version = "1", features = ["v4"] }

paste = { version = "1", optional = true}

[dev-dependencies]
# Needed for e.g. writing yaml files in tests
tempfile = "3.3.0"
Expand All @@ -65,12 +69,15 @@ tokio = { version = "1", features = ["rt", "time", "macros"] }
cfg-if = "1.0.0"
rustflags = "0.1"

# Helper crate for working with AMENT_PREFIX_PATH
ament_rs = "0.3"

[features]
default = []
serde = ["dep:serde", "dep:serde-big-array", "rosidl_runtime_rs/serde"]
serde = ["dep:serde", "dep:serde-big-array", "rosidl_runtime_rs/serde", "ros-env/serde"]
# This feature is solely for the purpose of being able to generate documetation without a ROS installation
# The only intended usage of this feature is for docs.rs builders to work, and is not intended to be used by end users
use_ros_shim = ["rosidl_runtime_rs/use_ros_shim"]
use_ros_shim = ["paste", "rosidl_runtime_rs/use_ros_shim"]

[package.metadata.docs.rs]
features = ["use_ros_shim"]
Expand Down
87 changes: 38 additions & 49 deletions rclrs/build.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,52 @@
use std::{env, path::Path};
const AMENT_PREFIX_PATH: &str = "AMENT_PREFIX_PATH";
use ament_rs::search_paths::get_search_paths;
use std::{env, path::PathBuf};

const ROS_DISTRO: &str = "ROS_DISTRO";
const KNOWN_DISTROS: &[&str] = &["humble", "jazzy", "kilted", "rolling"];

fn get_env_var_or_abort(env_var: &'static str) -> String {
if let Ok(value) = env::var(env_var) {
value
} else {
panic!(
"{} environment variable not set - please source ROS 2 installation first.",
env_var
);
}
fn get_ros_distro() -> String {
env::var(ROS_DISTRO)
.or_else(|_| {
if env::var("CARGO_FEATURE_USE_ROS_SHIM").is_ok() {
rustflags::from_env()
.find_map(|f| match f {
rustflags::Flag::Cfg { name, value } if name.as_str() == "ros_distro" => {
value
}
_ => None,
})
.ok_or_else(|| "Missing --cfg ros_distro in RUSTFLAGS".to_string())
} else {
Err(format!("Set {ROS_DISTRO} or use ROS shim"))
}
})
.expect("Failed to determine ROS distro")
}

fn main() {
println!(
"cargo:rustc-check-cfg=cfg(ros_distro, values(\"{}\"))",
["humble", "jazzy", "kilted", "rolling"].join("\", \"")
KNOWN_DISTROS.join("\", \"")
);
let ros_distro = if let Ok(value) = env::var(ROS_DISTRO) {
value
} else {
cfg_if::cfg_if! {
if #[cfg(feature="use_ros_shim")] {
use rustflags;
// // Look for --cfg ros_distro=<ros_distro>
for flag in rustflags::from_env() {
if matches!(flag, rustflags::Flag::Cfg { ref name, value : _ } if name == "ros_distro") {
if let rustflags::Flag::Cfg {name:_, value: flag_value} = flag {
println!("cargo:rustc-cfg=ros_distro=\"{}\"", flag_value.unwrap());
return;
} else {
continue;
}
}
}
let error_msg =
"When using the use_ros_shim feature, you must pass the ROS distribution you are targeting as a compiler flag with --cfg ros_distro=\"<ros_distro>\"";
panic!("{}", error_msg);
} else {
let error_msg =
"ROS_DISTRO environment variable not set - please source ROS 2 installation first.";
panic!("{}", error_msg);
}
}
};
println!("cargo:rustc-cfg=ros_distro=\"{ros_distro}\"");
println!("cargo:rustc-cfg=ros_distro=\"{}\"", get_ros_distro());
println!("cargo:rerun-if-env-changed={ROS_DISTRO}");

let ament_prefix_paths = get_search_paths().unwrap_or_default();

let ament_prefix_paths = get_env_var_or_abort(AMENT_PREFIX_PATH);
for ament_prefix_path in ament_prefix_paths.split(':').map(Path::new) {
for ament_prefix_path in &ament_prefix_paths {
// Link the native libraries
let library_path = ament_prefix_path.join("lib");
let library_path = PathBuf::from(ament_prefix_path).join("lib");
println!("cargo:rustc-link-search=native={}", library_path.display());
}

println!("cargo:rustc-link-lib=dylib=rcl");
println!("cargo:rustc-link-lib=dylib=rcl_action");
println!("cargo:rustc-link-lib=dylib=rcl_yaml_param_parser");
println!("cargo:rustc-link-lib=dylib=rcutils");
println!("cargo:rustc-link-lib=dylib=rmw");
println!("cargo:rustc-link-lib=dylib=rmw_implementation");
[
"rcl",
"rcl_action",
"rcl_yaml_param_parser",
"rcutils",
"rmw",
"rmw_implementation",
]
.iter()
.for_each(|lib| println!("cargo:rustc-link-lib=dylib={lib}"));
}
13 changes: 6 additions & 7 deletions rclrs/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ pub use action_goal_receiver::*;
pub(crate) mod action_server;
pub use action_server::*;

use crate::{log_error, rcl_bindings::*, vendor::builtin_interfaces::msg::Time, DropGuard};
use crate::{log_error, rcl_bindings::*, DropGuard};
use ros_env::builtin_interfaces::msg::Time;
use std::fmt;

#[cfg(feature = "serde")]
Expand Down Expand Up @@ -255,13 +256,11 @@ fn empty_goal_status_array() -> DropGuard<rcl_action_goal_status_array_t> {

#[cfg(test)]
mod tests {
use crate::{
vendor::example_interfaces::action::{
Fibonacci, Fibonacci_Feedback, Fibonacci_Goal, Fibonacci_Result,
},
*,
};
use crate::*;
use futures::StreamExt;
use ros_env::example_interfaces::action::{
Fibonacci, Fibonacci_Feedback, Fibonacci_Goal, Fibonacci_Result,
};
use std::time::Duration;
use tokio::sync::mpsc::unbounded_channel;

Expand Down
12 changes: 5 additions & 7 deletions rclrs/src/action/action_client.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use super::empty_goal_status_array;
use crate::{
log_warn,
rcl_bindings::*,
vendor::{action_msgs::srv::CancelGoal_Response, builtin_interfaces::msg::Time},
CancelResponse, CancelResponseCode, DropGuard, GoalStatus, GoalStatusCode, GoalUuid,
MultiCancelResponse, Node, NodeHandle, QoSProfile, RclPrimitive, RclPrimitiveHandle,
RclPrimitiveKind, RclrsError, ReadyKind, TakeFailedAsNone, ToResult, Waitable,
WaitableLifecycle, ENTITY_LIFECYCLE_MUTEX,
log_warn, rcl_bindings::*, CancelResponse, CancelResponseCode, DropGuard, GoalStatus,
GoalStatusCode, GoalUuid, MultiCancelResponse, Node, NodeHandle, QoSProfile, RclPrimitive,
RclPrimitiveHandle, RclPrimitiveKind, RclrsError, ReadyKind, TakeFailedAsNone, ToResult,
Waitable, WaitableLifecycle, ENTITY_LIFECYCLE_MUTEX,
};
use ros_env::{action_msgs::srv::CancelGoal_Response, builtin_interfaces::msg::Time};
use rosidl_runtime_rs::{Action, Message, RmwFeedbackMessage, RmwGoalResponse, RmwResultResponse};
use std::{
any::Any,
Expand Down
6 changes: 3 additions & 3 deletions rclrs/src/action/action_client/goal_client.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
vendor::builtin_interfaces::msg::Time, CancellationClient, FeedbackClient, GoalStatus,
GoalStatusCode, ResultClient, StatusWatcher,
CancellationClient, FeedbackClient, GoalStatus, GoalStatusCode, ResultClient, StatusWatcher,
};
use ros_env::builtin_interfaces::msg::Time;
use rosidl_runtime_rs::Action;
use std::{
pin::Pin,
Expand Down Expand Up @@ -96,7 +96,7 @@ impl<A: Action> GoalClient<A> {
///
/// ```
/// use rclrs::*;
/// use crate::rclrs::vendor::example_interfaces::action::Fibonacci;
/// use ros_env::example_interfaces::action::Fibonacci;
/// use futures::StreamExt;
///
/// async fn process_goal_client_stream(
Expand Down
4 changes: 2 additions & 2 deletions rclrs/src/action/action_server.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use super::empty_goal_status_array;
use crate::{
action::GoalUuid, error::ToResult, rcl_bindings::*,
vendor::action_msgs::srv::CancelGoal_Response, ActionGoalReceiver, CancelResponseCode,
action::GoalUuid, error::ToResult, rcl_bindings::*, ActionGoalReceiver, CancelResponseCode,
DropGuard, GoalStatusCode, Node, NodeHandle, QoSProfile, RclPrimitive, RclPrimitiveHandle,
RclPrimitiveKind, RclrsError, ReadyKind, TakeFailedAsNone, Waitable, WaitableLifecycle,
ENTITY_LIFECYCLE_MUTEX,
};
use futures::future::BoxFuture;
use ros_env::action_msgs::srv::CancelGoal_Response;
use rosidl_runtime_rs::{Action, Message, RmwGoalRequest, RmwResultRequest};
use std::{
any::Any,
Expand Down
12 changes: 5 additions & 7 deletions rclrs/src/action/action_server/cancellation_state.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
use super::ActionServerHandle;
use crate::{
log_error,
rcl_bindings::*,
vendor::{
action_msgs::{msg::GoalInfo, srv::CancelGoal_Response},
unique_identifier_msgs::msg::UUID,
},
CancelResponseCode, GoalUuid, Node, RclrsErrorFilter, ToResult,
log_error, rcl_bindings::*, CancelResponseCode, GoalUuid, Node, RclrsErrorFilter, ToResult,
};
use futures::{
future::{select, Either},
pin_mut,
};
use futures_lite::future::race;
use ros_env::{
action_msgs::{msg::GoalInfo, srv::CancelGoal_Response},
unique_identifier_msgs::msg::UUID,
};
use rosidl_runtime_rs::{Action, Message};
use std::{
borrow::Cow,
Expand Down
9 changes: 5 additions & 4 deletions rclrs/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ where
/// signatures and which returns a `()` (a.k.a. nothing).
/// ```
/// # use rclrs::*;
/// # use crate::rclrs::vendor::test_msgs;
/// # use ros_env::test_msgs;
/// # let node = Context::default()
/// # .create_basic_executor()
/// # .create_node("test_node")?;
Expand Down Expand Up @@ -191,7 +191,7 @@ where
///
/// ```
/// # use rclrs::*;
/// # use crate::rclrs::vendor::test_msgs;
/// # use ros_env::test_msgs;
/// # use std::future::Future;
/// # let node = Context::default()
/// # .create_basic_executor()
Expand Down Expand Up @@ -220,7 +220,7 @@ where
///
/// ```
/// # use rclrs::*;
/// # use crate::rclrs::vendor::test_msgs;
/// # use ros_env::test_msgs;
/// # let node = Context::default()
/// # .create_basic_executor()
/// # .create_node("test_node")?;
Expand Down Expand Up @@ -609,7 +609,8 @@ unsafe impl Send for rcl_client_t {}
#[cfg(test)]
mod tests {
use super::*;
use crate::{test_helpers::*, vendor::test_msgs};
use crate::test_helpers::*;
use ros_env::test_msgs;

#[test]
fn traits() {
Expand Down
16 changes: 10 additions & 6 deletions rclrs/src/dynamic_message/dynamic_publisher.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use std::ffi::CString;
use std::sync::{Arc, Mutex};
use std::{
ffi::CString,
sync::{Arc, Mutex},
};

use super::{
get_type_support_handle, get_type_support_library, DynamicMessage, DynamicMessageError,
DynamicMessageMetadata, MessageTypeName,
};
use crate::error::{RclrsError, ToResult};
use crate::rcl_bindings::*;
use crate::{Node, PublisherHandle, PublisherOptions, ENTITY_LIFECYCLE_MUTEX};
use crate::{
error::{RclrsError, ToResult},
rcl_bindings::*,
Node, PublisherHandle, PublisherOptions, ENTITY_LIFECYCLE_MUTEX,
};

/// Struct for sending dynamic messages.
///
Expand Down Expand Up @@ -150,8 +154,8 @@ mod tests {

#[test]
fn test_dynamic_publishers() -> Result<(), RclrsError> {
use crate::vendor::test_msgs::msg;
use crate::TopicEndpointInfo;
use ros_env::test_msgs::msg;

let namespace = "/test_dynamic_publishers_graph";
let graph = construct_test_graph(namespace)?;
Expand Down
20 changes: 11 additions & 9 deletions rclrs/src/dynamic_message/dynamic_subscription.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
use std::any::Any;
use std::boxed::Box;
use std::ffi::CString;
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};
use std::{
any::Any,
boxed::Box,
ffi::CString,
ops::{Deref, DerefMut},
sync::{Arc, Mutex},
};

use futures::future::BoxFuture;

use super::{
get_type_support_handle, get_type_support_library, DynamicMessage, DynamicMessageMetadata,
MessageStructure, MessageTypeName,
};
use crate::rcl_bindings::*;
use crate::{
MessageInfo, Node, NodeHandle, RclPrimitive, RclPrimitiveHandle, RclPrimitiveKind, RclrsError,
RclrsErrorFilter, ReadyKind, SubscriptionHandle, SubscriptionOptions, ToResult, Waitable,
WaitableLifecycle, WorkScope, Worker, WorkerCommands, ENTITY_LIFECYCLE_MUTEX,
rcl_bindings::*, MessageInfo, Node, NodeHandle, RclPrimitive, RclPrimitiveHandle,
RclPrimitiveKind, RclrsError, RclrsErrorFilter, ReadyKind, SubscriptionHandle,
SubscriptionOptions, ToResult, Waitable, WaitableLifecycle, WorkScope, Worker, WorkerCommands,
ENTITY_LIFECYCLE_MUTEX,
};

/// Struct for receiving messages whose type is not known at compile time.
Expand Down
Loading
Loading