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>
#                         [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"
)
posted @ 2025-11-08 14:47  码农要战斗  阅读(4)  评论(0)    收藏  举报