编译程序的时候,在链接库 ADD_LIBRARIES
踩到了大坑。
所以决定重新认真的好好学一下CMakeLists怎么编写。
编写 CMakeLists.txt
最常用的功能就是调用其他的.h头文件和.so/.a库文件,将.cpp/.c/.cc文件编译成可执行文件或者新的库文件。
每日一问:领导给买显卡了吗,学PCL了吗
:没有。
CMakeLists.txt 内容
set
# CMake最低版本要求
cmake_minimum_required(VERSION 3.1)
# 指定为C++11 版本
set(CMAKE_CXX_STANDARD 11)
# 设置指定的C++编译器版本是必须的,如果不设置,或者为FALSE,则指定版本不可用时,会使用上一版本。
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
# set make model
set( CMAKE_BUILD_TYPE Debug)
# 如果想要生成的可执行文件拥有符号表,可以gdb调试,就直接加上这句
# g是保留调试符号,wall是开启所有警告
add_definitions("-Wall -g")
在这里,set(变量 value)
可以设置变量variable的值为value。
cmake中所有变量都是string类型的,可以使用 set()
来声明一个变量, unset()
来移除一个变量。
CMAKE_BUILD_TYPE
有4种 build 方式:
- Release —— 不可以打断点调试,程序开发完成后发行使用的版本,占的体积小。 它对代码做了优化,因此速度会非常快,在编译器中使用命令: -O3 -DNDEBUG 可选择此版本。
- Debug ——调试的版本,体积大。在编译器中使用命令: -g 可选择此版本。
- MinSizeRel—— 最小体积版本。在编译器中使用命令:-Os -DNDEBUG可选择此版本。
- RelWithDebInfo—— 既优化又能调试。在编译器中使用命令:-O2 -g -DNDEBUG可选择此版本。
find_package
# 项目名称和版本
project(v8pkg VERSION 1.0)
# 找到工程需要引用的头文件(.h)和库文件(.so)
find_package(CUDA 10.2 REQUIRED)
# 变量的引用 使用美元符号 ${引用的变量名字}
if(${CUDA_FOUND})
# 使用message函数打印,分为三个消息类型:FATAL_ERROR(致命错误) WARNING(警告) STATUS(正常)
message("CUDA information:")
message(" CUDA_INCLUDE_DIRS: ${CUDA_INCLUDE_DIRS}")
message(" CUDA_LIBRARIES: ${CUDA_LIBRARIES}")
message(" CUDA_LIBRARY_DIRS: ${CUDA_LINK_DIRECTORIES}")
else()
message(FATAL_ERROR "CUDA-10.0 is not found in the system.")
endif()
在找到CUDA(其他库同理)后,会生成一些变量,如 CUDA_FOUND,CUDA_INCLUDE_DIRS
等。
# 设定OpenCV的路径
set(OpenCV_DIR "/media/nvidia/0739c4aa-df47-4f65-b632-6c50782120ab/opencv_4_sourcecode/build")
# 找到OpenCV库的位置,版本为 4
find_package(OpenCV 4 REQUIRED)
message(STATUS "OpenCV library status:")
message(STATUS " config: ${OpenCV_DIR}")
message(STATUS " version: ${OpenCV_VERSION}")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
关于 find_package
的接口:
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[NO_POLICY_SCOPE])
- PackageName:必填参数。待查找包的名称。此外它还决定两种搜索模下的.cmake文件名称:例如模块模式下的名称为Find[PackageName].cmake,而配置模式下为[lowercasePackageName]-config.cmake/[lowercasePackageName]-config-version.cmake。注意大小写。
- version:可选参数。如果指定就必须检查找到的包的版本是否和version兼容。如果指定EXACT则表示必须完全匹配的版本而不是兼容版本就可以。
- EXACT:该选项要求待查找包的版本必须与指定的版本精确匹配,因此如果指定的是一个版本范围,不能使用该参数。
- QUIET:可选参数,表示如果查找失败,不会在屏幕进行输出(但是如果指定了REQUIRED字段,则QUIET无效,仍然会输出查找失败提示语)。
- REQUIRED:表示一定要找到包,找不到的话就立即停掉整个CMake。而如果不指定REQUIRED则CMake会继续执行。
- MODULE:该选项指定find_package命令只使用module模式搜索方式查找。未指定该选项时,find_package会优先使用module模式搜索,仍未找到包时,会切换成config模式搜索。
- COMPONENTS:指定要查找的组件。通常一个包可能包含多个组件。
Module模式查找顺序
Module模式下是要查找到名为Find[PackageName].cmake的配置文件。
先在CMAKE_MODULE_PATH变量对应的路径中查找。如路径为空,或路径中查找失败,则在CMake安装目录(即CMAKE_ROOT变量)下的Modules目录下查找。这两个变量可以在CMakeLists.txt文件中打印查看具体内容:
# CMAKE_MODULE_PATH默认为空,可以利用set命令赋值。
message(STATUS "CMAKE_MODULE_PATH = ${CMAKE_MODULE_PATH}")
# CMAKE_ROOT 通常为 /usr/share/cmake-X.XX/Modules
message(STATUS "CMAKE_ROOT = ${CMAKE_ROOT}")
在安装CMake时,CMake为我们提供了很多开发库的FindXXX.cmake模块文件,可以通过命令查询:
funnywii@funnywii-3020S:~$ cmake --help-module-list | grep -E ^Find
FindALSA
FindASPELL
FindAVIFile
FindArmadillo
FindBISON
FindBLAS
FindBZip2
FindBacktrace
FindBoost
FindBullet
FindCABLE
FindCUDA
FindCURL
上面是一部分输出结果,可以看到CUDA,OpenCV等库都已经在 FindXXX.cmake
模块中,不管你是否已经安装了这些库。通常,库安装时会拷贝一份XXXConfig.cmake到系统目录中,因此在没有显式指定搜索路径时也可以顺利找到。
当然也可以自定义某个库的路径 [PackageName]_DIR,比如上面的 set(OpenCV_DIR "/media/nvidia/0739c4aa-df47-4f65-b632-6c50782120ab/opencv_4_sourcecode/build")
add_library
add_library
就是将指定的一些源文件打包成动态库和静态库,第一个参数就是生成库的名字,第二个参数是生成库的类型,静态库|动态(共享)和模块库,后面的参数就都是源文件。
但是如果有 IMPORTED
变量为 TRUE
,说明导入的是已编译好的库,不需要再制定源文件和头文件。
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[<source>...])
# Add libcam.so library
# 使用 src 文件夹中的 .cpp和.hpp 文件,编译的到名为 libcam.so的库(在默认设置下,名称会被编译为liblibcam.so)
add_library(libcam SHARED src/cam_block.cpp src/cam_block.hpp )
# 添加libcam需要的依赖库 OpenCV
target_link_libraries(libcam ${OpenCV_LIBS})
# Add liboptical.so library
# 导入文件夹下已经被编译好的liboptical.so库
add_library(liboptical SHARED IMPORTED)
set_target_properties(liboptical PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/../liboptical.so)
target_link_libraries
target_link_libraries
可以将Library链接到生成的目标上。在这里,生成的是.so库,那么就是将OpenCV库链接到 libcam
库上。
set_target_properties
在 add_library
中指定了 IMPORTED
后,需要在 set_target_properties
中的 PROPERTIES
指定 IMPORTED_LOCATION
。
set_target_properties(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)
关于CMAKE的内置变量,可以看 https://stdrc.cc/post/2021/12/28/cmake-variables/ 这篇文章,写的很清晰。
- CMAKE_SOURCE_DIR:当前 CMake source tree 的顶层,始终是项目根目录。
- CMAKE_BINARY_DIR:当前 CMake build tree 的顶层,始终是项目根目录下build的目录。
- CMAKE_CURRENT_SOURCE_DIR:当前正在处理的 source 目录
- CMAKE_CURRENT_BINARY_DIR:当前正在处理的 binary 目录
- CMAKE_CURRENT_LIST_FILE:当前正在处理的 CMake list 文件(CMakeLists.txt 或 *.cmake)
- CMAKE_CURRENT_LIST_DIR:当前正在处理的 CMake list 文件所在目录
- PROJECT_SOURCE_DIR:当前最近的 project() 命令所在 source 目录,例如:
project(v8pkg VERSION 1.0)
- PROJECT_BINARY_DIR:当前最近的 project() 命令对应 binary 目录,如果CMakeList.txt 中只有一个project,那么分别等同于
CMAKE_SOURCE_DIR
和CMAKE_BINARY_DIR
,但是如果后面添加新的project(),则会改变。
add_executable
用于将源文件和头文件编译成可执行文件,可执行文件的名称为 test_obj_detect
。
add_executable(test_obj_detect vp_detect.cpp
#src/controlcan.h
src/yolo_v2_class.hpp
src/cam_block.hpp
src/HungarianAlg.h src/HungarianAlg.cpp src/kcftracker.hpp src/kcftracker.cpp src/ffttools.hpp src/fhog.cpp src/fhog.hpp
src/labdata.hpp src/recttools.hpp src/tracker.h src/tracking_block.hpp src/tracking_block.cpp src/display.cpp
src/display.h
src/opticalflow_vp_detect.h src/readpra.cpp src/readpra.h src/other.cpp
src/other.h
#src/cansend.cpp src/cansend.h
src/dis_spe_cal.cpp src/dis_spe_cal.h src/measurement_package.h
src/ground_truth_package.h src/ukf.cpp src/ukf.h src/detector.cpp src/detector.h src/logging.h)
add_subdirectory
# 作用: 添加子文件夹,该语句会在执行完当前文件夹CMakeLists.txt之后执行src子目录下的CMakeLists.txt
# 用法:add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
# 示例:add_subdirectory(src)
link_libraries
与target_link_library存在区别
# link_libraries用在add_executable之前,
# target_link_libraries用在add_executable之后
# link_libraries用来链接静态库
# target_link_libraries用来链接导入的库,即按照header file + .lib + .dll方式隐式调用动态库的.lib库
add_dependencies
add_executable(funnywiisubscriber src/funnywiisubscriber.cpp)
target_link_libraries(funnywiisubscriber ${catkin_LIBRARIES})
# add_dependencies 必须在 add_executable 之后
add_dependencies(funnywiitest ${PROJECT_NAME}_gencpp)
add_dependencies(funnywiisubscriber ${PROJECT_NAME}_gencpp)
参考文章
[1] https://blog.csdn.net/zhanghm1995/article/details/105466372
[2] https://www.bilibili.com/video/BV14h41187FZ/?vd_source=aeef501feeeb9c8ccd28befde06d1208
[3] https://cmake.org/cmake/help/latest/command/add_library.html
[4] https://stdrc.cc/post/2021/12/28/cmake-variables/
[5] https://zhuanlan.zhihu.com/p/380394266