cmakelists.txt需以cmake_minimum_required(version 3.10)和project()开头,二者顺序不可颠倒;add_executable/add_library的目标名不能含路径或连字符;优先使用target_include_directories而非include_directories;cmake_build_type不应硬编码,而应通过命令行传入。

直接上手写 CMakeLists.txt 之前,先明确一点:它不是“写完就能编译”的脚本,而是告诉 CMake “我要生成什么、用什么编译、依赖哪些东西”——最终靠 make 或 ninja 才真正编译。版本低于 cmake_minimum_required(VERSION 3.10) 的写法,在现代项目里基本会卡住。
cmake_minimum_required 和 project 必须放在最开头
这两条指令必须是文件前两行(或至少是前两条有效指令),顺序不能颠倒,否则 CMake 直接报错退出。
-
cmake_minimum_required(VERSION 3.10)不只是“建议”,它是硬性门槛:低于该版本时 CMake 会终止解析,并提示类似CMake Error at CMakeLists.txt:1 (cmake_minimum_required): CMake 3.10 or higher is required. -
project(MyApp VERSION 1.2 LANGUAGES CXX)会自动定义PROJECT_NAME、PROJECT_VERSION、CMAKE_PROJECT_NAME等变量;省略LANGUAGES时默认支持 C 和 CXX,但显式写上更稳妥,尤其当你混用 C 和 C++ 源码时 - 别写成
project("MyApp")—— 引号在 CMake 中不是必需的,加了反而容易和变量拼接出错
add_executable 和 add_library 的 target 名不能含路径或特殊字符
目标名(即第一个参数)只是逻辑标识符,不是输出文件名,也不参与路径拼接。它会被用于后续 target_link_libraries 和属性设置。
- 错误写法:
add_executable(src/main.cpp)或add_executable(my-app)—— 前者被当作文件路径处理导致找不到源码,后者因连字符触发 CMake 解析异常 - 正确写法:
add_executable(myapp main.cpp),然后用set_target_properties(myapp PROPERTIES OUTPUT_NAME "my-app")控制最终二进制名 -
add_library(mylib STATIC lib.cpp)和add_library(mylib SHARED lib.cpp)区别只在链接方式,但 target 名不能和已存在的可执行目标重名,否则构建失败
include_directories 是过时写法,优先用 target_include_directories
include_directories 全局生效,容易污染其他 target;而 target_include_directories 能精确控制作用域,是现代 CMake 推荐方式。
- 旧写法:
include_directories(${PROJECT_SOURCE_DIR}/include)—— 所有后续add_executable都会带上这个路径,哪怕某个可执行目标根本不用它 - 新写法:
target_include_directories(myapp PRIVATE ${PROJECT_SOURCE_DIR}/include),其中PRIVATE表示仅 myapp 编译时可见,不传递给依赖它的其他 target - 如果头文件要被下游 target 使用(比如你写的库提供公共 API),改用
PUBLIC或INTERFACE,否则链接时可能报undefined reference
CMAKE_BUILD_TYPE 不该写死在 CMakeLists.txt 里
硬编码 set(CMAKE_BUILD_TYPE Debug) 看似方便,实则破坏外部构建控制权,且在多配置生成器(如 Visual Studio、Xcode)下完全失效。
- 正确做法是:不在
CMakeLists.txt中设CMAKE_BUILD_TYPE,而是通过命令行传入:cmake -B build -DCMAKE_BUILD_TYPE=Release - 若需根据不同构建类型启用不同编译选项,用
if判断:if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_options(myapp PRIVATE -g -O0) endif() - 注意:
CMAKE_BUILD_TYPE只对单配置生成器(如 Makefile、Ninja)有意义;VS/Xcode 用户应忽略它,改用configuration机制
真正容易被忽略的是变量作用域——set() 定义的变量默认只在当前目录作用域生效,跨子目录要用 set(... PARENT_SCOPE) 或改用 target_* 系列指令。一个没注意这点的 add_subdirectory 就能让 include 路径突然失效。











