1
$\begingroup$

Rosanswers logo

I have a ROS2 Project and I want to develop a good structured filesystem. I wanted to do some "good-practice"(from my point of knowledge) and import a class definition from seperate header and source files into my "main" source file.

I want to get a minimal example running, but have linking issues when building it and am also not sure if what I'm trying is even considered as good practice or not.

I have following package structure (inside workspace/src/):

pkg_user/
   /include/pkg_user/
            CUserInterface.h  // class declaration
   /src
            CUserInterface.cpp  // class definition
            user_interface.cpp  // "main" source file
    CMakeLists.txt
    package.xml

"/include/pkg_user/" containing the Class-declaration: CUserInterface.h

#include "my_interfaces/srv/submit_pose.hpp"

#ifndef CUSERINTERFACE_H
#define CUSERINTERFACE_H

class CUserInterface
{
public:
    CUserInterface(){};
    ~CUserInterface(){};
    int submit_pose_to_robot();
private:

};

#endif

/src/ containing the Class-definition: CUserInterface.cpp

#include "CUserInterface.h"

int CUserInterface::submit_pose_to_robot()
{
    std::cout<<"hello world from CUserInterface.cpp"<<std::endl;
    return 0;
}

and also the "main" source code: user_interface.cpp which implements the class

#include "rclcpp/rclcpp.hpp"
#include <unistd.h>
#include "CUserInterface.h"


int main(int argc, char **argv)
{
    rclcpp::init(argc, argv);
    
    CUserInterface myUI;

    myUI.submit_pose_to_robot();

    sleep(5);
    
    rclcpp::shutdown(); 
    return 0;
}

Now when I compile that I get following (linker?) error:

jo@myubuntu:~/workspace/ros_ur_driver$ colcon build --packages-select pkg_user
Starting >>> pkg_user
--- stderr: pkg_user                              
/usr/bin/ld: CMakeFiles/user_interface.dir/src/user_interface.cpp.o: in function `main':
user_interface.cpp:(.text+0xd7): undefined reference to `CUserInterface::submit_pose_to_robot()'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/user_interface.dir/build.make:141: user_interface] Error 1
make[1]: *** [CMakeFiles/Makefile2:78: CMakeFiles/user_interface.dir/all] Error 2
make: *** [Makefile:141: all] Error 2
---
Failed   <<< pkg_user [3.73s, exited with code 2]

Summary: 0 packages finished [3.98s]
  1 package failed: pkg_user
  1 package had stderr output: pkg_user

Unfortunately I have not much knowledge in CMake, and I couldn't figure out how to link(?) the files to get it working. My CMakeLists.txt looks like this:

cmake_minimum_required(VERSION 3.5)
project(pkg_user)

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(my_interfaces REQUIRED)

add_executable(user_interface src/user_interface.cpp)
ament_target_dependencies(user_interface rclcpp my_interfaces)

# include my own headers
target_include_directories(user_interface PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME}
)

install(TARGETS
    user_interface
    DESTINATION lib/${PROJECT_NAME})

ament_package()

How is the right way to place the files and how should the CMakeLists.txt should look like?


Originally posted by jorosuser on ROS Answers with karma: 31 on 2021-11-26

Post score: 0

$\endgroup$

2 Answers 2

1
$\begingroup$

Rosanswers logo

You need to build CUserInterface as a separate library to link against your executable.

This annotated CMakeLists.txt should work:

cmake_minimum_required(VERSION 3.5)
project(pkg_user)

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(my_interfaces REQUIRED)

# building the library
add_library(CUserInterface src/CUserInterface.cpp)
target_include_directories(CUserInterface PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>
)
# linking against my_interfaces since you plan to use a service from said package?
ament_target_dependencies(CUserInterface my_interfaces)

add_executable(user_interface src/user_interface.cpp)
ament_target_dependencies(user_interface rclcpp CUserInterface)

install(TARGETS
    user_interface
    DESTINATION lib/${PROJECT_NAME})

ament_package()

Originally posted by aprotyas with karma: 456 on 2021-11-28

This answer was NOT ACCEPTED on the original site

Post score: 1


Original comments

Comment by aprotyas on 2021-11-28:
For your reference, the ament_cmake documentation is a good source for "to-do"s: https://docs.ros.org/en/foxy/How-To-Guides/Ament-CMake-Documentation.html

A good source of best practices is ROS-agnostic CMake resources, such as: https://cliutils.gitlab.io/modern-cmake/

Comment by jorosuser on 2021-11-29:
Thanks for your answer and the links! I tried your example CMakeLists.txt, but now I get following error:

jo@myubuntu:~/workspace/ros_ur_driver$ colcon build --packages-select pkg_user
Starting >>> pkg_user
--- stderr: pkg_user                         
CMake Error at /opt/ros/foxy/share/ament_cmake_target_dependencies/cmake/ament_target_dependencies.cmake:77 (message):
  ament_target_dependencies() the passed package name 'CUserInterface' was
  not found before
