0

I am mainly using ROS which is cmake based, where I want to link a library that I compiled previously and copied the .so and .a file to /usr/lib and .h to /usr/include.

When I compile it with gcc, there is no problem with: gcc -o test test.c -lpynq -lcma -lpthread. How can I link those libraries in the cmakelist.txt?

Thank you for the help.

EDIT: Following JohnFilleau and the answer from ChrisZZ, this is my CMakeLists.txt:

cmake_minimum_required(VERSION 3.0.2)
project(proof_of_concept_string_ips)

## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
)

## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)


## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()

################################################
## Declare ROS messages, services and actions ##
################################################

## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
##   your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
##   * add a build_depend tag for "message_generation"
##   * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
##   * If MSG_DEP_SET isn't empty the following dependency has been pulled in
##     but can be declared for certainty nonetheless:
##     * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
##   * add "message_generation" and every package in MSG_DEP_SET to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * add "message_runtime" and every package in MSG_DEP_SET to
##     catkin_package(CATKIN_DEPENDS ...)
##   * uncomment the add_*_files sections below as needed
##     and list every .msg/.srv/.action file to be processed
##   * uncomment the generate_messages entry below
##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)

## Generate messages in the 'msg' folder
# add_message_files(
#   FILES
#   Message1.msg
#   Message2.msg
# )

## Generate services in the 'srv' folder
# add_service_files(
#   FILES
#   Service1.srv
#   Service2.srv
# )

## Generate actions in the 'action' folder
# add_action_files(
#   FILES
#   Action1.action
#   Action2.action
# )

## Generate added messages and services with any dependencies listed here
# generate_messages(
#   DEPENDENCIES
#   std_msgs
# )

################################################
## Declare ROS dynamic reconfigure parameters ##
################################################

## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
##   * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
##   * add "dynamic_reconfigure" to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * uncomment the "generate_dynamic_reconfigure_options" section below
##     and list every .cfg file to be processed

## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
#   cfg/DynReconf1.cfg
#   cfg/DynReconf2.cfg
# )

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES proof_of_concept_string_ips
#  CATKIN_DEPENDS roscpp std_msgs
#  DEPENDS system_lib
)

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
)

## Declare a C++ library
# add_library(${PROJECT_NAME}
#   src/${PROJECT_NAME}/proof_of_concept_string_ips.cpp
# )

## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/proof_of_concept_string_ips_node.cpp)

## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")

## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
#   ${catkin_LIBRARIES}
# )

#############
## Install ##
#############

# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html

## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# catkin_install_python(PROGRAMS
#   scripts/my_python_script
#   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark executables for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
# install(TARGETS ${PROJECT_NAME}_node
#   RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark libraries for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
# install(TARGETS ${PROJECT_NAME}
#   ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )

## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
#   DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
#   FILES_MATCHING PATTERN "*.h"
#   PATTERN ".svn" EXCLUDE
# )

## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
#   # myfile1
#   # myfile2
#   DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )

#############
## Testing ##
#############

## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_proof_of_concept_string_ips.cpp)
# if(TARGET ${PROJECT_NAME}-test)
#   target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()

## Add folders to be run by python nosetests
# catkin_add_nosetests(test)

add_executable(pc_pub_string src/pc_pub_string.cpp)
target_link_libraries(pc_pub_string ${catkin_LIBRARIES})
#add_dependencies(pc_pub_string0 proof_of_concept_string_ips_generate_messages_cpp)

add_executable(arm_to_fpga src/arm_to_fpga.cpp)
target_link_libraries(arm_to_fpga ${catkin_LIBRARIES})
# target_link_libraries(arm_to_fpga ${catkin_LIBRARIES}) /usr/lib/libpynq.so)
#add_dependencies(arm_to_fpga proof_of_concept_generate_messages_cpp)

# add_executable(pynq_test_api src/pynq_test_api.cpp)
# target_link_libraries(pynq_test_api ${catkin_LIBRARIES})

set(pynq_lib "/usr/lib/libpynq.so")
set(cma_lib "/usr/lib/libcma.so")

add_executable(pynq_test_api src/pynq_test_api.cpp)
target_link_libraries(pynq_test_api PUBLIC
  ${catkin_LIBRARIES}
  ${pynq_lib}
  ${cma_lib}
  pthread
  )
  target_include_directories(pynq_test_api PUBLIC
  "/usr/include"
)

And the error:

CMakeFiles/pynq_test_api.dir/src/pynq_test_api.cpp.o: In function `main':
pynq_test_api.cpp:(.text+0xba): undefined reference to `PYNQ_allocatedSharedMemory(shared_memory_state_struct*, unsigned int, int)'
pynq_test_api.cpp:(.text+0xc8): undefined reference to `PYNQ_allocatedSharedMemory(shared_memory_state_struct*, unsigned int, int)'
collect2: error: ld returned 1 exit status
proof_of_concept_string_ips/CMakeFiles/pynq_test_api.dir/build.make:114: recipe for target '/home/xilinx/fpga_ros_scheduler/catkin_ws/devel/lib/proof_of_concept_string_ips/pynq_test_api' failed
make[2]: *** [/home/xilinx/fpga_ros_scheduler/catkin_ws/devel/lib/proof_of_concept_string_ips/pynq_test_api] Error 1
CMakeFiles/Makefile2:449: recipe for target 'proof_of_concept_string_ips/CMakeFiles/pynq_test_api.dir/all' failed
make[1]: *** [proof_of_concept_string_ips/CMakeFiles/pynq_test_api.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
aripod
  • 55
  • 1
  • 14
  • Generally, [`target_link_libraries`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) does the job, but the CMake build system needs to know about the target you're linking. External projects that have more idiomatic C++ support will support the `find_package()` command, which populates information about where to find these targets. If you've previously invoked `add_executable` or `add_library`, then CMake would already have knowledge of these targets. Can you show what you've done? – JohnFilleau Jan 18 '22 at 14:14
  • Recommended reading: [Effective Modern CMake](https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1) [An Introduction to Modern CMake](https://cliutils.gitlab.io/modern-cmake/) – JohnFilleau Jan 18 '22 at 14:16
  • I've edited the question to include the CMakeLists.txt – aripod Jan 18 '22 at 14:28
  • Are your libraries actually at `/usr/lib/libpynq.so` and `/usr/lib/libcma.so`? – JohnFilleau Jan 18 '22 at 14:44
  • Yes, I can see both files in /usr/lib – aripod Jan 18 '22 at 14:46
  • Can you run `nm /usr/lib/libpynq.so` and verify that it has the symbol `PYNQ_allocatedSharedMemory(shared_memory_state_struct*, unsigned int, int)` somewhere in its symbol table? I don't see why it wouldn't since your `gcc` compilation works fine. I just want to make sure. – JohnFilleau Jan 18 '22 at 14:53
  • When you `#include "pynq_api.h"` do you wrap it in an `extern "C" {` ... `}`? The functions in your .so libraries should have C linkage but your cpp file should be looking for them using C++ linkage? – JohnFilleau Jan 18 '22 at 15:02
  • I ran `nm /usr/lib/libpynq.so | grep PYNQ_allocatedSharedMemory` and I got `00001e10 T PYNQ_allocatedSharedMemory` so I guess that is expected. I didn't wrap the include.... – aripod Jan 18 '22 at 15:09
  • Please wrap your include in the `extern "C"` directive and report back. I believe this might be a name mangling issue. – JohnFilleau Jan 18 '22 at 15:12
  • 1
    Seems to be the extern "C" the issue here! – aripod Jan 18 '22 at 15:12
  • I very much recommend reading the links I commented above regarding best practice CMake usage. It helped me understand how to read and use CMake instead of guess-and-checking like I used to. – JohnFilleau Jan 18 '22 at 15:13
  • I believe the canonical fix is to add include guards to your `.h` files. See https://stackoverflow.com/questions/3789340/combining-c-and-c-how-does-ifdef-cplusplus-work – JohnFilleau Jan 18 '22 at 15:15
  • 1
    Yes, absolutely. I only had to add pynq cma pthread to the target_link_libraries. Of course, good point like you said to read the best practices about CMake. Thanks JohnFilleau for the help – aripod Jan 18 '22 at 15:28
  • Does this answer your question? [What is an undefined reference/unresolved external symbol error and how do I fix it?](https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix) – fabian Jan 18 '22 at 18:06
  • No, because that was not the issue. Here, suggestions and guidance were given on cmake in general, an option was given as an answer, test to check whether the external library was compiled and lastly the solution which was including a c library in a c++ code which was solved with the `extern "C"` guard. – aripod Jan 19 '22 at 07:22

1 Answers1

1

The quick-but-not-elegant solution:

cmake_minimum_required(VERSION 3.20) # I like newer version

project(my_project)

set(pynq_lib "/usr/lib/libpynq.a")  # the full path. if postfix is `.so`, change it
set(cma_lib "/usr/lib/libcma.a")  # the full path. change if needed.

add_executable(test_app test.c)
target_link_libraries(test_app PUBLIC
  ${pynq_lib}
  ${cma_lib}
  pthread
)
target_include_directories(test_app PUBLIC
  "/usr/include"
)

The elegant way: as JohnFilleau suggested, suppose we generate pynq and cma libraries via CMake, then we treat them as "packages", and we "find package and link package", with commands like find_package() for external installed packages, or just use target_link_libraries() if build from same CMake based project.

ChrisZZ
  • 1,521
  • 2
  • 17
  • 24
  • I added it (see edited question) but I still get errors with respect to the library `undefined reference to ...` – aripod Jan 18 '22 at 14:29
  • @aripod please post the full error message when you get it. My CMake version will tell me the line number and file it's having issue with – JohnFilleau Jan 18 '22 at 14:30
  • I've edited again the question. It is n the executable where I call the functions of the library – aripod Jan 18 '22 at 14:42