I am new to ROS2 and do not have any prior knowledge of Cmake.
I have a ROS2 package that looks like this:
├── CMakeLists.txt
├── include
│ └── package_name
│ ├── bmi088.h
│ ├── bmi08x_defs.h
│ ├── bmi08x.h
│ ├── rpi_bmi088.h
│ └── rpi_i2c.h
├── package.xml
├── scripts
├── src
│ ├── bmi088.c
│ ├── imu_data_reader.cpp
│ ├── rpi_bmi088.c
│ └── rpi_i2c.c
└── package_name
└── __init__.py
I got the sensor drivers from a github repo and tested the code by running the makefile that was included in the repo. The code worked perfectly, but when implementing in ROS2, I got the following error when building with colcon:
Starting >>> package_name
--- stderr: package_name
/usr/bin/ld: CMakeFiles/imu_data_reader.dir/src/imu_data_reader.cpp.o: in function `main':
imu_data_reader.cpp:(.text+0x12a): undefined reference to `rpi_bmi088_init(rpi_bmi088_t*, char const*, int, int, bmi08x_cfg const*, bmi08x_cfg const*)'
/usr/bin/ld: imu_data_reader.cpp:(.text+0x139): undefined reference to `rpi_bmi088_get_sensor_time(rpi_bmi088_t*)'
/usr/bin/ld: imu_data_reader.cpp:(.text+0x17f): undefined reference to `rpi_bmi088_get_accel(rpi_bmi088_t*, double*, double*, double*)'
/usr/bin/ld: imu_data_reader.cpp:(.text+0x1db): undefined reference to `rpi_bmi088_get_gyro(rpi_bmi088_t*, double*, double*, double*)'
collect2: error: ld returned 1 exit status
gmake[2]: *** [CMakeFiles/imu_data_reader.dir/build.make:154: imu_data_reader] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:148: CMakeFiles/imu_data_reader.dir/all] Error 2
gmake[1]: *** Waiting for unfinished jobs....
gmake: *** [Makefile:146: all] Error 2
---
Failed <<< package_name [0.42s, exited with code 2]
Summary: 0 packages finished [0.70s]
1 package failed: package_name
1 package had stderr output: package_name
I know that this error comes from not declaring dependencies or falsely compiling, so I probably messed up with the CMakeLists file:
cmake_minimum_required(VERSION 3.8)
project(package_name)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclpy REQUIRED)
# include directories
include_directories(include)
# CPP Nodes:
add_executable(imu_data_reader src/imu_data_reader.cpp)
ament_target_dependencies(imu_data_reader rclcpp)
# Link libraries:
add_library(rpi_bmi088 src/rpi_bmi088.c)
add_library(bmi088 src/bmi088.c)
add_library(rpi_i2c src/rpi_i2c.c)
target_link_libraries(imu_data_reader rpi_bmi088 bmi088 rpi_i2c)
install(TARGETS
imu_data_reader
DESTINATION lib/${PROJECT_NAME}
)
# Install Python modules
ament_python_install_package(${PROJECT_NAME})
# Install Python executables
install(PROGRAMS
DESTINATION lib/${PROJECT_NAME}
)
ament_package()
My question is: how does one correctly compile header files with their corresponding .c files?
In my source files, I am including the header files #include "package_name/rpi_bmi088.h"
where the functions are declared, but they are not being linked to the corresponding .c file in this case src/rpi_bmi088.c
where the functions are implemented.
In case it is also important, I will include my package.xml file:
...package_info...
<buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>ament_cmake_python</buildtool_depend>
<depend>rclcpp</depend>
<depend>rclpy</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
UPDATE 1:
After implementing michael's answer:
extern "C"
{
#include "package_name/rpi_bmi088.h"
}
I now get a different but similar error:
Starting >>> package_name
--- stderr: package_name
/usr/bin/ld: libbmi088.a(bmi088.c.o): warning: relocation against `bmi08x_config_file' in read-only section `.text'
/usr/bin/ld: librpi_bmi088.a(rpi_bmi088.c.o): in function `rpi_bmi088_init':
rpi_bmi088.c:(.text+0x119): undefined reference to `bmi08a_get_regs'
/usr/bin/ld: rpi_bmi088.c:(.text+0x14c): undefined reference to `bmi08g_get_regs'
/usr/bin/ld: rpi_bmi088.c:(.text+0x183): undefined reference to `bmi08a_set_power_mode'
/usr/bin/ld: rpi_bmi088.c:(.text+0x195): undefined reference to `bmi08a_set_meas_conf'
/usr/bin/ld: rpi_bmi088.c:(.text+0x1a7): undefined reference to `bmi08a_get_power_mode'
/usr/bin/ld: rpi_bmi088.c:(.text+0x200): undefined reference to `bmi08g_set_power_mode'
/usr/bin/ld: rpi_bmi088.c:(.text+0x212): undefined reference to `bmi08g_set_meas_conf'
/usr/bin/ld: librpi_bmi088.a(rpi_bmi088.c.o): in function `rpi_bmi088_get_accel':
rpi_bmi088.c:(.text+0x2a0): undefined reference to `bmi08a_get_data'
/usr/bin/ld: librpi_bmi088.a(rpi_bmi088.c.o): in function `rpi_bmi088_get_gyro':
rpi_bmi088.c:(.text+0x384): undefined reference to `bmi08g_get_data'
/usr/bin/ld: librpi_bmi088.a(rpi_bmi088.c.o): in function `rpi_bmi088_get_sensor_time':
rpi_bmi088.c:(.text+0x46e): undefined reference to `bmi08a_get_sensor_time'
/usr/bin/ld: libbmi088.a(bmi088.c.o): in function `bmi088_init':
bmi088.c:(.text+0x18): undefined reference to `bmi08a_init'
/usr/bin/ld: bmi088.c:(.text+0x2d): undefined reference to `bmi08g_init'
/usr/bin/ld: libbmi088.a(bmi088.c.o): in function `bmi088_apply_config_file':
bmi088.c:(.text+0x66): undefined reference to `bmi08x_config_file'
/usr/bin/ld: bmi088.c:(.text+0x76): undefined reference to `bmi08a_write_config_file'
/usr/bin/ld: libbmi088.a(bmi088.c.o): in function `bmi088_configure_data_synchronization':
bmi088.c:(.text+0x14b): undefined reference to `bmi08a_set_meas_conf'
/usr/bin/ld: bmi088.c:(.text+0x166): undefined reference to `bmi08g_set_meas_conf'
/usr/bin/ld: bmi088.c:(.text+0x1a0): undefined reference to `bmi08a_write_feature_config'
/usr/bin/ld: libbmi088.a(bmi088.c.o): in function `bmi088_configure_anymotion':
bmi088.c:(.text+0x29a): undefined reference to `bmi08a_write_feature_config'
/usr/bin/ld: libbmi088.a(bmi088.c.o): in function `bmi088_get_synchronized_data':
bmi088.c:(.text+0x32c): undefined reference to `bmi08a_get_regs'
/usr/bin/ld: bmi088.c:(.text+0x35c): undefined reference to `bmi08a_get_regs'
/usr/bin/ld: bmi088.c:(.text+0x402): undefined reference to `bmi08g_get_data'
/usr/bin/ld: libbmi088.a(bmi088.c.o): in function `bmi088_set_data_sync_int_config':
bmi088.c:(.text+0x44f): undefined reference to `bmi08a_set_int_config'
/usr/bin/ld: bmi088.c:(.text+0x475): undefined reference to `bmi08a_set_int_config'
/usr/bin/ld: bmi088.c:(.text+0x49b): undefined reference to `bmi08g_set_int_config'
/usr/bin/ld: bmi088.c:(.text+0x4c1): undefined reference to `bmi08g_set_int_config'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
gmake[2]: *** [CMakeFiles/imu_data_reader.dir/build.make:154: imu_data_reader] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:148: CMakeFiles/imu_data_reader.dir/all] Error 2
gmake[1]: *** Waiting for unfinished jobs....
gmake: *** [Makefile:146: all] Error 2
---
Failed <<< package_name [0.23s, exited with code 2]
Summary: 0 packages finished [0.34s]
1 package failed: package_name
1 package had stderr output: package_name
Note that:
imu_data_reader.cpp
includes only rpi_bmi088.h
rpi_bmi088.h
includes bmi08x.h
rpi_bmi088.c
includes rpi_bmi088.h
, rpi_i2c.h
and bmi088.h
bmi08x.h
includes bmi08x_defs.h
which doesn't include anything
rpi_i2c.c
includes rpi_i2c.h
which doesn't include anything
bmi088.h
also includes bmi08x_defs.h
bmi088.c
includes bmi08x.h
and bmi088.h
So maybe it's a dependency problem?
UPDATE 2:
I finally solved my problem with the following changes in the CMakeLists.txt file:
# Add and link libraries:
add_library(rpi_bmi088 SHARED src/rpi_bmi088.c)
add_library(bmi088 SHARED src/bmi088.c)
add_library(rpi_i2c SHARED src/rpi_i2c.c)
target_link_libraries(imu_data_reader rpi_bmi088 rpi_i2c bmi088)
So I was on the right track but I needed to add the SHARED keyword.
UPDATE 3:
Actually only 'colcon build' works but running the node I get:
colcon_ws/install/package_name/lib/package_name/imu_data_reader: error while loading shared libraries: librpi_bmi088.so: cannot open shared object file: No such file or directory
[ros2run]: Process exited with failure 127
So now I'm clueless again
What solved the problem:
# Adding libraries:
add_library(rpi_bmi088 STATIC src/rpi_bmi088.c)
add_library(bmi088 STATIC src/bmi088.c)
add_library(rpi_i2c STATIC src/rpi_i2c.c)
# Linking libraries:
target_link_libraries(rpi_bmi088 rpi_i2c bmi088)
target_link_libraries(imu_data_reader rpi_bmi088)
# Install CPP executables:
install(TARGETS
imu_data_reader
rpi_bmi088
bmi088
rpi_i2c
DESTINATION lib/${PROJECT_NAME}
)