Skip to content

Commit 9309c1c

Browse files
committed
micro-ROS Rolling add CI
Signed-off-by: David Laseca Perez <davidlaseca@eprosima.com>
1 parent f5436e7 commit 9309c1c

19 files changed

Lines changed: 727 additions & 233 deletions

‎.github/workflows/ubuntu-ci.yml‎

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
name: ubuntu CI micro-ROS rcl
2+
3+
# The container image is derived from the branch under test, which works
4+
# because the branch names are the distro names:
5+
# - humble -> ros:humble-ros-base (Ubuntu 22.04 jammy)
6+
# - jazzy -> ros:jazzy-ros-base (Ubuntu 24.04 noble)
7+
# - kilted -> ros:kilted-ros-base (Ubuntu 24.04 noble)
8+
# - lyrical -> ros:lyrical-ros-base (Ubuntu 26.04 resolute)
9+
# - rolling -> ros:rolling-ros-base (Ubuntu 26.04 resolute)
10+
#
11+
# rcl is tested against rmw_microxrcedds, the middleware micro-ROS actually ships.
12+
# `-DRCL_MICROROS=ON` only selects rcl's stripped code paths; it says nothing about
13+
# the RMW, so without the stack below rcl would bind to the image's rmw_fastrtps_cpp
14+
# and none of this fork's target middleware would ever be exercised.
15+
#
16+
# That means building the XRCE client stack, the type support generator and the Agent
17+
# from source (none of them are released into a ROS distro), plus rebuilding every
18+
# interface package the tests use: the image's binary test_msgs carries DDS type
19+
# support only, and under XRCE all serialization happens on the client.
20+
#
21+
# rcl registers its tests once per *available* RMW (call_for_each_rmw_implementation),
22+
# so one build yields both __rmw_microxrcedds and __rmw_fastrtps_cpp entries and the
23+
# two test steps below select between them with ctest. Note that rcl_action registers
24+
# only part of its suite that way and rcl_lifecycle none of it: those tests carry no
25+
# RMW suffix and follow the RMW_IMPLEMENTATION in the environment instead.
26+
#
27+
# Besides push and pull_request, this is callable via workflow_call so a scheduled
28+
# workflow on the default branch can run it once per distro branch. Note that
29+
# `uses: ./.github/workflows/ci.yml` always resolves to the *caller's* copy of this
30+
# file, so the default branch's copy is what runs for every branch it tests; only
31+
# the checked-out source and the container image follow `inputs.branch`.
32+
33+
on:
34+
push:
35+
branches:
36+
- humble
37+
- jazzy
38+
- kilted
39+
- lyrical
40+
- rolling
41+
pull_request:
42+
branches:
43+
- humble
44+
- jazzy
45+
- kilted
46+
- lyrical
47+
- rolling
48+
workflow_call:
49+
inputs:
50+
branch:
51+
description: >
52+
Distro branch to check out and test. Leave unset for push and
53+
pull_request, where the triggering ref is used instead.
54+
type: string
55+
required: false
56+
default: ''
57+
# To enable manual trigger
58+
workflow_dispatch:
59+
60+
concurrency:
61+
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.branch }}
62+
cancel-in-progress: true
63+
64+
defaults:
65+
run:
66+
shell: bash
67+
68+
jobs:
69+
rcl_microros_ci:
70+
runs-on: ubuntu-latest
71+
# inputs.branch is used when this workflow is called
72+
# github.base_ref covers pull requests
73+
# github.ref_name is the branch on a push
74+
container: ros:${{ inputs.branch || github.base_ref || github.ref_name }}-ros-base
75+
timeout-minutes: 90
76+
77+
steps:
78+
- uses: actions/checkout@v5
79+
with:
80+
# Empty on push and pull_request, which leaves checkout's own behaviour
81+
# intact — importantly the PR merge ref, so PRs keep testing the merge
82+
# result rather than the base branch.
83+
ref: ${{ inputs.branch }}
84+
path: src/rcl
85+
86+
- name: Download dependencies
87+
run: |
88+
: "${ROS_DISTRO:?not set — is the container a ros:<distro>-ros-base image?}"
89+
. /opt/ros/$ROS_DISTRO/setup.bash
90+
91+
# The image ships no apt lists (every layer of its Dockerfile ends in
92+
# `rm -rf /var/lib/apt/lists/*`) and rosdep does not refresh them itself,
93+
# so this is required before rosdep can install anything.
94+
apt-get update
95+
apt-get install -y git
96+
97+
# Micro-CDR and the XRCE Client are distro independent and pinned; the RMW,
98+
# the type support generator and the Agent wrapper track the distro.
99+
git clone -b v2.0.2 https://github.com/eProsima/Micro-CDR src/Micro-CDR
100+
git clone -b v3.0.2 https://github.com/eProsima/Micro-XRCE-DDS-Client src/Micro-XRCE-DDS-Client
101+
git clone -b $ROS_DISTRO https://github.com/micro-ROS/rmw_microxrcedds src/rmw_microxrcedds
102+
git clone -b $ROS_DISTRO https://github.com/micro-ROS/rosidl_typesupport_microxrcedds src/rosidl_typesupport_microxrcedds
103+
git clone -b $ROS_DISTRO https://github.com/micro-ROS/micro-ROS-Agent src/micro-ROS-Agent
104+
touch src/rosidl_typesupport_microxrcedds/test/COLCON_IGNORE
105+
106+
# The tests' interface packages must be rebuilt from source so the micro-ROS
107+
# type support is generated for them; the binaries in the image have DDS type
108+
# support only. rcl_interfaces carries test_msgs, action_msgs, lifecycle_msgs,
109+
# service_msgs, builtin_interfaces and type_description_interfaces.
110+
git clone -b $ROS_DISTRO https://github.com/ros2/rcl_interfaces src/rcl_interfaces
111+
git clone -b $ROS_DISTRO https://github.com/ros2/test_interface_files src/test_interface_files
112+
git clone -b $ROS_DISTRO https://github.com/ros2/unique_identifier_msgs src/unique_identifier_msgs
113+
114+
# rosdep is already initialised in the image; only its cache is stale.
115+
# microcdr, microxrcedds_client and microxrcedds_agent are built from source
116+
# here (the Agent's superbuild clones the XRCE Agent itself), so skip those keys.
117+
rosdep update --rosdistro $ROS_DISTRO
118+
rosdep install --from-paths src --ignore-src --rosdistro $ROS_DISTRO -y \
119+
--skip-keys "microcdr microxrcedds_client microxrcedds_agent"
120+
121+
- name: Build
122+
id: build
123+
# GitHub replaces the image's entrypoint for container jobs, so
124+
# /ros_entrypoint.sh never runs and each step must source ROS itself.
125+
#
126+
# The build is staged because ordering is load-bearing: nothing *declares* a
127+
# dependency on the type support generator — rosidl_generate_interfaces finds
128+
# generators through the ament index at configure time — so an interface package
129+
# configured before the generator is installed silently ends up without
130+
# micro-ROS type support. Re-sourcing between stages is what makes it visible.
131+
# Staging also gives per-package cmake args without a colcon.meta.
132+
run: |
133+
. /opt/ros/$ROS_DISTRO/setup.bash
134+
135+
# 1. Type support generators, so every interface package built later sees them
136+
colcon build \
137+
--event-handlers console_direct+ \
138+
--packages-up-to rosidl_typesupport_microxrcedds_c rosidl_typesupport_microxrcedds_cpp \
139+
--cmake-args -DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=ON
140+
. install/local_setup.bash
141+
142+
# 2. The RMW under test and the XRCE client, sized with micro-ROS's own host
143+
# profile (micro_ros_setup/config/host/generic/client-host-colcon.meta). The
144+
# pools are per process and the stock 4 is exceeded immediately; the Agent
145+
# address is compile-time, so it has to match the Agent command below.
146+
# UCLIENT_PROFILE_MULTITHREAD reaches rmw_microxrcedds through the client's
147+
# installed config.h and is what guards both the shared XRCE session and the
148+
# RMW's own static mempools — rcl's tests drive them from several threads.
149+
# This stage must precede rcl: call_for_each_rmw_implementation only emits the
150+
# __rmw_microxrcedds test entries if the RMW is installed when rcl is configured.
151+
colcon build \
152+
--event-handlers console_direct+ \
153+
--packages-up-to rmw_microxrcedds \
154+
--cmake-args -DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=ON \
155+
-DUCLIENT_PROFILE_MULTITHREAD=ON \
156+
-DRMW_UXRCE_TRANSPORT=udp \
157+
-DRMW_UXRCE_DEFAULT_UDP_IP=127.0.0.1 -DRMW_UXRCE_DEFAULT_UDP_PORT=8888 \
158+
-DRMW_UXRCE_MAX_NODES=15 -DRMW_UXRCE_MAX_PUBLISHERS=15 \
159+
-DRMW_UXRCE_MAX_SUBSCRIPTIONS=15 -DRMW_UXRCE_MAX_SERVICES=15 \
160+
-DRMW_UXRCE_MAX_CLIENTS=15 \
161+
-DRMW_UXRCE_STREAM_HISTORY=32 -DRMW_UXRCE_MAX_HISTORY=10 \
162+
-DRMW_UXRCE_TOPIC_NAME_MAX_LENGTH=120 -DRMW_UXRCE_TYPE_NAME_MAX_LENGTH=150
163+
. install/local_setup.bash
164+
165+
# 3. The interface packages other interface packages depend on, before the rest.
166+
# Without this stage test_msgs configures while service_msgs and
167+
# unique_identifier_msgs still resolve to the image's binaries, and its
168+
# micro-ROS type support links their Fast DDS type support instead — which
169+
# only surfaces later as an undefined symbol when an action type is used.
170+
# micro_ros_setup's host build.sh stages these two for the same reason.
171+
# service_msgs doesn't exist in humble, so only add it if present.
172+
packages_stage3="unique_identifier_msgs builtin_interfaces"
173+
if [ -d src/rcl_interfaces/service_msgs ]; then
174+
packages_stage3="service_msgs $packages_stage3"
175+
fi
176+
colcon build \
177+
--event-handlers console_direct+ \
178+
--packages-up-to $packages_stage3 \
179+
--cmake-args -DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=ON
180+
. install/local_setup.bash
181+
182+
# 4. The packages under test, pulling in the interface packages as dependencies
183+
# — now regenerated with micro-ROS type support. BUILD_TESTING stays on, and
184+
# RelWithDebInfo keeps asserts and symbols: this suite has never run against
185+
# this middleware, so a usable backtrace is worth more than -O3.
186+
colcon build \
187+
--event-handlers console_direct+ \
188+
--packages-up-to rcl rcl_action rcl_lifecycle \
189+
--cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=ON -DRCL_MICROROS=ON
190+
. install/local_setup.bash
191+
192+
# 5. The Agent is test infrastructure only: built last, so a failure in the
193+
# packages under test is reported before the several minutes this takes, and
194+
# built optimised since we never debug it. Its superbuild clones and builds
195+
# the XRCE Agent itself, taking fastcdr and fastdds from the image.
196+
colcon build \
197+
--event-handlers console_direct+ \
198+
--packages-up-to micro_ros_agent \
199+
--cmake-args -DCMAKE_BUILD_TYPE=Release
200+
201+
# No `if:` here on purpose: this step carries the implicit success() condition, so
202+
# it is skipped when the build failed, and its own failure fails the job. This is
203+
# the middleware the fork targets, so it is the gate.
204+
- name: Test (micro XRCE-DDS)
205+
run: |
206+
. /opt/ros/$ROS_DISTRO/setup.bash
207+
. install/local_setup.bash
208+
209+
# start an Agent
210+
install/micro_ros_agent/lib/micro_ros_agent/micro_ros_agent udp4 --port 8888 -d -v4 &
211+
agent_pid=$!
212+
sleep 1
213+
214+
# rcl registers each middleware test once per available RMW, as <test>__<rmw>.
215+
# Excluding the Fast DDS ones leaves this middleware's copies plus the entries
216+
# that carry no RMW suffix. It assumes rmw_fastrtps_cpp is the only DDS implementation
217+
# in the image, which holds for every ros-base image these branches use.
218+
# RMW_IMPLEMENTATION is needed because the un-suffixed entries are not
219+
# middleware free: every rcl test binary resolves an RMW on start-up, so
220+
# without it they would run against the image's Fast DDS. The suffixed ones
221+
# set it themselves through ctest and are unaffected.
222+
# What cannot run here is guarded in the sources or in CMakeLists.
223+
RMW_IMPLEMENTATION=rmw_microxrcedds colcon test \
224+
--event-handlers console_direct+ \
225+
--return-code-on-test-failure \
226+
--packages-select rcl \
227+
--ctest-args -E "__rmw_fastrtps_cpp"
228+
229+
# rcl_action registers only three targets per RMW (all guarded in its
230+
# CMakeLists) and rcl_lifecycle none, so their tests carry no RMW suffix and
231+
# take the middleware from the environment instead of a ctest filter.
232+
RMW_IMPLEMENTATION=rmw_microxrcedds colcon test \
233+
--event-handlers console_direct+ \
234+
--return-code-on-test-failure \
235+
--packages-select rcl_action rcl_lifecycle
236+
237+
# kill Agent so the next step runs without it
238+
kill $agent_pid
239+
wait $agent_pid || true
240+
241+
- name: Test results (micro XRCE-DDS)
242+
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
243+
run: |
244+
. /opt/ros/$ROS_DISTRO/setup.bash
245+
colcon test-result --verbose
246+
247+
- name: Test (Fast DDS)
248+
# the same reduced suite against Fast DDS
249+
# it excludes the uxrce test entries
250+
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
251+
run: |
252+
. /opt/ros/$ROS_DISTRO/setup.bash
253+
. install/local_setup.bash
254+
colcon test \
255+
--event-handlers console_direct+ \
256+
--return-code-on-test-failure \
257+
--packages-select rcl rcl_action rcl_lifecycle \
258+
--ctest-args -E "__rmw_microxrcedds"
259+
260+
- name: Test results (Fast DDS)
261+
# this colcon test-result covers both runs
262+
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
263+
run: |
264+
. /opt/ros/$ROS_DISTRO/setup.bash
265+
colcon test-result --verbose
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Weekly ubuntu CI micro-ROS rcl
2+
3+
on:
4+
schedule:
5+
# Mondays at 02:00 UTC
6+
- cron: '0 2 * * 1'
7+
# To enable manual trigger
8+
workflow_dispatch:
9+
10+
jobs:
11+
ubuntu_ci:
12+
name: ${{ matrix.branch }}
13+
strategy:
14+
# Without this, one distro failing would cancel the others and
15+
# hide if they are fine
16+
fail-fast: false
17+
matrix:
18+
branch:
19+
- humble
20+
- jazzy
21+
- kilted
22+
- lyrical
23+
- rolling
24+
uses: ./.github/workflows/ubuntu-ci.yml
25+
with:
26+
branch: ${{ matrix.branch }}

