CMake 语法详解
1. 基础语法结构
基本命令格式
command_name(argument1 argument2 ...)
注释
# 这是单行注释
#[[
这是多行注释
可以跨越多行
]]
2. 变量和字符串
变量定义和使用
# 设置变量
set(MY_VARIABLE "Hello World")
set(NUMBER_VARIABLE 42)
set(LIST_VARIABLE item1 item2 item3)
# 使用变量
message("Variable: ${MY_VARIABLE}")
# 环境变量
set(ENV{PATH} "$ENV{PATH}:${CMAKE_CURRENT_SOURCE_DIR}")
# 缓存变量(用户可配置)
set(MY_OPTION "ON" CACHE BOOL "Description")
字符串操作
# 字符串连接
set(STR1 "Hello")
set(STR2 "World")
set(RESULT "${STR1} ${STR2}") # "Hello World"
# 字符串长度
string(LENGTH "${MY_VARIABLE}" STR_LENGTH)
# 子字符串
string(SUBSTRING "${MY_VARIABLE}" 0 5 SUB_STR)
# 查找和替换
string(FIND "${MY_VARIABLE}" "World" POSITION)
string(REPLACE "Hello" "Hi" NEW_STR "${MY_VARIABLE}")
3. 流程控制
条件语句
if(condition)
# 代码块
elseif(another_condition)
# 代码块
else()
# 代码块
endif()
条件表达式
# 布尔运算
if(VARIABLE) # 如果变量已定义且不为 false
if(NOT VARIABLE) # 取反
if(VARIABLE1 AND VARIABLE2) # 与
if(VARIABLE1 OR VARIABLE2) # 或
# 比较运算
if("${VAR}" STREQUAL "value") # 字符串相等
if(NUMBER_VAR EQUAL 42) # 数字相等
if(NUMBER_VAR GREATER 10) # 大于
if(NUMBER_VAR LESS 100) # 小于
# 文件操作
if(EXISTS "file.txt") # 文件存在
if(IS_DIRECTORY "dir") # 是目录
# 类型检查
if(DEFINED VARIABLE) # 变量已定义
if(COMMAND command_name) # 命令存在
循环语句
# foreach 循环
foreach(item IN ITEMS apple banana cherry)
message("Fruit: ${item}")
endforeach()
foreach(i RANGE 1 10 2) # 从1到10,步长为2
message("Number: ${i}")
endforeach()
# while 循环
set(counter 0)
while(counter LESS 5)
message("Counter: ${counter}")
math(EXPR counter "${counter} + 1")
endwhile()
4. 函数和宏
函数定义
function(my_function arg1 arg2)
message("Arguments: ${arg1} ${arg2}")
set(${arg1}_RESULT "processed" PARENT_SCOPE) # 返回给父作用域
endfunction()
# 调用函数
my_function("hello" "world")
宏定义
macro(my_macro arg1 arg2)
message("Arguments: ${arg1} ${arg2}")
set(${arg1}_RESULT "processed") # 会影响调用者的作用域
endmacro()
# 调用宏
my_macro("hello" "world")
函数 vs 宏
- 函数: 有自己的作用域,不会影响调用者变量
- 宏: 没有独立作用域,会影响调用者变量
5. 项目配置
基本项目设置
cmake_minimum_required(VERSION 3.16) # 最低CMake版本
project(MyProject # 项目名
VERSION 1.0.0 # 版本号
DESCRIPTION "My CMake Project" # 描述
LANGUAGES CXX C # 支持的语言
)
编译器选项
# C++ 标准设置
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 编译选项
if(MSVC)
target_compile_options(my_target PRIVATE /W4 /WX)
else()
target_compile_options(my_target PRIVATE -Wall -Wextra -Werror)
endif()
# 预处理器定义
target_compile_definitions(my_target
PRIVATE
MY_DEFINITION=1
"OTHER_DEFINITION=\"value\""
)
6. 目标管理
创建目标
# 可执行文件
add_executable(my_app
main.cpp
utils.cpp
headers.h
)
# 静态库
add_library(my_lib STATIC
lib_source.cpp
lib_header.h
)
# 动态库
add_library(my_shared_lib SHARED
shared_source.cpp
)
目标属性设置
# 包含目录
target_include_directories(my_target
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# 链接库
target_link_libraries(my_target
PUBLIC
Threads::Threads
${CMAKE_DL_LIBS}
PRIVATE
my_internal_lib
)
# 编译特性
target_compile_features(my_target
PRIVATE
cxx_std_17
cxx_auto_type
)
7. 查找包和依赖
find_package 使用
# 查找系统包
find_package(OpenCV REQUIRED COMPONENTS core imgproc)
# 使用找到的包
if(OpenCV_FOUND)
target_link_libraries(my_app ${OpenCV_LIBS})
target_include_directories(my_app PRIVATE ${OpenCV_INCLUDE_DIRS})
endif()
pkg-config 集成
find_package(PkgConfig REQUIRED)
pkg_check_modules(FFMPEG REQUIRED IMPORTED_TARGET
libavcodec
libavformat
libavutil
)
target_link_libraries(my_app PkgConfig::FFMPEG)
8. 目录和文件操作
文件操作
# 文件操作
file(READ "file.txt" CONTENT)
file(WRITE "output.txt" "Hello World")
file(APPEND "log.txt" "New entry\n")
# 文件查找
file(GLOB SOURCE_FILES "src/*.cpp")
file(GLOB_RECURSE HEADER_FILES "include/*.h")
# 目录操作
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/generated")
配置时文件生成
# 生成配置文件
configure_file(
"config.h.in"
"config.h"
@ONLY
)
# 自定义命令
add_custom_command(
OUTPUT "${CMAKE_BINARY_DIR}/generated.cpp"
COMMAND ${PYTHON_EXECUTABLE} script.py
DEPENDS script.py template.txt
COMMENT "Generating source file"
)
9. 生成器表达式
条件表达式
# 根据配置设置不同选项
target_compile_definitions(my_target
PRIVATE
$<$<CONFIG:Debug>:DEBUG_MODE=1>
$<$<CONFIG:Release>:OPTIMIZE=1>
)
# 根据编译器设置
target_compile_options(my_target
PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/MP>
$<$<CXX_COMPILER_ID:GNU>:-fopenmp>
)
目标相关表达式
# 获取目标属性
$<TARGET_FILE:my_target> # 目标文件路径
$<TARGET_LINKER_FILE:my_lib> # 链接库文件
$<TARGET_PROPERTY:my_target,PROP> # 目标属性值
10. 安装和打包
安装规则
# 安装目标
install(TARGETS my_app my_lib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static
)
# 安装文件
install(FILES header.h DESTINATION include)
install(DIRECTORY docs/ DESTINATION share/doc)
# 安装时脚本
install(SCRIPT custom_install.cmake)
打包
# CPack 配置
set(CPACK_PACKAGE_NAME "MyApplication")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_VENDOR "My Company")
include(CPack)
11. 常用变量
CMake 内置变量
CMAKE_SOURCE_DIR # 源码根目录
CMAKE_BINARY_DIR # 构建根目录
CMAKE_CURRENT_SOURCE_DIR # 当前处理的CMakeLists.txt所在目录
CMAKE_CURRENT_BINARY_DIR # 当前目录对应的构建目录
CMAKE_CXX_COMPILER # C++ 编译器
CMAKE_CXX_FLAGS # C++ 编译标志
PROJECT_NAME # 项目名
PROJECT_SOURCE_DIR # 项目源码目录
CMAKE_INSTALL_PREFIX # 安装前缀
平台相关变量
WIN32 # Windows 平台
UNIX # Unix-like 平台
APPLE # Apple 平台
MSVC # MSVC 编译器
CMAKE_SYSTEM_NAME # 系统名称
12. 完整示例
基础项目配置
cmake_minimum_required(VERSION 3.16)
project(MyApp
VERSION 1.0.0
LANGUAGES CXX
)
# 编译器设置
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 查找依赖
find_package(Threads REQUIRED)
# 添加可执行文件
add_executable(my_app main.cpp)
# 设置属性
target_compile_features(my_app PRIVATE cxx_std_17)
target_link_libraries(my_app PRIVATE Threads::Threads)
# 安装规则
install(TARGETS my_app DESTINATION bin)
包含子目录
# 主 CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(MyProject)
add_subdirectory(src) # 包含源码目录
add_subdirectory(tests) # 包含测试目录
add_subdirectory(docs) # 包含文档目录