Call Stack (most recent call first):
  CMakeLists.txt:20 (ament_target_dependencies)


---
Failed   <<< pkg_user [0.66s, exited with code 1]

Summary: 0 packages finished [0.92s]
  1 package failed: pkg_user
  1 package had stderr output: pkg_user

Do I need to modify the package.xml as well for my own library?

Comment by aprotyas on 2021-11-29:
My bad - I forgot that CUserInterface isn't a package itself, just a library. As such, you can't invoke ament_target_dependencies on it. You need to use the more general way of linking against dependencies for CUserInterface called target_link_libraries. Replace ament_target_dependencies(CUserInterface my_interfaces) with target_link_libraries(CUserInterface PUBLIC my_interfaces)!

Comment by jorosuser on 2021-11-29:
Still the same error, the problem lies in this line: ament_target_dependencies(user_interface rclcpp CUserInterface)

Comment by jorosuser on 2021-11-29:
I removed the my_interface dependency for a more minimal example

with this line: ament_target_dependencies(user_interface rclcpp CUserInterface) I get this error:

Starting >>> pkg_user
--- stderr: pkg_user                         
CMake Error at /opt/ros/foxy/share/ament_cmake_target_dependencies/cmake/ament_target_dependencies.cmake:77 (message):
  ament_target_dependencies() the passed package name 'CUserInterface' was
  not found before
Call Stack (most recent call first):
  CMakeLists.txt:21 (ament_target_dependencies)


make: *** [Makefile:308: cmake_check_build_system] Error 1
---
Failed   <<< pkg_user [0.60s, exited with code 2]

Summary: 0 packages finished [0.86s]
  1 package failed: pkg_user
  1 package had stderr output: pkg_user

Comment by jorosuser on 2021-11-29:
and with target_link_libraries(user_interface PUBLIC rclcpp CUserInterface) this one:

Starting >>> pkg_user
--- stderr: pkg_user                             
/home/jo/workspace/ros_ur_driver/src/pkg_user/src/user_interface.cpp:8:10: fatal error: rclcpp/rclcpp.hpp: No such file or directory
    8 | #include "rclcpp/rclcpp.hpp"
      |          ^~~~~~~~~~~~~~~~~~~
compilation terminated.
make[2]: *** [CMakeFiles/user_interface.dir/build.make:76: CMakeFiles/user_interface.dir/src/user_interface.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:165: CMakeFiles/user_interface.dir/all] Error 2
make: *** [Makefile:146: all] Error 2
---
Failed   <<< pkg_user [0.92s, exited with code 2]

Summary: 0 packages finished [1.19s]
  1 package failed: pkg_user
  1 package had stderr output: pkg_user

Comment by aprotyas on 2021-11-29:
So, I think what's happening is you need to ament_target_dependencies the ROS packages (rclcpp), and you need to target_link_libraries the other targets (CUserInterface).

Comment by jorosuser on 2021-11-29:
I also tried that, then I get this error:

jo@myubuntu:~/workspace/ros_ur_driver$ colcon build --packages-select pkg_user --cmake-clean-first 
Starting >>> pkg_user
--- stderr: pkg_user                         
CMake Error at CMakeLists.txt:22 (target_link_libraries):
  The plain signature for target_link_libraries has already been used with
  the target "user_interface".  All uses of target_link_libraries with a
  target must be either all-keyword or all-plain.

  The uses of the plain signature are here:

   * /opt/ros/foxy/share/ament_cmake_target_dependencies/cmake/ament_target_dependencies.cmake:145 (target_link_libraries)
  

make: *** [Makefile:308: cmake_check_build_system] Error 1
---
Failed   <<< pkg_user [0.66s, exited with code 2]

Summary: 0 packages finished [0.91s]
  1 package failed: pkg_user
  1 package had stderr output: pkg_user

Comment by aprotyas on 2021-11-29:
Sorry I'm not being very helpful! This post seems relevant: https://stackoverflow.com/questions/59522267/cmake-rejects-a-second-target-link-libraries-talking-about-keyword-vs-plain

$\endgroup$
1
$\begingroup$

Rosanswers logo

Thanks @aprotyas for your help!

I finally got my problem solved with this CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)
project(pkg_user)

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(my_interfaces REQUIRED)


# building the library
add_library(CUserInterface src/CUserInterface.cpp)

# linking against my_interfaces
ament_target_dependencies(CUserInterface my_interfaces)

# find the header files
include_directories(include)

add_executable(user_interface src/user_interface.cpp)
ament_target_dependencies(user_interface rclcpp)

target_link_libraries(user_interface CUserInterface)

install(TARGETS
    user_interface
    DESTINATION lib/${PROJECT_NAME})

ament_package()

Originally posted by jorosuser with karma: 31 on 2021-11-30

This answer was NOT ACCEPTED on the original site

Post score: 1

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.