‎rcl/CMakeLists.txt‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ if(NOT RCL_MICROROS)
121121
endif()
122122

123123
if(NOT RCL_MICROROS)
124-
target_compile_definitions(${PROJECT_NAME}
125-
PUBLIC
126-
RCL_MICROROS_COMPLETE_IMPL
124+
target_compile_definitions(${PROJECT_NAME}
125+
PUBLIC
126+
RCL_MICROROS_COMPLETE_IMPL
127127
)
128128
endif()
129129

‎rcl/src/rcl/init.c‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ rcl_init(
239239
context->impl->init_options.impl->rmw_init_options.enclave = rcutils_strdup(
240240
"/", context->impl->allocator);
241241
}
242+
#else
243+
context->impl->init_options.impl->rmw_init_options.enclave = rcutils_strdup(
244+
"/", context->impl->allocator);
245+
#endif // RCL_MICROROS_COMPLETE_IMPL
242246

243247
if (!context->impl->init_options.impl->rmw_init_options.enclave) {
244248
RCL_SET_ERROR_MSG("failed to set context name");
@@ -276,7 +280,6 @@ rcl_init(
276280
fail_ret = ret;
277281
goto fail;
278282
}
279-
#endif // RCL_MICROROS_COMPLETE_IMPL
280283

281284
// Initialize rmw_init.
282285
rmw_ret_t rmw_ret = rmw_init(

‎rcl/src/rcl/logging.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ rcl_logging_configure_with_output_handler(
7373
const char * config_file = global_args->impl->external_log_config_file;
7474
bool global_disable = false;
7575
#ifdef RCUTILS_NO_LOGGING
76-
global_disable = true;
76+
global_disable = true;
7777
#endif
7878
g_rcl_logging_stdout_enabled = !global_args->impl->log_stdout_disabled && !global_disable;
7979
g_rcl_logging_rosout_enabled = !global_args->impl->log_rosout_disabled && !global_disable;

‎rcl/src/rcl/subscription.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,12 @@ rcl_subscription_get_default_options(void)
252252
default_options.allocator = rcl_get_default_allocator();
253253
default_options.rmw_subscription_options = rmw_get_default_subscription_options();
254254

255-
#ifdef RCL_MICROROS_COMPLETE_IMPL
256255
// Load disable flag to LoanedMessage via environmental variable.
257256
// TODO(clalancette): This is kind of a copy of rcl_get_disable_loaned_message(), but we need
258257
// more information than that function provides.
259258
default_options.disable_loaned_message = true;
260259

260+
#ifdef RCL_MICROROS_COMPLETE_IMPL
261261
const char * env_val = NULL;
262262
const char * env_error_str = rcutils_get_env(RCL_DISABLE_LOANED_MESSAGES_ENV_VAR, &env_val);
263263
if (NULL != env_error_str) {

0 commit comments

Comments
 (0)