FunnyWii
FunnyWii
Published on 2023-05-19 / 34 Visits
0
0

CMakeList.txt 学习笔记

编译程序的时候,在链接库 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 可以将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_DIRCMAKE_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)

与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


Comment