跳到主要内容

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(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 宏

特性functionmacro
变量作用域独立作用域调用者作用域
参数处理${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")

小结

本章我们学习了:

  1. CMake 语言基础:注释、字符串、列表
  2. 项目配置:cmake_minimum_required、project
  3. 构建目标:add_executable、add_library
  4. 编译配置:include 路径、定义、选项
  5. 条件判断:if、elseif、else
  6. 循环:foreach、while
  7. 函数和宏:自定义命令

练习

  1. 创建一个包含多个源文件的项目
  2. 使用条件判断设置不同平台的编译选项
  3. 编写一个函数来简化常用的目标配置
  4. 使用 configure_file 生成配置头文件