CMake是生成构建系统配置的元构建工具,非自动构建工具;其核心是通过CMakeLists.txt声明项目信息、标准与依赖,再生成Makefile或VS解决方案等实际构建文件。

直接说结论:CMake 不是“自动构建工具”,而是“生成构建系统配置的元构建工具”——你写 CMakeLists.txt 的本质,是在告诉 CMake “我有哪些源文件、想生成什么目标、依赖哪些库、用什么编译器选项”,它再据此生成 Makefile(Linux/macOS)或 Visual Studio 解决方案(Windows)等真正干活的东西。
怎么写最简可用的 CMakeLists.txt
新手常卡在第一行就报错,核心是版本声明 + 项目名 + 最低 C++ 标准这三要素缺一不可:
-
cmake_minimum_required(VERSION 3.10):低于 3.10 会丢失target_compile_features等关键功能;3.20+ 更推荐(支持FetchContent、find_package(... CONFIG)更健壮) -
project(MyApp LANGUAGES CXX):必须显式声明CXX,否则add_executable可能默认当 C 项目处理,导致std::string等链接失败 -
set(CMAKE_CXX_STANDARD 17):不设的话默认可能是 C++98,现代库(如fmt、spdlog)直接编译不过
cmake_minimum_required(VERSION 3.10) project(MyApp LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(myapp main.cpp utils.cpp)
如何正确链接第三方库(静态 / 动态 / 头文件-only)
错误写法:target_link_libraries(myapp /usr/lib/libjsoncpp.a) —— 路径硬编码、无跨平台性、不检查是否存在。
正确做法分三类:
立即学习“C++免费学习笔记(深入)”;
-
系统级已安装库(如
zlib、OpenSSL):用find_package+target_link_libraries,CMake 自动找头文件路径和库文件 -
头文件-only 库(如
fmt、range-v3):用find_package(fmt REQUIRED)后target_link_libraries(myapp PRIVATE fmt::fmt),PRIVATE 表示仅本 target 使用,不传递给依赖它的其他 target -
未系统安装的本地库(如自己写的
libmath.a):先add_library(math STATIC IMPORTED),再set_property(TARGET math PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib/libmath.a),最后target_link_libraries(myapp PRIVATE math)
注意:target_link_libraries 的作用域(PUBLIC/PRIVATE/INTERFACE)直接影响依赖传递——比如你封装了一个库 mylib 并链接了 boost_filesystem,又希望使用者包含 mylib 就自动获得 boost_filesystem 的头文件,则需写 target_link_libraries(mylib PUBLIC boost::filesystem)。
常见报错与绕过陷阱
以下错误出现频率极高,且原因隐蔽:
-
Cannot find -lxxx:不是库没装,而是find_package(xxx)成功但没用target_link_libraries关联;或用了find_library却忘了target_link_libraries(... ${XXX_LIBRARY}) -
undefined reference to `xxxx' (from std::filesystem):Linux 下std::filesystem需要显式链接-lstdc++fs,加target_link_libraries(myapp PRIVATE stdc++fs)(Clang/GCC 9+) -
CMake Error at CMakeLists.txt:12 (add_executable): No SOURCES given:add_executable后没跟任何源文件,或变量为空(如set(SRCS)写了但没赋值),建议用message(STATUS "SRCS = ${SRCS}")调试 - Windows 下链接
.lib但运行时报dll not found:动态库路径没进PATH,开发期可在 CMake 中加set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)并把 DLL 拷到该目录
依赖管理进阶:用 FetchContent 嵌入子项目
不想手动下载/编译 gtest 或 spdlog?CMake 3.14+ 提供 FetchContent,直接从 Git 或 URL 拉取并作为子项目构建:
include(FetchContent) FetchContent_Declare( spdlog GIT_REPOSITORY https://github.com/gabime/spdlog.git GIT_TAG v1.12.0 ) FetchContent_MakeAvailable(spdlog) add_executable(myapp main.cpp) target_link_libraries(myapp PRIVATE spdlog::spdlog)
注意:FetchContent_MakeAvailable 会自动调用 add_subdirectory,所以不用再写一遍;但它会把子项目所有 target 暴露到当前作用域,命名冲突时可用 set(spdlog_SOURCE_DIR ${CMAKE_BINARY_DIR}/_deps/spdlog-src) 控制路径。
真正难的从来不是语法,而是搞清「谁负责提供头文件路径」「谁负责链接符号」「谁负责传播编译定义」——这三个问题理不清,target_include_directories、target_compile_definitions、target_link_libraries 就永远像在碰运气。









