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用于将库(内部库和外部库)链接到目标。
3.与之相结合的命令
find_package()
和find_library()
是 CMake 中用于查找外部库的两个不同的命令。
(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()
命令找到并加载OpenCV包的配置文件后,会设置这几个变量供后续的 CMake 命令使用:
* OpenCV_FOUND
:一个布尔变量,指示是否找到了指定的包。如果找到了,该变量值为 TRUE
;否则,为 FALSE
。
* OpenCV_INCLUDE_DIRS
:一个表示包含目录的路径的变量。它用于指示包的头文件所在的目录路径。
* OpenCV_LIBRARIES
:一个表示库文件的路径或名称的变量。它用于指示包的库文件路径或库文件的名称。
* OpenCV_VERSION
:一个表示包的版本号的变量。它用于指示找到的包的版本。
后续的 CMake 命令使用:
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(${OpenCV_LIBS})
(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):
- 共享库是动态库的一种常见形式,上述动态库的特性同样适用于共享库。
- 共享库还可以被多个程序同时使用,这使得共享库更加灵活和可复用。
选择使用静态库、动态库或共享库取决于开发环境、项目需求和性能等因素。静态库适用于独立的、较小规模的项目,而动态库和共享库适用于大型项目或需要动态加载和共享的场景。