CMake 基础语法
本章将介绍 CMakeLists.txt 的基本语法和常用命令,帮助你理解 CMake 构建脚本的核心概念。
CMake 语言基础
注释
# 这是单行注释
#[[
这是
多行
注释
]]
命令调用
CMake 命令不区分大小写,但约定使用小写:
# 推荐写法(小写)
project(MyProject)
# 也可以(但不推荐)
PROJECT(MyProject)
Project(MyProject)
字符串
# 双引号字符串(支持变量替换)
set(message "Hello, ${name}!")
# 引号内的特殊字符需要转义
set(path "C:\\Users\\Admin")
# 原始字符串(不进行转义)
set(raw_path [[C:\Users\Admin]])
列表
# 使用分号分隔的字符串
set(SOURCES "a.cpp;b.cpp;c.cpp")
# 使用空格分隔的多个参数
set(SOURCES a.cpp b.cpp c.cpp)
# 多行列表
set(SOURCES
src/main.cpp
src/utils.cpp
src/config.cpp
)
# 追加元素
list(APPEND SOURCES src/new.cpp)
# 列表操作
list(LENGTH SOURCES count) # 获取长度
list(GET SOURCES 0 first) # 获取第一个元素
list(REMOVE_ITEM SOURCES old) # 移除元素
项目配置
cmake_minimum_required
指定需要的最低 CMake 版本:
cmake_minimum_required(VERSION 3.15)
# 指定版本范围
cmake_minimum_required(VERSION 3.15...3.28)
# 设置策略版本
cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
project
定义项目名称和属性:
# 基本项目定义
project(MyProject)
# 完整项目定义
project(MyProject
VERSION 1.0.0
DESCRIPTION "A sample project"
LANGUAGES CXX C
)
# 项目版本变量
# PROJECT_VERSION = 1.0.0
# PROJECT_VERSION_MAJOR = 1
# PROJECT_VERSION_MINOR = 0
# PROJECT_VERSION_PATCH = 0
构建目标
add_executable
创建可执行目标:
# 基本语法
add_executable(target_name source1.cpp source2.cpp ...)
# 示例
add_executable(myapp
src/main.cpp
src/utils.cpp
)
# 从列表创建
set(SOURCES src/main.cpp src/utils.cpp)
add_executable(myapp ${SOURCES})
add_library
创建库目标:
# 静态库
add_library(mylib STATIC
src/lib.cpp
src/helper.cpp
)
# 动态库
add_library(mylib SHARED
src/lib.cpp
src/helper.cpp
)
# 模块库(用于运行时加载)
add_library(mylib MODULE
src/plugin.cpp
)
# 对象库(编译但不归档)
add_library(mylib OBJECT
src/common.cpp
)
# 接口库(仅头文件)
add_library(mylib INTERFACE)
target_link_libraries
链接库到目标:
# 链接库
target_link_libraries(myapp
mylib # 项目内的库
pthread # 系统库
${OpenCV_LIBS} # 外部库
)
# 链接可见性
target_link_libraries(myapp
PUBLIC public_lib # 传播给依赖者
PRIVATE private_lib # 仅当前目标使用
INTERFACE interface_lib # 仅对依赖者可用
)
可见性说明:
| 可见性 | 当前目标 | 依赖者 |
|---|---|---|
| PUBLIC | 可用 | 可用 |
| PRIVATE | 可用 | 不可用 |
| INTERFACE | 不可用 | 可用 |
编译配置
target_include_directories
设置头文件搜索路径:
target_include_directories(myapp
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_compile_definitions
添加预处理器定义:
target_compile_definitions(myapp
PRIVATE
DEBUG_MODE
VERSION="1.0"
PUBLIC
USE_FEATURE_X
)
target_compile_options
添加编译选项:
target_compile_options(myapp
PRIVATE
-Wall
-Wextra
-O2
)
target_compile_features
指定需要的 C++ 特性:
# 要求 C++17
target_compile_features(myapp PUBLIC cxx_std_17)
# 要求特定特性
target_compile_features(myapp PRIVATE
cxx_lambdas
cxx_auto_type
)
set_target_properties
设置目标属性:
set_target_properties(myapp PROPERTIES
OUTPUT_NAME "my_application" # 输出文件名
CXX_STANDARD 17 # C++ 标准
CXX_STANDARD_REQUIRED ON # 强制要求标准
CXX_EXTENSIONS OFF # 禁用编译器扩展
)
构建类型
设置构建类型
# 默认构建类型
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# 可选值:Debug, Release, RelWithDebInfo, MinSizeRel
构建类型对比
| 类型 | 优化 | 调试信息 | 用途 |
|---|---|---|---|
| Debug | 无 | 完整 | 开发调试 |
| Release | 完全 | 无 | 生产发布 |
| RelWithDebInfo | 部分 | 完整 | 性能分析 |
| MinSizeRel | 大小 | 无 | 最小体积 |
自定义编译标志
# 全局编译标志
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
# 目标特定的编译标志(推荐)
target_compile_options(myapp PRIVATE
$<$<CONFIG:Debug>:-g -O0>
$<$<CONFIG:Release>:-O3>
)
文件操作
file 命令
# 读取文件
file(READ "config.txt" CONFIG_CONTENT)
# 写入文件
file(WRITE "output.txt" "Hello, World!")
# 追加内容
file(APPEND "log.txt" "New log entry\n")
# 列出文件
file(GLOB SOURCES "src/*.cpp")
file(GLOB_RECURSE SOURCES "src/**/*.cpp")
# 复制文件
file(COPY "data" DESTINATION "${CMAKE_BINARY_DIR}")
# 创建目录
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/output")
configure_file
配置文件(替换变量):
# config.h.in
#cmakedefine VERSION "@PROJECT_VERSION@"
#cmakedefine HAVE_FEATURE_X
# CMakeLists.txt
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/config.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/config.h"
)
条件判断
if 语句
# 基本条件
if(WIN32)
message("Building on Windows")
elseif(UNIX)
message("Building on Unix")
else()
message("Unknown platform")
endif()
# 布尔判断
if(ENABLE_FEATURE)
# ...
endif()
# 变量比较
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
# ...
endif()
# 版本比较
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.15")
# ...
endif()
# 文件存在
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/config.h")
# ...
endif()
逻辑运算
# AND
if(WIN32 AND MSVC)
# Windows + MSVC
endif()
# OR
if(UNIX OR APPLE)
# Unix or macOS
endif()
# NOT
if(NOT DEBUG_MODE)
# ...
endif()
循环
foreach 循环
# 遍历列表
foreach(file IN LISTS SOURCES)
message("Processing: ${file}")
endforeach()
# 遍历范围
foreach(i RANGE 0 9)
message("Number: ${i}")
endforeach()
# 遍历多项
foreach(arg IN ITEMS a b c)
message("Item: ${arg}")
endforeach()
while 循环
set(i 0)
while(i LESS 10)
message("i = ${i}")
math(EXPR i "${i} + 1")
endwhile()
函数和宏
function
function(add_custom_target name)
message("Creating target: ${name}")
add_executable(${name} $\{ARGN\})
endfunction()
# 调用
add_custom_target(myapp main.cpp utils.cpp)
宏
macro(print_list list_name)
message("List ${list_name}:")
foreach(item IN LISTS ${list_name})
message(" ${item}")
endforeach()
endmacro()
set(MY_LIST a b c)
print_list(MY_LIST)
函数 vs 宏
| 特性 | function | macro |
|---|---|---|
| 变量作用域 | 独立作用域 | 调用者作用域 |
| 参数处理 | ${ARGN} | ${ARGN} |
| 返回值 | set(结果 PARENT_SCOPE) | 直接设置变量 |
消息输出
message 命令
# 普通消息
message("Configuring project...")
# 状态消息
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
# 警告
message(WARNING "This feature is deprecated")
# 错误(继续处理)
message(SEND_ERROR "Configuration error")
# 致命错误(立即停止)
message(FATAL_ERROR "Cannot continue")
小结
本章我们学习了:
- CMake 语言基础:注释、字符串、列表
- 项目配置:cmake_minimum_required、project
- 构建目标:add_executable、add_library
- 编译配置:include 路径、定义、选项
- 条件判断:if、elseif、else
- 循环:foreach、while
- 函数和宏:自定义命令
练习
- 创建一个包含多个源文件的项目
- 使用条件判断设置不同平台的编译选项
- 编写一个函数来简化常用的目标配置
- 使用 configure_file 生成配置头文件