Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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 = "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 @@ -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