C++学习笔记—CMake
1. 最小可行的 CMakeLists.txt
思考:
我需要告诉 CMake 什么?
- 它需要知道最低兼容的 CMake 版本是多少?(
cmake_minimum_required
) - 我的项目叫什么名字?(
project
) - 我想生成什么?(一个可执行文件?一个库?)(
add_executable
或add_library
) - 这个生成目标需要哪些源文件?(
add_executable
或add_library
的参数)
1 | # 1. 指定 CMake 最低版本要求 |
解释:
cmake_minimum_required(VERSION 3.16)
:告诉 CMake 使用至少 3.16 版本的语法和功能。如果系统上的 CMake 版本低于此,它会报错。project(MyMinimalProject CXX)
:定义项目名为MyMinimalProject
,并声明主要语言是 C++ (CXX
)。这会让 CMake 检查 C++ 编译器是否可用。add_executable(my_app main.cpp)
:指示 CMake 创建一个名为my_app
的可执行文件,该文件由main.cpp
编译而来。
构建步骤:
- 打开终端,进入
my_minimal_project
目录。 - 创建构建目录并进入:
mkdir build && cd build
(推荐将构建产物与源码分开) - 运行 CMake 配置:
cmake ..
(..
指向包含CMakeLists.txt
的上级目录) - 编译项目:
make
(或者cmake --build .
) - 运行可执行文件:
./my_app
2. 更多源文件的处理
思考:
- 如何告诉
add_executable
所有需要的.cpp
文件?- 直接在
add_executable
命令中列出所有.cpp
文件? - 用一个变量来存储源文件列表,然后传递给
add_executable
?(更整洁) - 让 CMake 自动查找目录下的
.cpp
文件?(aux_source_directory
或file(GLOB ...)
,不推荐用于源文件,因为新增/删除文件时 CMake 可能不会自动检测到变化)
- 直接在
- 如果源文件分散在不同目录(如
src/
),CMake 如何找到它们?- 在文件名中包含相对路径(例如
src/main.cpp
)。
- 在文件名中包含相对路径(例如
- 如果头文件放在单独的目录(如
include/
),编译器如何找到它们?- 需要告诉 CMake 头文件的搜索路径。(
target_include_directories
)
- 需要告诉 CMake 头文件的搜索路径。(
代码示例:
假设项目结构为:
1 | my_multi_file_project/ |
include/helper.h
:
1 |
|
src/helper.cpp
:
1 |
|
src/main.cpp
:
1 |
|
CMakeLists.txt
:
1 | cmake_minimum_required(VERSION 3.16) |
解释:
set(SOURCES ...)
:创建了一个名为SOURCES
的 CMake 变量,存储了所有.cpp
文件的列表。这比在add_executable
中写一长串文件名更清晰,易于维护。add_executable(my_app ${SOURCES})
:使用${VAR_NAME}
语法来引用变量SOURCES
。target_include_directories(my_app PUBLIC include)
:这是关键一步。它告诉编译器在编译my_app
的源文件时,去include
目录下查找#include
的头文件。没有这一行,#include "helper.h"
会失败。PUBLIC
关键字表示这个包含目录不仅my_app
自己用,如果将来有其他 CMake 目标链接到my_app
,它们也会继承这个包含目录(对于可执行文件,通常用PRIVATE
即可,表示仅my_app
内部使用)。
3. 第三方库如何使用(以OpenCV为例)
思考:
- 如何让 CMake 找到已安装的 OpenCV 库?
- 使用
find_package
命令。这是 CMake 查找外部库的标准方式。
- 使用
- 我需要 OpenCV 的哪些部分(模块)?
- OpenCV 是模块化的(如
core
,imgproc
,highgui
等)。明确指定需要的模块可以减少不必要的依赖和链接。 find_package
允许通过COMPONENTS
参数指定所需模块。
- OpenCV 是模块化的(如
find_package
找到库后,如何将它链接到我的目标(my_app
)?- 需要告诉 CMake 两件事:
- 编译器在哪里找到 OpenCV 的头文件
- 链接器在哪里找到 OpenCV 的库文件并将它们链接到我的可执行文件
- 现代 CMake 方式(推荐): 使用
target_link_libraries
配合find_package
提供的 “Imported Target” (例如OpenCV::opencv_core
)。这种方式会自动处理头文件路径和库链接。 - 旧式 CMake 方式(了解即可):
find_package
会设置一些变量(如OpenCV_INCLUDE_DIRS
和OpenCV_LIBS
),然后手动使用target_include_directories
和target_link_libraries
。
- 需要告诉 CMake 两件事:
代码示例:
假设项目结构不变,我们修改 main.cpp
来使用 OpenCV。
src/main.cpp
(示例:读取并显示一张图片):
1 |
|
CMakeLists.txt
:
1 | cmake_minimum_required(VERSION 3.16) |
解释:
find_package(OpenCV REQUIRED COMPONENTS ...)
:指示 CMake 查找 OpenCV。REQUIRED
确保找不到时构建失败。COMPONENTS
列出了我们代码中实际用到的 OpenCV 模块 (core
对应cv::Mat
,imgcodecs
对应cv::imread
,highgui
对应cv::imshow
,cv::waitKey
)。message(STATUS ...)
:在 CMake 配置阶段打印信息,方便调试。target_link_libraries(my_app PRIVATE OpenCV::core ...)
:这是最关键的一步。它将my_app
链接到find_package
找到的 OpenCV 模块。使用OpenCV::module_name
这种 Imported Target 是现代 CMake 的推荐做法,它比旧方法更简洁、更健壮,CMake 会自动管理头文件路径 (target_include_directories
不需要再为 OpenCV 手动添加) 和库文件链接。