Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f95f294
Working example of re-exporting of the generated interface crates fou…
maspe36 Dec 5, 2025
bb22553
Swap humble to custom rosidl branch
maspe36 Dec 9, 2025
846197d
Swap jazzy to custom rosidl branch
maspe36 Dec 9, 2025
8a4d20b
Swap kilted to custom rosidl branch
maspe36 Dec 9, 2025
1e3cd47
Swap rolling to custom rosidl branch
maspe36 Dec 9, 2025
879c126
- Streamline parts of the build script to get closer to docs.rs builds.
maspe36 Jan 4, 2026
116d006
Lets use https instead of ssh?
maspe36 Jan 4, 2026
12ce745
Make sure we can build rclrs for rust 1.75!
maspe36 Jan 4, 2026
1123657
Be more specific about the pin
maspe36 Jan 4, 2026
c5c2917
Make sure we pass `cargo fmt`
maspe36 Jan 4, 2026
3d81a99
Refresh the build script to use ament_rs
maspe36 Jan 4, 2026
2fefa45
Okay, that was a bad idea, lets go back and make it slightly more ref…
maspe36 Jan 4, 2026
5916204
Make sure the changes are formatted with the nightly formatter. Also …
maspe36 Jan 5, 2026
a94da35
Turn off some lints locally for unused imports and missing docs.
maspe36 Jan 8, 2026
2eae32e
Enable building docs with use_ros_shim feature!
maspe36 Jan 10, 2026
79642ac
cargo_toml 0.14 has been yanked. Let's try going to 0.22 now that we'…
maspe36 Mar 9, 2026
77830f3
Try to fix the unit tests
maspe36 Mar 9, 2026
389d7c8
Fix cargo fmt
maspe36 Mar 9, 2026
a9cca62
Use the latest rosidl_rust!
maspe36 Mar 9, 2026
e4e9d2c
Move re-export logic from rclrs, to the new ros-env crate.
maspe36 Mar 15, 2026
d33186b
Update rosidl_rust version to main
maspe36 Apr 11, 2026
bbb9358
Verify ros-env works on windows
maspe36 Apr 11, 2026
459798f
Add ros-env to all repos for the test
maspe36 Apr 12, 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
3 changes: 2 additions & 1 deletion .github/workflows/rust-minimal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,12 @@ jobs:

# Colcon can not be run in a venv which is required in Ubuntu Noble
# Removing the externally managed file
- name: Install colcon-cargo and colcon-ros-cargo
- name: Install colcon-cargo, colcon-ros-cargo, and cargo-ament-build
run: |
sudo rm -f /usr/lib/python3.12/EXTERNALLY-MANAGED
sudo pip3 install git+https://github.com/colcon/colcon-cargo.git
sudo pip3 install git+https://github.com/colcon/colcon-ros-cargo.git
cargo install --git https://git@github.com/maspe36/cargo-ament-build.git --branch feature/skip_reexport_marker_file

- name: Check formatting of Rust packages
run: |
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/rust-stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,12 @@ jobs:

# Colcon can not be run in a venv which is required in Ubuntu Noble
# Removing the externally managed file
- name: Install colcon-cargo and colcon-ros-cargo
- name: Install colcon-cargo, colcon-ros-cargo, and cargo-ament-build
run: |
sudo rm -f /usr/lib/python3.12/EXTERNALLY-MANAGED
sudo pip3 install git+https://github.com/colcon/colcon-cargo.git
sudo pip3 install git+https://github.com/colcon/colcon-ros-cargo.git
cargo install --git https://git@github.com/maspe36/cargo-ament-build.git --branch feature/skip_reexport_marker_file

- name: Check formatting of Rust packages
run: |
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/rust-win.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ jobs:
# prerequisites and fixes for windows build ros2_rust:
# * Libclang has to be added (from the ros2_rust instructions) and the dll has to be renamed
# * colcon-ros-cargo and colcon-cargo have to be added as PyPI packages
# * cargo-ament-build is manually installed from github for now to include the change to skip
# installing the marker file if the package should be reexported
run: |
pixi add libclang --manifest-path C:\pixi_ws\pixi.toml
$src = "C:\pixi_ws\.pixi\envs\default\Library\bin\libclang-13.dll"
Expand All @@ -48,6 +50,7 @@ jobs:
pixi add --pypi "colcon-ros-cargo@git+https://github.com/colcon/colcon-ros-cargo.git" --manifest-path C:\pixi_ws\pixi.toml
pixi add --pypi "colcon-cargo@git+https://github.com/colcon/colcon-cargo.git" --manifest-path C:\pixi_ws\pixi.toml
pixi upgrade colcon-core --manifest-path C:\pixi_ws\pixi.toml
cargo install --git https://git@github.com/maspe36/cargo-ament-build.git --branch feature/skip_reexport_marker_file

- name: Get prebuilt ROS files and unzip
run: |
Expand Down
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 = { path = "../../ros-env" }

# 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 @@ -159,7 +159,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 @@ -187,7 +187,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 @@ -216,7 +216,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 @@ -568,7 +568,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
Loading
Loading