CMake入门手册
CMake入门手册
cmake是一个跨平台的构建系统生成器。类似于工作流,除了完成程序的生成(预处理、编译、汇编、链接)工作,还能完成测试、打包、安装等任务
准备工作
- 源码
CMakeLists.txt文件(区分大小写)
# 最小cmake文件
cmake_minimum_required(VERSION 3.5 FATAL_ERROR) # 声明cmake版本
project(recipe-01 LANGUAGES CXX) # 声明项目语言
add_executable(hello-world hello-world.cpp) # 创建可执行文件
执行cmake
# 跨平台方案
mkdir build
cd build
cmake .. # 通过指定CMakeLists.txt的位置来调用CMake
cmake --build . # 开始构建项目
NOTE:CMake语言不区分大小写,但是参数区分大小写
CMake常用命令
切换生成器
cmake -G
# =============================================================================
# 命令: cmake -G <generator> <path-to-source>
#
# 描述:
# 使用指定的生成器配置CMake项目,生成对应平台的构建系统文件。
# 此命令在CMake配置阶段执行,用于生成IDE项目文件或构建脚本。
#
# 参数:
# -G <generator> # 指定构建系统生成器
# # 此参数必须为cmake识别的有效生成器名称
#
# <path-to-source> # 包含CMakeLists.txt的源目录路径
# # 通常使用".."表示上一级目录
#
# 常用生成器选项:
# "Visual Studio 17 2022" # 生成Visual Studio 2022解决方案和项目文件
# "Ninja" # 生成Ninja构建文件(构建速度较快)
# "Unix Makefiles" # 生成标准的Unix Makefile文件
# "MinGW Makefiles" # 生成MinGW环境的Makefile文件
#
# 可选参数:
# -A <platform> # 指定目标平台架构(主要适用于Visual Studio生成器)
# # 示例: "x64", "Win32", "ARM", "ARM64"
#
# -T <toolset> # 指定工具集(适用于Visual Studio生成器)
# # 示例: "v143", "ClangCL", "v142"
#
# -D <var>[=<value>] # 定义或设置CMake缓存变量
# # 示例: -DCMAKE_BUILD_TYPE=Release
#
# 用法示例:
# cmake -G "Visual Studio 17 2022" -A x64 ..
# cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Debug ..
# cmake -G "Unix Makefiles" ..
#
# 注意:
# 1. 生成器名称包含空格时必须使用引号包围
# 2. 使用 `cmake --help` 可查看本机支持的所有生成器列表
# 3. 此命令应在专门的构建目录中执行,避免污染源代码目录
# 4. 不同的生成器会产生不同类型的构建文件:
# - Visual Studio: 生成.sln和.vcxproj文件
# - Ninja: 生成build.ninja文件
# - Makefiles: 生成Makefile文件
# 5. 生成器选择会影响后续的构建命令和构建流程
#
# 相关命令:
# cmake --build . # 执行构建过程
# cmake --install . # 执行安装过程
# cmake --help # 查看完整帮助信息
# =============================================================================
# 典型使用流程
# 创建并进入构建目录
mkdir build
cd build
# 使用指定生成器配置项目
cmake -G "Visual Studio 17 2022" -A x64 ..
# 执行构建
cmake --build .
编译
cmake build
# =============================================================================
# 命令: cmake --build
#
# 描述:
# 跨平台构建命令,用于执行实际的编译和链接过程。该命令抽象了底层的
# 构建系统(如make、ninja、msbuild等),提供统一的构建接口。
#
# 语法:
# cmake --build <dir> [options] [-- [native-options]]
#
# 参数:
# <dir> # 构建目录路径(通常是包含CMakeCache.txt的目录)
#
# 常用选项:
# --target <tgt> # 构建特定目标(默认为all目标)
# --config <cfg> # 指定构建配置(Debug|Release|RelWithDebInfo|MinSizeRel)
# --parallel [N] # 并行构建,可指定线程数
# --clean-first # 构建前先执行清理
# --verbose # 显示详细的构建命令
# -v # 同上,显示详细输出
#
# 用法示例:
# # 基本构建(使用默认配置)
# cmake --build .
#
# # 构建特定目标
# cmake --build . --target my_app
# cmake --build . --target clean
#
# # 指定构建配置
# cmake --build . --config Release
# cmake --build . --config Debug
#
# # 并行构建
# cmake --build . --parallel 8
# cmake --build . -j 8 # 简写形式
#
# # 构建前先清理
# cmake --build . --clean-first
#
# # 详细输出
# cmake --build . --verbose
#
# =============================================================================
使用场景
# 清理构建产物
cmake --build . --target clean
# 构建Release版本
cmake --build . --config Release
CMake常用函数
包管理
find_package
# =============================================================================
# 命令: find_package
#
# 描述:
# 查找并加载外部依赖包的设置。这是CMake中管理第三方依赖的主要方式,
# 可以自动查找系统安装的库,并设置包含路径、链接库、编译定义等。
#
# 语法:
# find_package(<PackageName> [version] [EXACT] [QUIET] [REQUIRED]
# [[COMPONENTS] [components...]]
# [OPTIONAL_COMPONENTS components...]
# [MODULE|CONFIG|NO_MODULE]
# [NO_POLICY_SCOPE]
# [NAMES name1 [name2 ...]]
# [CONFIGS config1 [config2 ...]]
# [HINTS path1 [path2 ...]]
# [PATHS path1 [path2 ...]]
# [PATH_SUFFIXES suffix1 [suffix2 ...]]
# [NO_DEFAULT_PATH]
# [NO_CMAKE_ENVIRONMENT_PATH]
# [NO_CMAKE_PATH]
# [NO_SYSTEM_ENVIRONMENT_PATH]
# [NO_CMAKE_PACKAGE_REGISTRY]
# [NO_CMAKE_BUILDS_PATH]
# [NO_CMAKE_SYSTEM_PATH]
# [NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
# [CMAKE_FIND_ROOT_PATH_BOTH |
# ONLY_CMAKE_FIND_ROOT_PATH |
# NO_CMAKE_FIND_ROOT_PATH])
#
# 常用参数:
# <PackageName> # 要查找的包名(如 SQLite3、Boost、OpenCV)
# version # 要求的最低版本(如 3.0)
# EXACT # 要求版本必须精确匹配
# QUIET # 不输出查找信息
# REQUIRED # 如果找不到包则报错
# COMPONENTS # 包的组件(如Boost的filesystem、system等)
#
# 用法示例:
# # 基本用法 - 查找包
# find_package(SQLite3)
#
# # 要求特定版本
# find_package(Boost 1.70)
#
# # 要求精确版本
# find_package(OpenCV 4.5 EXACT)
#
# # 必需依赖 - 找不到则报错
# find_package(ZLIB REQUIRED)
#
# # 查找特定组件
# find_package(Boost REQUIRED COMPONENTS filesystem system)
#
# # 静默查找
# find_package(Threads QUIET)
#
# 查找结果变量:
# 成功找到包后通常会设置以下变量:
# <PackageName>_FOUND # 是否找到包
# <PackageName>_INCLUDE_DIRS # 包含目录
# <PackageName>_LIBRARIES # 库文件
# <PackageName>_DEFINITIONS # 编译定义
# <PackageName>_VERSION # 找到的版本
#
# 现代导入目标方式:
# 现代CMake包通常提供导入目标,推荐使用:
# find_package(Threads REQUIRED)
# target_link_libraries(my_app Threads::Threads)
#
# find_package(OpenSSL REQUIRED)
# target_link_libraries(my_app OpenSSL::SSL OpenSSL::Crypto)
#
# 实际项目示例:
# # 查找SQLite3
# find_package(SQLite3 REQUIRED)
# if(SQLite3_FOUND)
# message(STATUS "Found SQLite3: ${SQLite3_VERSION}")
# target_link_libraries(my_app SQLite::SQLite3)
# endif()
#
# # 查找Boost
# find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system)
# target_link_libraries(my_app Boost::filesystem Boost::system)
#
# # 条件依赖
# find_package(OpenCV QUIET)
# if(OpenCV_FOUND)
# target_link_libraries(my_app OpenCV::OpenCV)
# target_compile_definitions(my_app PRIVATE HAVE_OPENCV)
# else()
# message(WARNING "OpenCV not found, disabled video support")
# endif()
#
# 查找机制:
# CMake会按以下顺序查找包配置:
# 1. <prefix>/lib/cmake/<PackageName>/ 目录
# 2. CMake特定的环境变量
# 3. 系统标准路径(/usr/lib, /usr/local/lib等)
# 4. Windows注册表
#
# 包配置文件:
# 包通常提供以下文件之一:
# - <PackageName>Config.cmake
# - Find<PackageName>.cmake
#
# 注意:
# 1. 包名区分大小写,通常使用首字母大写
# 2. 使用REQUIRED可以确保依赖满足,否则需要手动检查_FOUND变量
# 3. 现代CMake包提供导入目标(如Boost::filesystem),比直接使用变量更好
# 4. 如果系统没有安装,可以考虑使用FetchContent或子模块
# 5. 可以设置CMAKE_PREFIX_PATH变量添加自定义查找路径
#
# 相关命令:
# find_library() # 查找库文件
# find_path() # 查找包含目录
# find_program() # 查找可执行程序
# pkg_check_modules() # 使用pkg-config查找包
# FetchContent() # 下载和管理外部项目
# =============================================================================
# 使用示例
cmake_minimum_required(VERSION 3.20)
project(MiniGit LANGUAGES CXX)
# 尝试查找系统安装的SQLite3
find_package(SQLite3 REQUIRED)
add_library(mini_git_lib STATIC ...)
# 使用导入目标链接SQLite3
target_link_libraries(mini_git_lib PRIVATE SQLite::SQLite3)
# 不需要手动设置包含目录,导入目标会自动处理
构建目标命令
add_library
# =============================================================================
# 函数: add_library
#
# 描述:
# 创建并定义一个新的库目标。库可以是静态库、动态库或仅包含头文件的接口库。
# 该命令将指定的源文件编译并打包成库文件,供其他目标链接使用。
#
# 语法:
# add_library(<name> [STATIC | SHARED | MODULE | OBJECT | INTERFACE]
# [EXCLUDE_FROM_ALL]
# [source1] [source2 ...])
#
# 参数:
# <name> # 库目标的名称,在项目中必须唯一
#
# 库类型(可选):
# STATIC # 创建静态库(.a, .lib)
# SHARED # 创建动态库(.so, .dll)
# MODULE # 插件式动态库(不会被链接,可能动态加载)
# OBJECT # 对象库(一组对象文件的集合)
# INTERFACE # 接口库(无源文件,仅传递属性)
#
# EXCLUDE_FROM_ALL # 从默认的all目标中排除此库
#
# sourceN # 库的源文件列表(.cpp, .c等)
#
# 用法示例:
# # 创建静态库
# add_library(math_static STATIC src/math.cpp src/vector.cpp)
#
# # 创建动态库
# add_library(math_shared SHARED src/math.cpp src/vector.cpp)
#
# # 创建仅头文件库(接口库)
# add_library(math_interface INTERFACE)
# target_include_directories(math_interface INTERFACE include)
#
# # 创建对象库
# add_library(math_objects OBJECT src/math.cpp src/vector.cpp)
#
# 注意:
# 1. 如果省略库类型,CMake会根据BUILD_SHARED_LIBS变量或项目设置选择默认类型
# 2. 静态库在链接时会被完整复制到可执行文件中
# 3. 动态库在运行时被加载,可被多个程序共享
# 4. 接口库不生成实际的库文件,仅用于传递编译要求
# 5. 对象库生成.o/.obj文件,可在其他目标中重复使用
#
# 生成文件:
# Linux: lib<name>.a (静态), lib<name>.so (动态)
# Windows: <name>.lib (静态), <name>.dll + <name>.lib (动态)
# =============================================================================
add_executable
# =============================================================================
# 函数: add_executable
#
# 描述:
# 定义一个可执行文件目标。该命令指定用于生成可执行程序的源文件,
# 并创建一个可以在构建系统中编译和链接的目标。
#
# 语法:
# add_executable(<name> [WIN32] [MACOSX_BUNDLE]
# [EXCLUDE_FROM_ALL]
# [source1] [source2 ...])
#
# 参数:
# <name> # 可执行文件目标的名称,在项目中必须唯一
#
# 可选关键字:
# WIN32 # 在Windows上创建GUI应用程序(而非控制台程序)
# MACOSX_BUNDLE # 在macOS上创建应用程序包(.app)
# EXCLUDE_FROM_ALL # 从默认的all目标中排除,需要显式构建
#
# source1... # 构建可执行文件所需的源文件列表
# # 支持.cpp, .c, .cc等源文件格式
#
# 返回值:
# 无返回值,但会创建一个可以在后续命令中引用的目标<name>
#
# 用法示例:
# # 基本用法 - 从单个源文件创建可执行文件
# add_executable(my_app main.cpp)
#
# # 从多个源文件创建可执行文件
# add_executable(my_app
# main.cpp
# utils.cpp
# network.cpp
# )
#
# # 使用变量包含源文件列表
# set(APP_SOURCES
# src/main.cpp
# src/utils.cpp
# src/network.cpp
# )
# add_executable(my_app ${APP_SOURCES})
#
# # 创建Windows GUI应用程序
# add_executable(my_win_app WIN32 win_main.cpp)
#
# # 创建macOS应用程序包
# add_executable(my_mac_app MACOSX_BUNDLE mac_main.cpp)
#
# # 排除从默认构建
# add_executable(test_runner EXCLUDE_FROM_ALL test_main.cpp)
#
# 注意:
# 1. 目标名称在项目中必须唯一,不能与库目标重名
# 2. 源文件路径可以是相对路径或绝对路径
# 3. 可执行文件的实际输出路径和名称可通过set_target_properties调整
# 4. 创建目标后,可使用target_*系列命令设置其属性
# 5. WIN32选项仅在Windows平台上有效
# 6. MACOSX_BUNDLE选项仅在macOS平台上有效
#
# 生成文件:
# Linux/macOS: <name> 或 <name>.app(macOS bundle)
# Windows: <name>.exe (控制台) 或 <name>.exe (GUI)
#
# 相关命令:
# add_library() # 创建库目标
# target_link_libraries() # 为可执行文件链接库
# set_target_properties() # 设置目标属性
# install() # 安装可执行文件
# =============================================================================
target_link_libraries
# =============================================================================
# 函数: target_link_libraries
#
# 描述:
# 指定目标(可执行文件或库)需要链接的库依赖关系。
# 该命令不仅处理链接操作,还会自动传递包含目录、编译定义等依赖属性。
#
# 语法:
# target_link_libraries(<target>
# [PRIVATE | PUBLIC | INTERFACE] <item>...
# [<PRIVATE | PUBLIC | INTERFACE> <item>...]...)
#
# 参数:
# <target> # 要添加依赖的目标(必须是add_executable或add_library创建的目标)
#
# 作用域说明符:
# PRIVATE # 依赖项仅用于当前目标的实现,不传递给使用者
# PUBLIC # 依赖项用于当前目标的实现和接口,会传递给使用者
# INTERFACE # 依赖项仅用于当前目标的接口,会传递给使用者但不用于实现
#
# <item> # 要链接的库,可以是:
# # - 由add_library创建的库目标
# # - 系统库名称(如pthread, dl)
# # - 完整的库文件路径
# # - 编译器标志(如 -pthread)
#
# 用法示例:
# # 创建库和可执行文件
# add_library(math math.cpp)
# add_executable(calculator main.cpp)
#
# # 链接库到可执行文件
# target_link_libraries(calculator PRIVATE math)
#
# # 使用不同作用域的链接
# add_library(A a.cpp)
# add_library(B b.cpp)
# add_executable(app main.cpp)
#
# # A需要B的实现细节,但使用者不需要知道B
# target_link_libraries(A PRIVATE B)
# target_link_libraries(app PRIVATE A) # 不会自动链接B
#
# # A需要B,且使用者也需要B
# target_link_libraries(A PUBLIC B)
# target_link_libraries(app PRIVATE A) # 会自动链接B
#
# 注意:
# 1. 现代CMake推荐始终使用作用域说明符(PRIVATE/PUBLIC/INTERFACE)
# 2. 链接库目标比使用库文件名更好,可以自动处理依赖传递
# 3. PUBLIC依赖意味着"需要这个库来编译我,且使用我的目标也需要它"
# 4. PRIVATE依赖意味着"需要这个库来编译我,但使用我的目标不需要知道它"
# 5. INTERFACE依赖意味着"我不需要这个库,但使用我的目标需要它"
#
# 依赖传递:
# 当目标A以PUBLIC或INTERFACE方式链接库B时,B的包含目录、
# 编译定义等属性会自动传递给任何链接A的目标。
# =============================================================================
使用示例
cmake_minimum_required(VERSION 3.10)
project(MyProject)
# 创建数学库
add_library(math STATIC
src/math_utils.cpp
src/vector_ops.cpp
)
# 设置数学库的包含目录
target_include_directories(math PUBLIC include)
# 创建主程序
add_executable(my_app src/main.cpp)
# 链接数学库到主程序
target_link_libraries(my_app PRIVATE math)
# 如果需要系统库
target_link_libraries(my_app PRIVATE pthread m)
变量操作命令
set
# =============================================================================
# 函数: set
#
# 描述:
# 定义或修改变量的值。这是CMake中最基本和最常用的变量管理命令,
# 用于存储字符串、列表、路径、标志等各种类型的数据。
#
# 语法:
# set(<variable> <value>... [PARENT_SCOPE])
# set(<variable> <value>... CACHE <type> <docstring> [FORCE])
# set(ENV{<variable>} <value>)
#
# 参数:
# <variable> # 变量名称,区分大小写
# <value>... # 变量的值,可以是多个值(将创建列表)
#
# 可选修饰符:
# PARENT_SCOPE # 在父作用域中设置变量
# CACHE # 将变量设置为缓存变量(跨运行持久化)
# FORCE # 强制覆盖已存在的缓存变量
# ENV{<var>} # 设置环境变量(仅当前CMake进程有效)
#
# 缓存变量类型(<type>):
# BOOL # 布尔值(ON/OFF, TRUE/FALSE)
# STRING # 字符串值
# FILEPATH # 文件路径
# PATH # 目录路径
# INTERNAL # 内部缓存变量(不显示在GUI中)
#
# 用法示例:
# # 设置普通变量
# set(MY_VARIABLE "Hello World")
# set(SOURCE_FILES main.cpp utils.cpp network.cpp)
#
# # 设置缓存变量(在GUI中可见)
# set(BUILD_TESTS ON CACHE BOOL "是否构建测试")
# set(INSTALL_PREFIX "/usr/local" CACHE PATH "安装路径")
#
# # 设置环境变量
# set(ENV{PATH} "/custom/bin:$ENV{PATH}")
#
# # 在父作用域设置变量
# set(RESULT_VALUE "success" PARENT_SCOPE)
#
# # 强制覆盖缓存变量
# set(CMAKE_BUILD_TYPE "Release" CACHE STRING "构建类型" FORCE)
#
# 注意:
# 1. 变量名区分大小写,MY_VAR和my_var是不同的变量
# 2. 多个值会自动创建分号分隔的列表
# 3. 缓存变量会持久化在CMakeCache.txt中
# 4. 环境变量的设置仅影响当前CMake进程
# 5. 使用PARENT_SCOPE可在函数中修改调用者的变量
# 6. 未定义的变量值为空字符串
#
# 相关命令:
# unset() # 删除变量
# list() # 列表操作
# option() # 快速定义布尔缓存变量
# =============================================================================
# 字符串变量
set(PROJECT_NAME "MyApplication")
set(VERSION_STRING "1.0.0")
# 列表变量
set(SOURCE_FILES
src/main.cpp
src/utils.cpp
src/network.cpp
)
# 缓存变量(配置选项)
set(BUILD_SHARED_LIBS ON CACHE BOOL "构建动态库")
set(CMAKE_INSTALL_PREFIX "/opt/myapp" CACHE PATH "安装目录")
# 在函数中返回结果
function(calculate_result)
set(result "计算完成" PARENT_SCOPE)
endfunction()
流程控制命令
if-elseif
# =============================================================================
# 命令: if-elseif-else
#
# 描述:
# 提供条件判断功能,用于根据不同的条件执行不同的CMake代码块。
# 这是CMake脚本中实现逻辑分支的核心命令。
#
# 语法:
# if(condition)
# # commands...
# elseif(condition)
# # commands...
# else()
# # commands...
# endif()
#
# 使用场景:
#
# 1. 平台检测和条件编译
# if(WIN32)
# # Windows特定设置
# set(PLATFORM_LIBS ws2_32)
# elseif(UNIX AND NOT APPLE)
# # Linux特定设置
# set(PLATFORM_LIBS pthread dl)
# elseif(APPLE)
# # macOS特定设置
# set(PLATFORM_LIBS "-framework Cocoa")
# endif()
#
# 2. 编译器特性检测
# if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# # GCC特定选项
# target_compile_options(my_app PRIVATE -Wall -Wextra)
# elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
# # MSVC特定选项
# target_compile_options(my_app PRIVATE /W4)
# endif()
#
# 3. 构建类型配置
# if(CMAKE_BUILD_TYPE STREQUAL "Debug")
# set(DEBUG_FLAGS "-g -O0")
# add_definitions(-DDEBUG)
# else()
# set(DEBUG_FLAGS "-O2")
# endif()
#
# 4. 功能开关控制
# option(ENABLE_TESTS "启用测试" ON)
# if(ENABLE_TESTS)
# enable_testing()
# add_subdirectory(tests)
# endif()
#
# 5. 变量存在性检查
# if(DEFINED MY_CUSTOM_VAR)
# message("自定义变量已设置: ${MY_CUSTOM_VAR}")
# else()
# set(MY_CUSTOM_VAR "default_value")
# endif()
#
# 6. 文件存在性检查
# if(EXISTS "${CMAKE_SOURCE_DIR}/config.h")
# configure_file(config.h.in config.h)
# else()
# message(WARNING "配置文件模板不存在")
# endif()
#
# 7. 版本检查
# if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)
# # 使用新特性
# target_link_options(my_app PRIVATE --some-flag)
# else()
# # 回退方案
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --some-flag")
# endif()
#
# 8. 依赖库检测
# if(TARGET some_library)
# target_link_libraries(my_app PRIVATE some_library)
# else()
# message(FATAL_ERROR "必需的some_library未找到")
# endif()
#
# 常用条件表达式:
# if(Variable) # 变量值为非假(true, on, yes, 1等)
# if(NOT Variable) # 变量值为假
# if(Variable1 AND Variable2) # 逻辑与
# if(Variable1 OR Variable2) # 逻辑或
# if(COMMAND command) # 检查命令是否存在
# if(EXISTS path) # 检查文件/目录是否存在
# if(IS_DIRECTORY path) # 检查是否为目录
#
# 注意:
# 1. 条件表达式中的变量不需要${}解引用
# 2. 字符串比较使用STREQUAL, VERSION相关比较使用VERSION_*
# 3. 确保每个if都有对应的endif()
# 4. 空字符串、0、OFF、FALSE、NO、N、IGNORE、NOTFOUND都被视为假
#
# 相关命令:
# option() # 定义用户可配置的选项
# while() # 循环控制
# foreach() # 遍历循环
# =============================================================================
# 平台特定的配置
if(WIN32)
# Windows设置
add_definitions(-DWIN32_LEAN_AND_MEAN)
set(SYSTEM_LIBS ws2_32)
elseif(APPLE)
# macOS设置
set(SYSTEM_LIBS "-framework CoreFoundation")
else()
# Linux设置
set(SYSTEM_LIBS pthread dl rt)
endif()
# 构建配置
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(my_app PRIVATE DEBUG=1)
target_compile_options(my_app PRIVATE -g -O0)
else()
target_compile_options(my_app PRIVATE -O2 -DNDEBUG)
endif()
# 可选功能
option(BUILD_WITH_OPENMP "使用OpenMP并行化" OFF)
if(BUILD_WITH_OPENMP)
find_package(OpenMP REQUIRED)
target_link_libraries(my_app PRIVATE OpenMP::OpenMP_CXX)
endif()
编译配置命令
target_compile_features
NOTE:依赖关系是库依赖于执行目标,因此想要利用作用域依赖关系,需要对库配置而不是执行目标
# =============================================================================
# 命令: target_compile_features
#
# 描述:
# 为目标指定所需的编译器特性。这是现代CMake中设置C++标准的推荐方式,
# 可以确保代码使用的特性在目标编译器上可用,并提供更好的跨编译器兼容性。
#
# 语法:
# target_compile_features(<target> <PRIVATE|PUBLIC|INTERFACE> <feature> [...])
#
# 参数:
# <target> # 要设置特性的目标名称
#
# 作用域说明符:
# PRIVATE # 特性仅用于当前目标的编译
# PUBLIC # 特性用于当前目标的编译,并传递给依赖项
# INTERFACE # 特性不用于当前目标的编译,但传递给依赖项
#
# <feature> # 要启用的编译器特性
#
# 常用特性:
#
# C++标准特性:
# cxx_std_98 # C++98 标准
# cxx_std_11 # C++11 标准
# cxx_std_14 # C++14 标准
# cxx_std_17 # C++17 标准
# cxx_std_20 # C++20 标准
# cxx_std_23 # C++23 标准
#
# 具体语言特性(示例):
# cxx_auto_type # auto 类型推导
# cxx_lambda_expressions # lambda 表达式
# cxx_range_for # 范围for循环
# cxx_nullptr # nullptr 关键字
# cxx_constexpr # constexpr
# cxx_decltype # decltype
# cxx_attributes # 属性语法
# cxx_thread_local # thread_local 存储期
#
# 用法示例:
# # 设置C++17标准(推荐方式)
# target_compile_features(my_app PRIVATE cxx_std_17)
#
# # 设置具体特性而非整个标准
# target_compile_features(my_lib PUBLIC
# cxx_auto_type
# cxx_lambda_expressions
# cxx_range_for
# )
#
# # 为库设置特性并传递给使用者
# add_library(my_modern_lib modern.cpp)
# target_compile_features(my_modern_lib PUBLIC cxx_std_17)
#
# add_executable(my_app app.cpp)
# target_link_libraries(my_app my_modern_lib) # 自动获得C++17支持
#
# # 同时设置标准和其他特性
# target_compile_features(my_target PRIVATE
# cxx_std_17
# cxx_constexpr_if
# cxx_fold_expressions
# )
#
# 与set_target_properties的对比:
# # 传统方式 - 设置标准
# set_target_properties(my_target PROPERTIES
# CXX_STANDARD 17
# CXX_STANDARD_REQUIRED ON
# )
#
# # 现代方式 - 设置特性
# target_compile_features(my_target PRIVATE cxx_std_17)
#
# 优势:
# 1. 更好的编译器兼容性检查
# 2. 可以精确控制需要的具体特性
# 3. 自动处理依赖传递
# 4. 提供更清晰的特性需求声明
#
# 注意:
# 1. 此命令要求CMake 3.8或更高版本
# 2. 使用cxx_std_xx设置整个标准是最常见的用法
# 3. 特性名称区分大小写
# 4. 如果编译器不支持请求的特性,CMake会报错
# 5. PUBLIC/INTERFACE特性会传递给链接此目标的其他目标
#
# 相关命令:
# target_compile_options() # 设置编译选项
# target_include_directories() # 设置包含目录
# set_target_properties() # 设置目标属性(包括CXX_STANDARD)
# cmake_minimum_required() # 设置CMake最低版本要求
# =============================================================================
# 使用示例
cmake_minimum_required(VERSION 3.15)
project(ModernProject LANGUAGES CXX)
# 创建库目标
add_library(core_lib
src/core.cpp
src/utils.cpp
)
# 为库设置C++17标准并传递给使用者
target_compile_features(core_lib PUBLIC cxx_std_17)
# 创建可执行文件
add_executable(main_app src/main.cpp)
# 链接库,自动获得C++17支持
target_link_libraries(main_app core_lib)
# 如果需要额外特性,可以单独添加
target_compile_features(main_app PRIVATE
cxx_constexpr_if
cxx_structured_bindings
)
target_include_directories
# =============================================================================
# 命令: target_include_directories
#
# 描述:
# 为目标添加包含目录(头文件搜索路径)。该命令指定编译器在哪些目录中
# 查找头文件,是现代CMake中管理头文件路径的推荐方式。
#
# 语法:
# target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
# <INTERFACE|PUBLIC|PRIVATE> [items1...]
# [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
#
# 参数:
# <target> # 要添加包含目录的目标名称
#
# 作用域说明符:
# PRIVATE # 目录仅用于编译当前目标
# PUBLIC # 目录用于编译当前目标,并传递给依赖此目标的其他目标
# INTERFACE # 目录不用于编译当前目标,但传递给依赖此目标的其他目标
#
# 可选关键字:
# SYSTEM # 标记为系统目录(编译器警告抑制)
# BEFORE|AFTER # 控制目录的添加顺序(默认AFTER)
#
# items... # 要添加的目录路径列表
#
# 用法示例:
# # 基本用法 - 添加私有包含目录
# target_include_directories(my_lib PRIVATE src)
# target_include_directories(my_lib PRIVATE include third_party/include)
#
# # 添加公共包含目录(传递给使用者)
# target_include_directories(my_lib PUBLIC include)
#
# # 添加接口包含目录(仅传递给使用者)
# target_include_directories(my_header_lib INTERFACE include)
#
# # 标记系统目录(抑制编译器警告)
# target_include_directories(my_app SYSTEM PRIVATE third_party/noisy_lib)
#
# # 混合使用不同作用域
# target_include_directories(my_lib
# PUBLIC include # 公共API头文件
# PRIVATE src # 内部实现头文件
# PRIVATE third_party/lib # 第三方依赖
# )
#
# 实际项目示例:
# # 库项目
# add_library(math_lib math.cpp)
# target_include_directories(math_lib
# PUBLIC include # 用户需要这个来使用你的库
# PRIVATE src # 仅库内部使用
# PRIVATE third_party # 内部依赖
# )
#
# # 可执行项目
# add_executable(my_app main.cpp)
# target_link_libraries(my_app math_lib) # 自动获得include目录
#
# 与旧命令的对比:
# # 旧方式(不推荐)
# include_directories(include) # 全局影响所有目标
#
# # 现代方式(推荐)
# target_include_directories(my_target PRIVATE include) # 仅影响指定目标
#
# 作用域详细说明:
# PRIVATE: 当前目标编译时需要,但不传递给依赖者
# 适用于内部头文件、实现细节
#
# PUBLIC: 当前目标编译时需要,且依赖者也需要
# 适用于库的公共API头文件
#
# INTERFACE: 当前目标编译时不需要,但依赖者需要
# 适用于纯头文件库
#
# 注意:
# 1. 此命令要求CMake 3.0或更高版本
# 2. 路径可以是绝对路径或相对路径(相对于当前源目录)
# 3. 使用PUBLIC/INTERFACE可以自动处理依赖传递
# 4. SYSTEM选项可抑制第三方库的编译器警告
# 5. 相对于include_directories(),此命令更精确且可避免污染全局空间
# 6. 建议使用生成器表达式来支持不同的配置(如Debug/Release)
#
# 相关命令:
# target_link_libraries() # 链接库和传递依赖
# target_compile_definitions() # 设置编译定义
# target_compile_options() # 设置编译选项
# include_directories() # 旧式全局包含目录(不推荐)
# =============================================================================
# 使用示例
cmake_minimum_required(VERSION 3.10)
project(MyProject)
# 创建库
add_library(core
src/core.cpp
src/utils.cpp
)
# 设置库的包含目录
target_include_directories(core
PUBLIC
include # 公共API头文件 - 使用者需要
PRIVATE
src # 内部头文件 - 仅库自己需要
third_party # 第三方依赖 - 仅库自己需要
)
# 创建可执行文件
add_executable(app main.cpp)
# 链接库,自动获得 PUBLIC 包含目录
target_link_libraries(app core)
# 可执行文件自己的私有包含目录
target_include_directories(app PRIVATE app_include)
target_compile_definitions
# =============================================================================
# 命令: target_compile_definitions
#
# 描述:
# 为目标添加编译定义(预处理器宏)。该命令用于设置-D编译选项,
# 允许在代码中使用#ifdef、#ifndef等条件编译指令。
#
# 语法:
# target_compile_definitions(<target>
# <INTERFACE|PUBLIC|PRIVATE> [items1...]
# [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
#
# 参数:
# <target> # 要添加编译定义的目标名称
#
# 作用域说明符:
# PRIVATE # 定义仅用于编译当前目标
# PUBLIC # 定义用于编译当前目标,并传递给依赖此目标的其他目标
# INTERFACE # 定义不用于编译当前目标,但传递给依赖此目标的其他目标
#
# items... # 要添加的编译定义列表,支持多种格式
#
# 编译定义格式:
# NAME # 简单的定义 -DNAME
# NAME=VALUE # 带值的定义 -DNAME=VALUE
# NAME=VALUE1,VALUE2 # 逗号分隔的值(需要引号)
#
# 用法示例:
# # 添加简单的编译定义
# target_compile_definitions(my_lib PRIVATE DEBUG)
# target_compile_definitions(my_lib PRIVATE USE_OPENMP)
#
# # 添加带值的编译定义
# target_compile_definitions(my_lib PRIVATE VERSION=100)
# target_compile_definitions(my_lib PRIVATE MAX_BUFFER_SIZE=4096)
#
# # 添加复杂值的定义(需要引号)
# target_compile_definitions(my_lib PRIVATE "FEATURES=ENABLE_A,ENABLE_B")
#
# # 使用不同作用域
# target_compile_definitions(my_lib
# PUBLIC API_VERSION=2 # 使用者需要知道API版本
# PRIVATE INTERNAL_DEBUG=1 # 仅内部调试使用
# INTERFACE USE_DEPRECATED=OFF # 告诉使用者不要使用废弃API
# )
#
# # 条件编译定义
# if(WIN32)
# target_compile_definitions(my_lib PRIVATE PLATFORM_WINDOWS)
# elseif(UNIX)
# target_compile_definitions(my_lib PRIVATE PLATFORM_UNIX)
# endif()
#
# 实际项目示例:
# # 库项目设置
# add_library(network_lib network.cpp)
# target_compile_definitions(network_lib
# PUBLIC NETWORK_API_VERSION=2
# PRIVATE ENABLE_LOGGING
# PRIVATE BUFFER_SIZE=8192
# )
#
# # 可执行项目
# add_executable(my_app main.cpp)
# target_link_libraries(my_app network_lib)
# # 自动获得 PUBLIC 定义 NETWORK_API_VERSION=2
#
# 与 add_definitions() 的对比:
# # 旧方式(不推荐) - 全局影响
# add_definitions(-DDEBUG -DVERSION=100)
#
# # 现代方式(推荐) - 目标特定
# target_compile_definitions(my_target PRIVATE DEBUG VERSION=100)
#
# 作用域详细说明:
# PRIVATE: 仅当前目标使用,适用于内部实现细节
# 示例:调试标志、内部配置
#
# PUBLIC: 当前目标和使用者都需要,适用于接口定义
# 示例:API版本、特性开关
#
# INTERFACE: 仅使用者需要,当前目标不需要
# 示例:兼容性要求、使用约束
#
# 生成器表达式支持:
# # 根据配置设置不同的定义
# target_compile_definitions(my_lib
# PRIVATE
# $<$<CONFIG:Debug>:DEBUG_MODE>
# $<$<CONFIG:Release>:NDEBUG>
# "BUILD_TYPE=$<CONFIG>"
# )
#
# 注意:
# 1. 此命令要求CMake 3.0或更高版本
# 2. 定义名称通常使用大写字母,这是C/C++的惯例
# 3. 值中的空格和特殊字符需要适当转义或使用引号
# 4. PUBLIC/INTERFACE定义会传递给链接此目标的其他目标
# 5. 相对于add_definitions(),此命令更精确且可避免污染全局空间
# 6. 可以使用$<BUILD_INTERFACE:...>和$<INSTALL_INTERFACE:...>控制可见性
#
# 相关命令:
# target_compile_options() # 设置编译选项
# target_include_directories() # 设置包含目录
# add_definitions() # 旧式全局编译定义(不推荐)
# configure_file() # 通过模板生成头文件
# =============================================================================
cmake_minimum_required(VERSION 3.10)
project(MyProject)
# 配置选项
option(ENABLE_LOGGING "启用日志" ON)
option(ENABLE_DEBUG "启用调试" OFF)
add_library(core_lib core.cpp)
# 根据选项设置编译定义
if(ENABLE_LOGGING)
target_compile_definitions(core_lib PRIVATE ENABLE_LOGGING)
endif()
if(ENABLE_DEBUG)
target_compile_definitions(core_lib PRIVATE DEBUG_LEVEL=3)
else()
target_compile_definitions(core_lib PRIVATE NDEBUG)
endif()
# 平台特定定义
if(WIN32)
target_compile_definitions(core_lib PRIVATE OS_WINDOWS _CRT_SECURE_NO_WARNINGS)
elseif(APPLE)
target_compile_definitions(core_lib PRIVATE OS_MACOS)
else()
target_compile_definitions(core_lib PRIVATE OS_LINUX)
endif()
# 公共API定义
target_compile_definitions(core_lib
PUBLIC
API_VERSION=2
PROJECT_NAME="MyProject"
)
add_executable(my_app main.cpp)
target_link_libraries(my_app core_lib)
# 自动获得 PUBLIC 定义:API_VERSION=2, PROJECT_NAME="MyProject"
属性设置命令
set_target_properties
# =============================================================================
# 函数: set_target_properties
#
# 描述:
# 设置一个或多个目标的属性。该命令用于配置目标的各种行为特性,
# 包括输出名称、版本号、编译选项、平台特定设置等。
#
# 语法:
# set_target_properties(target1 [target2 ...]
# PROPERTIES
# property1 value1
# property2 value2
# ...)
#
# 参数:
# target1 [target2 ...] # 一个或多个目标名称
#
# PROPERTIES # 关键字,后面跟随属性键值对
#
# 常用属性:
# OUTPUT_NAME # 设置输出文件的基本名称(不包含扩展名和前缀)
# VERSION # 设置目标版本号
# SOVERSION # 设置动态库的API版本号
# CXX_STANDARD # 设置C++标准(11,14,17,20等)
# CXX_STANDARD_REQUIRED # 是否强制要求指定C++标准(ON/OFF)
# COMPILE_FLAGS # 设置目标的编译标志
# LINK_FLAGS # 设置目标的链接标志
# POSITION_INDEPENDENT_CODE # 是否生成位置无关代码(ON/OFF)
# DEBUG_POSTFIX # Debug配置下输出文件的后缀
# PREFIX # 输出文件的前缀(如设置库前缀为"lib")
# SUFFIX # 输出文件的后缀
# RUNTIME_OUTPUT_DIRECTORY # 输出目录
#
# 用法示例:
# # 设置单个目标的属性
# set_target_properties(math
# PROPERTIES
# OUTPUT_NAME "math_utils"
# VERSION 1.2.3
# SOVERSION 1)
#
# # 设置多个目标的属性
# set_target_properties(math calculator
# PROPERTIES
# CXX_STANDARD 17
# CXX_STANDARD_REQUIRED ON)
#
# # 设置动态库版本
# set_target_properties(mylib
# PROPERTIES
# VERSION 2.1.0
# SOVERSION 2
# OUTPUT_NAME "mylib")
#
# # 设置Debug版本后缀
# set_target_properties(myapp
# PROPERTIES
# DEBUG_POSTFIX "_d")
#
# 注意:
# 1. 此命令必须在目标创建(add_executable/add_library)之后使用
# 2. 可以同时设置多个目标的多个属性
# 3. 某些属性是平台相关的,在不同系统上可能有不同效果
# 4. 对于库版本控制,VERSION是完整版本号,SOVERSION是API兼容版本号
# 5. 现代CMake推荐使用target_*系列命令,但某些属性只能通过此命令设置
#
# 替代命令:
# 对于编译特性,优先考虑:
# target_compile_features() # 设置编译特性
# target_compile_options() # 设置编译选项
# target_include_directories() # 设置包含目录
# =============================================================================
# 创建库
add_library(mylib SHARED mylib.cpp)
# 设置库属性
set_target_properties(mylib
PROPERTIES
OUTPUT_NAME "mymath" # 输出文件: libmymath.so (Linux)
VERSION 1.5.0 # 完整版本号
SOVERSION 1 # API版本号
DEBUG_POSTFIX "_d" # Debug版本: libmymath_d.so
CXX_STANDARD 17 # C++17标准
CXX_STANDARD_REQUIRED ON # 强制要求C++17
POSITION_INDEPENDENT_CODE ON # 生成位置无关代码
)
set_source_files_properties
# =============================================================================
# 函数: set_source_files_properties
#
# 描述:
# 设置源文件的属性。该命令允许为单个或多个源文件指定特殊的编译设置,
# 可以覆盖目标的全局编译设置,实现文件级别的精细控制。
#
# 语法:
# set_source_files_properties(<file1> [<file2> ...]
# PROPERTIES
# <property1> <value1>
# <property2> <value2>
# ...)
#
# 参数:
# <file1> [<file2> ...] # 一个或多个源文件
#
# PROPERTIES # 关键字,后面跟随属性键值对
#
# 常用属性:
# COMPILE_FLAGS # 文件特定的编译选项
# COMPILE_DEFINITIONS # 文件特定的编译定义
# LANGUAGE # 覆盖文件的编程语言(C/CXX等)
# GENERATED # 标记文件为生成文件(非源码)
# OBJECT_DEPENDS # 文件依赖的其他文件
# COMPILE_OPTIONS # 现代CMake推荐,替代COMPILE_FLAGS
#
# 用法示例:
# # 为特定文件设置编译选项
# set_source_files_properties(legacy_code.cpp PROPERTIES
# COMPILE_FLAGS "-Wno-deprecated -O0"
# )
#
# # 为文件设置特定的编译定义
# set_source_files_properties(win32_specific.cpp PROPERTIES
# COMPILE_DEFINITIONS "WIN32_LEAN_AND_MEAN"
# )
#
# # 清除文件的编译标志(如你的例子)
# set_source_files_properties(third_party/sqlite3/sqlite3.c PROPERTIES
# COMPILE_FLAGS ""
# )
#
# # 覆盖文件的编程语言
# set_source_files_properties(assembly_file.s PROPERTIES
# LANGUAGE C
# )
#
# # 标记生成的文件
# set_source_files_properties(generated_code.cpp PROPERTIES
# GENERATED TRUE
# )
#
# # 设置文件依赖
# set_source_files_properties(main.cpp PROPERTIES
# OBJECT_DEPENDS "header.h template.h"
# )
#
# # 现代CMake方式设置编译选项
# set_source_files_properties(special.cpp PROPERTIES
# COMPILE_OPTIONS "-fopenmp"
# )
#
# 实际应用场景:
# # 第三方库需要特殊处理
# set_source_files_properties(third_party/old_lib.c PROPERTIES
# COMPILE_FLAGS "-w" # 禁用所有警告
# )
#
# # 特定平台文件
# if(WIN32)
# set_source_files_properties(win_impl.cpp PROPERTIES
# COMPILE_DEFINITIONS "WINDOWS_BUILD"
# )
# endif()
#
# # 调试版本特殊处理
# set_source_files_properties(debug_utils.cpp PROPERTIES
# COMPILE_FLAGS "$<$<CONFIG:Debug>:-DDEBUG_VERBOSE>"
# )
#
# 注意:
# 1. 文件路径可以是相对路径或绝对路径
# 2. COMPILE_FLAGS 是旧式属性,现代CMake推荐使用 COMPILE_OPTIONS
# 3. 属性设置会覆盖目标的全局设置
# 4. 可以使用生成器表达式实现条件设置
# 5. 对于大量文件,考虑使用 target_compile_options 配合 $<COMPILE_LANGUAGE>
# 6. 设置 GENERATED 属性可避免CMake检查文件是否存在
#
# 相关命令:
# set_target_properties() # 设置目标属性
# target_compile_options() # 设置目标编译选项
# target_compile_definitions() # 设置目标编译定义
# source_group() # 在IDE中分组源文件
# =============================================================================
# 为 SQLite3 源码清除编译标志,避免警告
set_source_files_properties(third_party/sqlite3/sqlite3.c PROPERTIES
COMPILE_FLAGS ""
COMPILE_OPTIONS "" # 现代CMake方式
)
# 或者为 SQLite3 设置特定选项
set_source_files_properties(third_party/sqlite3/sqlite3.c PROPERTIES
COMPILE_OPTIONS "-DSQLITE_DISABLE_INTRINSIC"
)

浙公网安备 33010602011771号