CMakeLists.txt生成链接第三方动态链接库的用法
一. 使用find_package
链接
如果 CMake 通过find_package找到一个包,它会自动创建几个CMake的环境变量,包括以下:
- <NAME>_FOUND, 如果找到该值为true,否则为false.
- <NAME>_INCLUDE_DIRS 或 <NAME>_INCLUDES, 包导出的包含路径.
- <NAME>_LIBRARIES 或 <NAME>_LIBS, 包导出的库
所以,只有使用find_package时,才能使用 ${<NAME>_INCLUDE_DIRS},或者 ${<NAME>_LIBRARIES} 这种变量.
示例:以OpenCV
库为例
find_package(OpenCV REQUIRED)
# OpenCV_INCLUDE_DIRS 是由于使用了find_package命令,CMake就会自动定义该变量,该变量代表OpenCV库的头文件夹路径
include_directories(${OpenCV_INCLUDE_DIRS})
# OpendCV_LIBS 来源同上,该变量代表了 OpenCV库的库文件路径, 注意,这里不是文件夹三个字。
target_link_libraries(MY_TARGET_NAME ${OpenCV_LIBS})
find_package
的优点是可以批量引入, 由于库的头文件路径 或者库文件路径不止一个,但是find_package
可以将多个头文件路径整合为 <NAME>_INCLUDE_DIRS
, 将多个库文件路径整合为 <NAME>_LIBRARIES
。
缺点是:有些库直接没有配置cmake
,使用find_package
命令会找不到。
二. 直接设置头文件和库文件的路径
关于include:
CMakeLists引入头文件的方式只有include_directories()指令。
区别是: 如果使用find_package命令,就会有 <NAME>_INCLUDE_DIRS 直接放include_directories()就行了。
而直接设置头文件的方式,需要你自己知道头文件在哪个位置。
(1)如果你直接将头文件放在 工程内 CMakeLists同级目录的include文件夹下
include_directories(include) 即可。
(2)如果你将头文件放在 工程内 CMakeLists同级目录的lib文件夹下, 第三方包命名,include下
include_directories(${PROJECT_SOURCE_DIR}/lib/第三方包命名/include)
(3)如果你将头文件放在 工程外 某个目录下,就需要使用绝对路径写全
include_directories(/opt/ros/foxy/include)
(4)如果你直接将第三方包,通过 make install命令安装在系统中了,比如/usr/include, 就不需要配置了
链接第三方库的用法:
如果使用 find_package命令,就会有 <NAME>_LIBRARIES 直接放在 target_link_libraries()就行了
而直接设置库文件的方式,需要你自己知道库文件在哪个位置。
link_directories() 将库文件所在文件夹加载进 CMakeLists.txt 工程
target_link_libraries() 指定具体哪个库文件,链接进编译工程
三、生成动态链接库到本地lib
文件夹内
# 生成动态链接库
add_library(canHandle SHARED src/localization.cpp src/Dbc.cpp src/CanIO.cpp)
# 指定动态链接库的生成路径为本地lib文件夹内
set_target_properties(canHandle PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
四、本地lib
文件夹内的动态链接库如果想被链接,编译
# 编译指令
link_directories(lib)
add_executable(${PROJECT_NAME} src/main.cpp )
target_link_libraries(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/lib/libcanHandle.so yaml-cpp ${rclcpp_LIBRARIES} ${sensor_msgs_LIBRARIES} ${interfaces_LIBRARIES})
针对(二)的示例:
-
首先声明我要链接哪个文件夹内的库
link_directories(lib)
# 本地lib
文件夹内有动态链接库需要链接 -
使用
target_link_libraries()
指令将动态库链接到可执行文件,本以为link_directories
已经设置了链接路径,动态库直接输名就可以了。测试发现,动态库需要完整的路径才能链接到。target_link_libraries( ${PROJECT_SOURCE_DIR}/lib/libcanHandle.so )
${PROJECT_SOURCE_DIR}
表示 当前工程目录绝对路径。 -
此处
libcanHandle.so
只有一个文件,写起来较简单,但是对于很多第三方库,会生成多个so
文件,需要一一链接,比较繁琐。所以,推荐使用find_package
的方式,就会有<NAME>_LIBRARIES
变量。
五、CMakeLists.txt
安装
如果想使用 ros2 run localization localization
指令启动可执行文件,需要将so
库安装到install
文件夹内的lib
文件夹。
安装库的语句
# 为了ros2启动,需要将so库安装到install里的lib文件夹下
install(FILES lib/libcanHandle.so
DESTINATION lib
)
安装可执行文件的语句
install(TARGETS
nav2_costmap_2d
nav2_costmap_2d_markers
nav2_costmap_2d_cloud
RUNTIME DESTINATION lib/${PROJECT_NAME}
)
安装头文件的语句
install(DIRECTORY include/
DESTINATION include/
)
安装其他文件的语句
install(FILES costmap_plugins.xml
DESTINATION share/${PROJECT_NAME}
)
安装launch
文件的语句
install(
DIRECTORY include launch params
DESTINATION share/${PROJECT_NAME}
)
六、动态链接库的使用
1. 动态链接库既需要在编译生成可执行文件时,被链接,作用类似于库的实现cpp
文件;
不然会报错,下面错误:
undefined reference to `rosidl_message_type_support_t const* rosidl_typesupport_cpp::get_message_type_support_handle<std_msgs::msg::UInt32_<std::allocator<void> > >()'
某个函数未被定义的引用。
2. 动态链接库也需要程序运行时,被动态加载,也就是这些so库确保位于${LD_LIBRARY_PATH}
里的路径中;
不然会报错,下面错误:
error while loading shared libraries: libcanHandle.so: cannot open shared object file: No such file or directory.