C++学习笔记—CMake
1. 最小可行的 CMakeLists.txt
思考:
我需要告诉 CMake 什么?
- 它需要知道最低兼容的 CMake 版本是多少?(
cmake_minimum_required) - 我的项目叫什么名字?(
project)- 会自动定义一些变量,如
- PROJECT_NAME:值为 “GreeterApp”
- GreeterApp_SOURCE_DIR 或 PROJECT_SOURCE_DIR:项目源代码根目录
- GreeterApp_BINARY_DIR 或 PROJECT_BINARY_DIR:构建目录
- 我想生成什么?(一个可执行文件?一个库?)(
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 手动添加) 和库文件链接。
