Skip to content

Commit

Permalink
Merge execution structure PR
Browse files Browse the repository at this point in the history
Signed-off-by: Michael X. Grey <greyxmike@gmail.com>
  • Loading branch information
mxgrey committed Dec 9, 2024
2 parents 22d2c84 + 0918476 commit f1c4716
Show file tree
Hide file tree
Showing 69 changed files with 3,790 additions and 627 deletions.
23 changes: 13 additions & 10 deletions .github/workflows/rust.yml → .github/workflows/rust-minimal.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
name: Rust
name: Rust Minimal

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * *'
# Run the CI at 02:22 UTC every Tuesday
# We pick an arbitrary time outside of most of the world's work hours
# to minimize the likelihood of running alongside a heavy workload.
- cron: '22 2 * * 2'

env:
CARGO_TERM_COLOR: always
Expand Down Expand Up @@ -42,7 +45,11 @@ jobs:
- name: Search packages in this repository
id: list_packages
run: |
echo ::set-output name=package_list::$(colcon list --names-only)
{
echo 'package_list<<EOF'
colcon list --names-only
echo EOF
} >> "$GITHUB_OUTPUT"
- name: Setup ROS environment
uses: ros-tooling/setup-ros@v0.7
Expand All @@ -51,7 +58,7 @@ jobs:
use-ros2-testing: ${{ matrix.ros_distribution == 'rolling' }}

- name: Setup Rust
uses: dtolnay/rust-toolchain@1.74.0
uses: dtolnay/rust-toolchain@1.75
with:
components: clippy, rustfmt

Expand All @@ -69,10 +76,6 @@ jobs:
cd -
done
- name: Install cargo-ament-build
run: |
cargo install --debug cargo-ament-build
- name: Build and test
id: build
uses: ros-tooling/action-ros-ci@v0.3
Expand All @@ -90,9 +93,9 @@ jobs:
echo "Running clippy in $path"
# Run clippy for all features except generate_docs (needed for docs.rs)
if [ "$(basename $path)" = "rclrs" ]; then
cargo clippy --all-targets -F default,dyn_msg -- -D warnings
cargo clippy --no-deps --all-targets -F default,dyn_msg -- -D warnings
else
cargo clippy --all-targets --all-features -- -D warnings
cargo clippy --no-deps --all-targets --all-features -- -D warnings
fi
cd -
done
Expand Down
130 changes: 130 additions & 0 deletions .github/workflows/rust-stable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: Rust Stable

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
# Run the CI at 02:22 UTC every Tuesday
# We pick an arbitrary time outside of most of the world's work hours
# to minimize the likelihood of running alongside a heavy workload.
- cron: '22 2 * * 2'

env:
CARGO_TERM_COLOR: always

jobs:
build:
strategy:
matrix:
ros_distribution:
- humble
- iron
- rolling
include:
# Humble Hawksbill (May 2022 - May 2027)
- docker_image: rostooling/setup-ros-docker:ubuntu-jammy-ros-humble-ros-base-latest
ros_distribution: humble
ros_version: 2
# Iron Irwini (May 2023 - November 2024)
- docker_image: rostooling/setup-ros-docker:ubuntu-jammy-ros-iron-ros-base-latest
ros_distribution: iron
ros_version: 2
# Rolling Ridley (June 2020 - Present)
- docker_image: rostooling/setup-ros-docker:ubuntu-jammy-ros-rolling-ros-base-latest
ros_distribution: rolling
ros_version: 2
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.ros_distribution == 'rolling' }}
container:
image: ${{ matrix.docker_image }}
steps:
- uses: actions/checkout@v4

- name: Search packages in this repository
id: list_packages
run: |
{
echo 'package_list<<EOF'
colcon list --names-only
echo EOF
} >> "$GITHUB_OUTPUT"
- name: Setup ROS environment
uses: ros-tooling/setup-ros@v0.7
with:
required-ros-distributions: ${{ matrix.ros_distribution }}
use-ros2-testing: ${{ matrix.ros_distribution == 'rolling' }}

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt

