基础模板
cmake_minimum_required(VERSION 3.14) # 最低cmake版本,必须放最开头
project(demo LANGUAGES CXX C) # 项目名,指定语言
set(CMAKE_CXX_STANDARD 17) # c++标准
set(CMAKE_CXX_STANDARD_REQUIRED ON) # 强制要求,不回退
set(CMAKE_CXX_EXTENSIONS OFF) # 关闭编译器扩展(-std=c++17而不是gnu++17)
set 变量
set(VAR "hello") #普通变量
set(SRC_LIST src/a.cpp src/b.cpp) #源文件列表,空格分割
set(VAR ${VAR} "append") #追加内容
message(STATUS "打印变量: ${VAR}") #打印日志
# 布尔变量 ON/OFF
set(ENABLE_TEST ON CACHE BOOL "是否开启单元测试")
路径相关
${PROJECT_SOURCE_DIR} #项目根目录(CMakeLists.txt所在顶层)
${CMAKE_SOURCE_DIR} #最顶层项目目录,submodule也不变
${CMAKE_CURRENT_SOURCE_DIR} #当前CMakeLists.txt所在目录
${CMAKE_BINARY_DIR} #构建输出目录build/
${PROJECT_BINARY_DIR}
#头文件路径,只是告诉编译器-I,不会编译文件
include_directories(include)
#推荐现代写法 target_include_directories,作用域绑定目标
target_include_directories(target_name
PUBLIC include
PRIVATE src
INTERFACE public_api
)
PUBLIC:自己用,依赖我的项目也会继承该头文件路径 PRIVATE:仅当前目标内部使用,对外不暴露 INTERFACE:我不用,依赖我的目标需要
编译目标 add_executable /add_library
#可执行程序
add_executable(main src/main.cpp)
#静态库 *.a
add_library(mylib STATIC ${SRC_LIST})
#动态库 *.so
add_library(mylib SHARED ${SRC_LIST})
#对象库,只编译不归档,多个目标复用编译产物
add_library(objlib OBJECT src/x.cpp)
target_link_libraries 链接库
target_link_libraries(main
PRIVATE mylib pthread
)
#系统库直接写名字,pthread dl rt
Git Submodule 引入外部库
#如果submodule没拉取,提示
find_git_submodules()
add_subdirectory(extern/mempool-cpp) #把子项目加入构建
#然后直接链接
target_link_libraries(main PRIVATE mempool)
子项目内部必须自己写 CMakeLists.txt 对外暴露库目标。
条件判断 if‑else
if(ENABLE_TEST)
message(STATUS "编译测试")
else()
message(STATUS "关闭测试")
endif()
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
endif()
#逻辑运算符 AND OR NOT
if(WIN32)
elseif(UNIX AND NOT APPLE) #Linux
endif()
常用平台变量:WIN32、UNIX、APPLE
编译选项 flags
#全局,不推荐
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
#现代:仅给单个目标设置编译选项
target_compile_options(mytarget
PRIVATE
-Wall
-Wextra
)
#定义宏,代码里#ifdef使用
target_compile_definitions(mytarget
PRIVATE
DEBUG_LOG=1
)
单元测试 enable_testing
enable_testing() #必须放在顶层
add_executable(test_demo tests/test.cpp)
target_link_libraries(test_demo PRIVATE mylib)
add_test(NAME test_demo COMMAND test_demo)
构建后执行:ctest
文件查找
#递归搜集所有cpp,简单项目用,大项目不推荐
file(GLOB_RECURSE SRC_FILES src/*.cpp)
install 安装
install(TARGETS mylib main
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
install(DIRECTORY include/ DESTINATION include)
常用构建类型
cmake -DCMAKE_BUILD_TYPE=Debug ..
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. #release + g调试符号,开发最推荐
作用域重点(高频踩坑)
include_directories/add_definitions全局,影响之后所有目标,旧写法。target_xxx系列:target_include_directories/target_link_libraries/target_compile_options,绑定具体 target,有 PUBLIC/PRIVATE/INTERFACE,优先使用。
常用命令行
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
make -j$(nproc)
ctest
子目录 add_subdirectory
project/
├── CMakeLists.txt
└── src/
└── CMakeLists.txt
顶层:add_subdirectory(src),子目录 CMakeLists 构建库。子目录定义的 target 可以在上层直接链接。
简单完整示例模板
cmake_minimum_required(VERSION 3.14)
namespace(file‑vault‑cpp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(extern/mempool‑cpp)
file(GLOB_RECURSE LIB_SOURCES src/*.cpp)
add_library(vault_lib STATIC ${LIB_SOURCES})
target_include_directories(vault_lib PUBLIC include)
target_link_libraries(vault_lib PUBLIC mempool pthread)
add_executable(server src/main_server.cpp)
target_link_libraries(server PRIVATE vault_lib)
option(BUILD_TEST "build test" ON)
if(BUILD_TEST)
enable_testing()
add_executable(test_app tests/test.cpp)
target_link_libraries(test_app PRIVATE vault_lib)
add_test(NAME all_test COMMAND test_app)
endif()
踩坑清单
${}取值,不加 ${} 代表变量名本身。add_subdirectory(submodule)前要 git submodule update 拉取代码,否则目录为空报错。CMAKE_CXX_STANDARD 只对目标生效,旧编译器不要写
-std=c++17放到 flags,优先 set 标准变量。GLOB 不会监控文件新增删除,新增源文件需要重新执行 cmake。
PUBLIC/PRIVATE 搞反会出现头文件找不到,链接外部库一般 PRIVATE。
字符串比较要用
STREQUAL,if(VAR == "xxx")是旧 cmake 扩展。不要混用全局 include_directories,尽量全部 target_include_directories。