CMakeLists.txt快速入门

一:CMakeLists.txt的基本结构

编写CMakeLists.txt最常用的功能就是调用其他的.h头文件和.so/.a库文件,将.cpp/.c/.cc文件编译成可执行文件或者新的库文件

命令的官方网站:CMake Reference Documentation

最常用的命令如下:

# 本CMakeLists.txt的project名称
# 会自动创建两个变量,PROJECT_SOURCE_DIR和PROJECT_NAME
# ${PROJECT_SOURCE_DIR}:本CMakeLists.txt所在的文件夹路径
# ${PROJECT_NAME}:本CMakeLists.txt的project名称
project(xxx)

# 获取路径下所有的.cpp/.c/.cc文件,并赋值给变量中
aux_source_directory(路径 变量)

# 给文件名/路径名或其他字符串起别名,用${变量}获取变量内容
set(变量 文件名/路径/...)

# 添加编译选项
add_definitions(编译选项)

# 打印消息
message(消息)

# 编译子文件夹的CMakeLists.txt
# 当运行到add_subdirectory这一句时,会先将子文件夹进行编译
add_subdirectory(子文件夹名称)

# 将.cpp/.c/.cc文件生成.a静态库
# 注意,库文件名称通常为libxxx.so,在这里只要写xxx即可
add_library(库文件名称 STATIC 文件)

# 将.cpp/.c/.cc文件生成可执行文件
add_executable(可执行文件名称 文件)

# 规定.h头文件路径
include_directories(路径)

# 规定.so/.a库文件路径
link_directories(路径)

# 对add_library或add_executable生成的文件进行链接操作
# 注意,库文件名称通常为libxxx.so,在这里只要写xxx即可
target_link_libraries(库文件名称/可执行文件名称 链接的库文件名称)

通常一个CMakeLists.txt需按照下面的流程

project(xxx)                                          #必须

add_subdirectory(子文件夹名称)                         #父目录必须,子目录不必

add_library(库文件名称 STATIC 文件)                    #通常子目录(二选一)
add_executable(可执行文件名称 文件)                     #通常父目录(二选一)

include_directories(路径)                              #必须
link_directories(路径)                                 #必须

target_link_libraries(库文件名称/可执行文件名称 链接的库文件名称)       #必须

除了这些之外,就是些set变量的语句,if判断的语句,或者其他编译选项的语句,但基本结构都是这样的。

以上参考:【CMake】CMakeLists.txt的超傻瓜手把手教程(附实例源码)_cmakelists教程_Yngz_Miao的博客-CSDN博客

二:CmakeLists构建核心

一个项目最终要生成可执行文件并运行,核心主要有3点:

  • 1.可执行文件运行的是main函数里面的内容。(add_executable)
  • 2.编译时需要解析头文件。(include_directories)
  • 3.可执行文件需要链接各种库文件。(target_link_libraries)

因此以下三个指令是CmakeLists的构建核心,其余指令都是用来辅助这3个指令的。

# 1.规定.h头文件路径
include_directories(${PROJECT_SOURCE_DIR})
# 2.生成可执行文件,test为可执行文件的名称
add_executable(test main.cpp)
# 3.为可执行文件链接库文件 libadd.so
target_link_libraries(test add)

总结:父目录和子目录中的CmakeLists.txt文件加在一起,必须指定整个项目中用到的全部头文件路径,必须给可执行文件链接整个项目中用到的全部库文件。

三:Cmake核心指令

include_directories、add_library、link_directories、target_link_libraries这几个命令需要搞懂其中的联系与区别:

1.在这里首先要注意的一点是头文件和库文件的区别:(编译和链接)

头文件:头文件本身只是包含了函数和类的声明,主要用于在编译时进行类型检查和符号解析。头文件不包含函数的定义,它们不会被直接链接到可执行文件中。

库文件:是一组已经编译好的目标文件(对象文件)的集合。库可以包含已经定义的函数、变量和其他符号的实现。链接库可以被链接到可执行文件中,使得可执行文件能够调用库中的函数和使用库中的资源。

2.知道了区别之后,就容易理解上面几个命名的区别

编译命令:include_directories

链接命令:add_library、link_directories、target_link_libraries

其中include_directories指令是用于指定头文件所在目录,以便在编译源文件时可以找到所需的头文件。

其余三个则是和库文件相关的操作指令:

  • add_library:这个命令用于创建一个内部库,并将源文件(.cc/.c文件)编译成目标文件。它可以创建静态库或动态库。这个命令用于编译阶段,将源文件编译为目标文件。
  • link_directories:指令是用于指定外部库文件所在目录并查找外部库文件。它只是告诉链接器在指定的目录中查找库文件。这是链接阶段的命令。
  • target_link_libraries:这个命令用于将库链接到目标(可执行文件或库)中。它指定了目标需要依赖的库。这是链接阶段的命令。

总结:add_library用于创建新的内部库,link_directories用于指定查找已有的外部库的位置,target_link_libraries用于将库(内部库和外部库)链接到目标。