- name: Install colcon-cargo and colcon-ros-cargo
run: |
sudo pip3 install git+https://github.com/colcon/colcon-cargo.git
sudo pip3 install git+https://github.com/colcon/colcon-ros-cargo.git
- name: Check formatting of Rust packages
run: |
for path in $(colcon list | awk '$3 == "(ament_cargo)" { print $2 }'); do
cd $path
rustup toolchain install nightly
cargo +nightly fmt -- --check
cd -
done
- name: Build and test
id: build
uses: ros-tooling/action-ros-ci@v0.3
with:
package-name: ${{ steps.list_packages.outputs.package_list }}
target-ros2-distro: ${{ matrix.ros_distribution }}
vcs-repo-file-url: ros2_rust_${{ matrix.ros_distribution }}.repos

- name: Run clippy on Rust packages
run: |
cd ${{ steps.build.outputs.ros-workspace-directory-name }}
. /opt/ros/${{ matrix.ros_distribution }}/setup.sh
for path in $(colcon list | awk '$3 == "(ament_cargo)" { print $2 }'); do
cd $path
echo "Running clippy in $path"
# Run clippy for all features except generate_docs (needed for docs.rs)
if [ "$(basename $path)" = "rclrs" ]; then
cargo clippy --no-deps --all-targets -F default,dyn_msg -- -D warnings
else
cargo clippy --no-deps --all-targets --all-features -- -D warnings
fi
cd -
done
- name: Run cargo test on Rust packages
run: |
cd ${{ steps.build.outputs.ros-workspace-directory-name }}
. install/setup.sh
for path in $(colcon list | awk '$3 == "(ament_cargo)" && $1 != "examples_rclrs_minimal_pub_sub" && $1 != "examples_rclrs_minimal_client_service" && $1 != "rust_pubsub" { print $2 }'); do
cd $path
echo "Running cargo test in $path"
# Run cargo test for all features except generate_docs (needed for docs.rs)
if [ "$(basename $path)" = "rclrs" ]; then
cargo test -F default,dyn_msg
elif [ "$(basename $path)" = "rosidl_runtime_rs" ]; then
cargo test -F default
else
cargo test --all-features
fi
cd -
done
- name: Rustdoc check
run: |
cd ${{ steps.build.outputs.ros-workspace-directory-name }}
. /opt/ros/${{ matrix.ros_distribution }}/setup.sh
for path in $(colcon list | awk '$3 == "(ament_cargo)" && $1 != "examples_rclrs_minimal_pub_sub" && $1 != "examples_rclrs_minimal_client_service" && $1 != "rust_pubsub" { print $2 }'); do
cd $path
echo "Running rustdoc check in $path"
cargo rustdoc -- -D warnings
cd -
done
5 changes: 2 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ RUN apt-get update && apt-get install -y \
python3-pip \
&& rm -rf /var/lib/apt/lists/*

# Install Rust and the cargo-ament-build plugin
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain 1.74.0 -y
# Install Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain 1.75.0 -y
ENV PATH=/root/.cargo/bin:$PATH
RUN cargo install cargo-ament-build

RUN pip install --upgrade pytest

Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
ROS 2 for Rust
==============

| Target | Status |
|----------|--------|
| **Ubuntu 20.04** | [![Build Status](https://github.com/ros2-rust/ros2_rust/actions/workflows/rust.yml/badge.svg?branch=main)](https://github.com/ros2-rust/ros2_rust/actions/workflows/rust.yml?branch=main) |
[![Minimal Version Status](https://github.com/ros2-rust/ros2_rust/actions/workflows/rust-minimal.yml/badge.svg?branch=main)](https://github.com/ros2-rust/ros2_rust/actions/workflows/rust-minimal.yml)
[![Stable CI Status](https://github.com/ros2-rust/ros2_rust/actions/workflows/rust-stable.yml/badge.svg?branch=main)](https://github.com/ros2-rust/ros2_rust/actions/workflows/rust-stable.yml)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

Introduction
------------
Expand Down Expand Up @@ -37,7 +37,6 @@ Here are the steps for building the `ros2_rust` examples in a vanilla Ubuntu Foc
# Assuming you installed the minimal version of ROS 2, you need these additional packages:
sudo apt install -y git libclang-dev python3-pip python3-vcstool # libclang-dev is required by bindgen
# Install these plugins for cargo and colcon:
cargo install --debug cargo-ament-build # --debug is faster to install
pip install git+https://github.com/colcon/colcon-cargo.git
pip install git+https://github.com/colcon/colcon-ros-cargo.git

Expand Down
1 change: 0 additions & 1 deletion docs/building.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ The exact steps may differ between platforms, but as an example, here is how you
# Assuming you installed the minimal version of ROS 2, you need these additional packages:
sudo apt install -y git libclang-dev python3-pip python3-vcstool # libclang-dev is required by bindgen
# Install these plugins for cargo and colcon:
cargo install cargo-ament-build
pip install git+https://github.com/colcon/colcon-cargo.git
pip install git+https://github.com/colcon/colcon-ros-cargo.git
```
Expand Down
49 changes: 49 additions & 0 deletions docs/message-generation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# `ros2_rust` Message Generation

The `ros2_rust` project strives to maintain consistency with the upstream ROS 2 message generation
system. To this end, it provides two main packages: `rosidl_generator_rs` and `rosidl_runtime_rs`.
These packages provide the infrastructure required for defining and using ROS 2 messages, services,
and actions in Rust.

At a high level, the `rosidl_generator_rs` package handles generation of interface crates from the
`.msg`, `.srv`, and `.action` files defined by a user. The `rosidl_runtime_rs` package provides
common functionality shared across message packages, such as support types and traits. Each of these
packages is described in more detail below.

## `rosidl_generator_rs`

`rosidl_generator_rs` follows a very similar pattern to the other message generation packages for
ROS 2. To tie into this pipeline, it registers itself as a `"rosidl_generate_idl_interfaces"`
extension with `ament`. Doing so ensures that message packages calling `rosidl_generate_interfaces`
will invoke the Rust language generator in addition to any others. This is accomplished using the
various CMake scripts under the `cmake` directory. When this happens, the input interface files will
be converted into IDL files which, along with additional metadata, are fed into the `generate_rs`
function of `rosidl_generator_rs/__init__.py`.

From here, the IDL files are parsed into an internal representation using the upstream
[`rosidl_parser`](https://github.com/ros2/rosidl/tree/rolling/rosidl_parser) package. This abstract
representation is then used to instantiate the various template files under the `resource`
subdirectory, producing a full Rust crate for each package.

For each input message type, two `struct`s are generated:

- An ergonomic representation of the message, using more idiomatic `std` types like `Vec` and
`String` for sequence and string fields. These are placed directly in the `msg` submodule of the
crate.
- A FFI-suitable `struct` that is ABI-compatible with the layout expected by the RMW layer. While
less ergonomic, these avoid the conversion overhead when passed to RMW. These `struct`s are placed
under the `msg::rmw` submodule.

All the produced `struct`s implement the standard traits from `std` when possible, such as `Clone`,
`PartialEq`, and `Debug`. Additionally, when the generated crate's `serde` feature is enabled, these
structs implement the `Serialize` and `Deserialize` traits.

## `rosidl_runtime_rs`

`rosidl_runtime_rs` is a runtime support package, providing `Message`, `Service`, and `Action`
traits that are implemented by the corresponding structs generated by `rosidl_generator_rs`. These
allow for generic interaction with these various interface types, both in client libraries like
`rclrs` and in user code.

The package also provides a number of string and sequence types that are ABI-compatible with their
`rosidl_runtime_c` equivalents. This allows for more ergonomic use of the RMW-native message types.
10 changes: 5 additions & 5 deletions examples/message_demo/src/message_demo.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{convert::TryInto, env, sync::Arc};
use std::convert::TryInto;

use anyhow::{Error, Result};
use rosidl_runtime_rs::{seq, BoundedSequence, Message, Sequence};
Expand Down Expand Up @@ -138,8 +138,8 @@ fn demonstrate_sequences() {
fn demonstrate_pubsub() -> Result<(), Error> {
println!("================== Interoperability demo ==================");
// Demonstrate interoperability between idiomatic and RMW-native message types
let context = rclrs::Context::new(env::args())?;
let node = rclrs::create_node(&context, "message_demo")?;
let mut executor = rclrs::Context::default_from_env()?.create_basic_executor();
let node = executor.create_node("message_demo")?;

let idiomatic_publisher = node.create_publisher::<rclrs_example_msgs::msg::VariousTypes>(
"topic",
Expand All @@ -166,10 +166,10 @@ fn demonstrate_pubsub() -> Result<(), Error> {
)?;
println!("Sending idiomatic message.");
idiomatic_publisher.publish(rclrs_example_msgs::msg::VariousTypes::default())?;
rclrs::spin_once(Arc::clone(&node), None)?;
executor.spin(rclrs::SpinOptions::spin_once())?;
println!("Sending RMW-native message.");
direct_publisher.publish(rclrs_example_msgs::msg::rmw::VariousTypes::default())?;
rclrs::spin_once(Arc::clone(&node), None)?;
executor.spin(rclrs::SpinOptions::spin_once())?;

Ok(())
}
Expand Down
10 changes: 5 additions & 5 deletions examples/minimal_client_service/src/minimal_client.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use std::env;

use anyhow::{Error, Result};

fn main() -> Result<(), Error> {
let context = rclrs::Context::new(env::args())?;
let mut executor = rclrs::Context::default_from_env()?.create_basic_executor();

let node = rclrs::create_node(&context, "minimal_client")?;
let node = executor.create_node("minimal_client")?;

let client = node.create_client::<example_interfaces::srv::AddTwoInts>("add_two_ints")?;

Expand All @@ -30,5 +28,7 @@ fn main() -> Result<(), Error> {
std::thread::sleep(std::time::Duration::from_millis(500));

println!("Waiting for response");
rclrs::spin(node).map_err(|err| err.into())
executor
.spin(rclrs::SpinOptions::default())
.map_err(|err| err.into())
}
9 changes: 4 additions & 5 deletions examples/minimal_client_service/src/minimal_client_async.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use std::env;

use anyhow::{Error, Result};

#[tokio::main]
async fn main() -> Result<(), Error> {
let context = rclrs::Context::new(env::args())?;
let mut executor = rclrs::Context::default_from_env()?.create_basic_executor();

let node = rclrs::create_node(&context, "minimal_client")?;
let node = executor.create_node("minimal_client")?;

let client = node.create_client::<example_interfaces::srv::AddTwoInts>("add_two_ints")?;

Expand All @@ -22,7 +20,8 @@ async fn main() -> Result<(), Error> {

println!("Waiting for response");

let rclrs_spin = tokio::task::spawn_blocking(move || rclrs::spin(node));
let rclrs_spin =
tokio::task::spawn_blocking(move || executor.spin(rclrs::SpinOptions::default()));

let response = future.await?;
println!(
Expand Down
10 changes: 5 additions & 5 deletions examples/minimal_client_service/src/minimal_service.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::env;

use anyhow::{Error, Result};

fn handle_service(
Expand All @@ -13,13 +11,15 @@ fn handle_service(
}

fn main() -> Result<(), Error> {
let context = rclrs::Context::new(env::args())?;
let mut executor = rclrs::Context::default_from_env()?.create_basic_executor();

let node = rclrs::create_node(&context, "minimal_service")?;
let node = executor.create_node("minimal_service")?;

let _server = node
.create_service::<example_interfaces::srv::AddTwoInts, _>("add_two_ints", handle_service)?;

println!("Starting server");
rclrs::spin(node).map_err(|err| err.into())
executor
.spin(rclrs::SpinOptions::default())
.map_err(|err| err.into())
}
Loading

0 comments on commit f1c4716

Please sign in to comment.