cmake_minimum_required(version 3.16)最稳妥,兼顾现代特性支持与兼容性;低于3.10官方已停止维护,3.16能稳定支持find_package(config required)、fetchcontent_declare等关键功能,避免旧版行为异常或变量不可靠问题。

cmake_minimum_required 版本选多少才不踩坑
低于 CMake 3.10 就别写了,官方已停止对 3.9 及更早版本的 patch 支持,很多现代写法(比如 target_link_libraries 的 PRIVATE/INTERFACE 用法)在 3.8 里要么报错要么行为异常。实际项目建议直接设为 cmake_minimum_required(VERSION 3.16)——它能稳定支持 find_package(... CONFIG REQUIRED)、FetchContent_Declare 和 add_subdirectory(... EXCLUDE_FROM_ALL),又不至于把 Windows 上还在用 VS2015 的老项目逼到绝路。
常见错误:写成 cmake_minimum_required(VERSION 3.1) 然后硬塞 set(CMAKE_CXX_STANDARD 17),结果 CMake 不认这个变量名(CMAKE_CXX_STANDARD 是 3.1 引入但直到 3.8 才真正可靠),编译器却按 C++11 解析代码,链接时报一堆 std::string_view undefined reference。
add_executable 和 add_library 的 target 名怎么命名
目标名不是文件名,也不是路径,而是 CMake 内部唯一标识符。它不能含 /、空格或点号(.),否则 target_link_libraries(my.app PRIVATE ...) 会解析失败;也不能和已有 target 重名,哪怕源码目录不同——CMake 全局查重。
推荐做法:
- 用小写字母 + 下划线,比如
http_server、json_parser - 库名加前缀避免冲突:
mylib_core、mylib_utils - 测试可统一用
test_开头:test_string_utils - 绝对不要用
add_executable(main ...)——太容易和别人撞名,也掩盖了模块意图
一个典型翻车场景:add_library(utils SHARED ...) 后又在另一处 find_package(Threads REQUIRED),CMake 会把 utils 当作 Threads 的 alias 处理,导致链接时漏掉 -lpthread。
target_include_directories 的 scope 到底怎么选
PUBLIC、PRIVATE、INTERFACE 不是“要不要导出头文件”的开关,而是控制“谁能看到这些路径”的传播规则。写错会导致头文件找不到,或头文件污染下游依赖。
判断依据只有一条:该路径下的头文件是否被当前 target 的 public 接口(比如 .h 文件里声明的函数)直接引用?
- 如果
src/a.cpp用了third_party/rapidjson/include,但头文件没暴露 rapidjson 类型 → 用PRIVATE - 如果
include/mylib.h里写了#include "rapidjson/document.h"→ 必须PUBLIC,否则下游target_link_libraries(app PRIVATE mylib)编译 app 时会报rapidjson/document.h: No such file -
INTERFACE几乎只用于 header-only 库,比如add_library(fmt INTERFACE)后跟target_include_directories(fmt INTERFACE ${fmt_DIR}/include)
性能影响:过度用 PUBLIC 会让所有依赖你的 target 都增加 include path,可能触发不必要的重新编译;而全用 PRIVATE 会导致下游反复手动加 target_include_directories,维护成本飙升。
find_package 怎么避免 silently fallback 到系统路径
find_package(OpenSSL REQUIRED) 看似简洁,但默认行为是先查系统 /usr/lib、再查 /opt/homebrew、最后才看 CMAKE_PREFIX_PATH。如果你本地编译了 OpenSSL 1.1.1w 放在 ./deps/openssl,CMake 却拉了系统自带的 1.0.2k,运行时大概率段错误。
必须显式约束查找范围:
- 加
CONFIG强制走OpenSSLConfig.cmake(现代包标配),拒绝用FindOpenSSL.cmake这种旧脚本:find_package(OpenSSL CONFIG REQUIRED) - 配合
PATHS锁定位置:find_package(OpenSSL CONFIG REQUIRED PATHS "${CMAKE_CURRENT_SOURCE_DIR}/deps/openssl") - 用
NO_DEFAULT_PATH彻底屏蔽系统路径(慎用,除非你 100% 控制所有依赖)
验证是否生效?加一行 message(STATUS "OpenSSL_FOUND = ${OpenSSL_FOUND}, OpenSSL_VERSION = ${OpenSSL_VERSION}"),构建时看输出路径是不是你期望的。
最常被忽略的是:find_package 成功后,真正要 link 的 target 名未必叫 OpenSSL——可能是 OpenSSL::SSL 或 OpenSSL::Crypto,查对应 Config.cmake 里的 add_library 定义,而不是凭经验写 target_link_libraries(app PRIVATE OpenSSL)。