注意事项

include_directories指定的头文件目录的位置应与引用头文件时目录保持一致,否则编译会报错找不到头文件。例如头文件在${PROJECT_SOURCE_DIR}/include/vision_mapping/目录下,但是在cmakelists中指定的位置如下,那么在程序中引用头文件时,就要以vision_mapping/开头

include_directories(
    ${PROJECT_SOURCE_DIR}/include
)

3.与之相结合的命令

find_package()find_library()是 CMake 中用于查找外部库的两个不同的命令。

3.1 find_package()

它用于查找和定位已经安装在系统中的 CMake 包与之对应的配置文件(通常是Find<PackageName>.cmake),如果找到了包的配置文件,CMake 将加载该文件并设置相关变量,例如包含目录、库文件和其他配置选项。

例如,可以使用以下命令查找并加载 OpenCV 包:

find_package(OpenCV 3.4 REQUIRED)
  • 3.4:也可以不加,当系统中有多个opencv版本时,加上表示寻找特定的版本。
  • REQUIRED:如果找不到指定的包,则停止构建过程并产生一个错误。使用 REQUIRED 选项可以将某个包标记为必需的,如果无法找到该包,CMake 将无法继续构建项目。
  • QUIET:用于指示 CMake 在查找包时不输出详细信息,只显示错误和警告信息。使用 QUIET 选项可以减少 CMake 输出的冗余信息,使输出更简洁。

find_package查找路径对应的环境变量如下:

<package>_DIR

CMAKE_PREFIX_PATH

CMAKE_FRAMEWORK_PATH

CMAKE_APPBUNDLE_PATH

PATH

PATH中的路径如果以binsbin结尾,则自动回退到上一级目录,接着检查这些目录下的 :

<prefix>/(lib/<arch>|lib|share)/cmake/<name>*/          (U)
<prefix>/(lib/<arch>|lib|share)/<name>*/                (U)
<prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/  (U)

cmake找到这些目录后,会开始依次找<package>Config.cmakeFind<package>.cmake文件。找到后即可执行该文件并生成相关链接信息。 

使用 find_package() 命令找到并加载OpenCV包的配置文件后,会设置这几个变量供后续的 CMake 命令使用:

  • * OpenCV_FOUND:一个布尔变量,指示是否找到了指定的包。如果找到了,该变量值为 TRUE;否则,为 FALSE
  • * OpenCV_INCLUDE_DIRS:一个表示包含目录的路径的变量。它用于指示包的头文件所在的目录路径。
  • * OpenCV_LIBRARIES:一个表示库文件的路径或名称的变量。它用于指示包的库文件路径或库文件的名称。
  • * OpenCV_VERSION一个表示包的版本号的变量。它用于指示找到的包的版本。

后续的 CMake 命令使用:

include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(${OpenCV_LIBS})

3.2 find_library()

它用于在系统中查找指定的库文件。与 find_package() 不同,它主要用于查找单个库文件,而不是整个包。在找到库文件后,它会设置一些变量以供后续的 CMake 命令使用。以下是 find_library() 命令返回的常见变量:

  • * <VAR>一个表示找到的库文件的完整路径或名称的变量。通常,您可以在命令中指定一个名为 <VAR> 的变量,来接收找到的库文件的路径。
find_library(VAR NAMES mylib)
if (VAR_NOTFOUND)
    message("Could not find mylib")
else()
    message("Found mylib at ${VAR}")
endif()

四:静态库和动态库区别

静态库、动态库和共享库是软件开发中常见的三种库文件形式,它们在编译、链接和运行时的行为和特性有所不同。

1. 静态库(Static Library):

   - 编译时:静态库是在编译时被链接到目标程序中的库文件。编译器将库的代码和数据复制到最终的可执行文件中。
   - 链接时:静态库的代码被完整地复制到目标程序中,使得目标程序成为一个独立的执行文件,不再依赖于原始的静态库文件。
   - 运行时:目标程序在运行时不需要额外的依赖,因为静态库的代码已经完全包含在目标程序中

2. 动态库(Dynamic Library)

   - 编译时:动态库在编译时并不会被复制到目标程序中,而是作为一个独立的文件存在。
   - 链接时:目标程序在链接时仅包含对动态库的引用,而不是完整的库代码。链接器记录了目标程序需要的动态库的信息。
   - 运行时:在运行时,目标程序加载动态库文件,并在内存中共享该库的代码和数据。多个程序可以共享同一个动态库,减少了内存的占用。

3. 共享库(Shared Library):

   - 共享库是动态库的一种常见形式,上述动态库的特性同样适用于共享库。
   - 共享库还可以被多个程序同时使用,这使得共享库更加灵活和可复用。

选择使用静态库、动态库或共享库取决于开发环境、项目需求和性能等因素。静态库适用于独立的、较小规模的项目,而动态库和共享库适用于大型项目或需要动态加载和共享的场景。

五:Cmake辅助指令

1.打印地址

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jiqiang_z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值