找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1376

积分

0

好友

233

主题
发表于 2025-12-26 10:37:28 | 查看: 36| 回复: 0

CMake是一款强大的跨平台构建工具,通过编写一份统一的 CMakeLists.txt 配置文件,即可为不同平台(如 Windows、Linux、macOS)生成相应的构建系统(如 Visual Studio 的 .sln 文件或 Makefile),随后再调用平台原生的编译器进行编译。它极大地简化了 C/C++ 项目的构建管理流程,是许多大型开源项目的首选。

理解构建系统与生成器

构建系统指的是平台原生的项目工程文件,例如:

  • Windows:Visual Studio 的 .sln.vcxproj 文件
  • Linux/Unix:Makefile 文件

CMake 的作用就是根据 CMakeLists.txt 生成这些构建系统文件,它支持多种生成器(Generator)。可以通过以下命令查看 CMake 支持生成哪些构建系统:

cmake -G

CMake 构建流程详解

标准的构建流程分为两步:

  1. 生成构建系统:通过解析 CMakeLists.txt 文件,检查环境(如编译器),并生成对应的项目文件。

    • 命令:cmake [选项] <CMakeLists.txt所在路径>
    • 工具:可使用 cmake 命令行或 cmake-gui 图形界面。
  2. 编译与安装:使用生成的原生构建工具(如 msbuildnmakemake)进行编译和安装。

常用构建选项与命令

  • -G:指定生成器(Generator)。
  • -D:定义或覆盖 CMake 变量。
  • -S-B:分别指定源代码目录和构建目录(现代 CMake 推荐用法)。

一个典型的跨平台构建步骤如下:

# 1. 在项目根目录创建并进入构建目录
mkdir build && cd build

# 2. 生成构建系统(根据平台选择生成器)
# 对于 Visual Studio 2015
cmake -G "Visual Studio 14 2015" ..
# 对于 Windows NMake
cmake -G "NMake Makefiles" ..
# 对于 Linux/Unix
cmake -G "Unix Makefiles" ..

# 3. 编译项目
cmake --build .
# 或者使用原生工具:make / msbuild / ninja 等

# 4. 安装(如果需要)
cmake --install .

更简洁的现代命令方式:

# 在项目根目录执行,-S 指定源码目录,-B 指定构建目录
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake --install build

CMake 核心概念与语法

关键文件类型

  1. 目录文件 (CMakeLists.txt):每个目录的构建入口。顶层 CMakeLists.txt 通过 add_subdirectory() 引入子目录。
  2. 脚本文件 (.cmake):通常包含可复用的宏(macro)或函数(function)。可通过 cmake -P script.cmake 直接执行测试,或通过 include() 命令在 CMakeLists.txt 中加载。
  3. 模块文件 (Find<PackageName>.cmake):由 find_package() 命令调用,用于查找第三方依赖库(如 OpenCV),并设置相关变量(如 OpenCV_INCLUDE_DIRS, OpenCV_LIBRARIES)。

变量系统

CMake 中所有变量本质都是字符串,作用域分为:

  • 函数作用域:在 function()...endfunction() 内定义的变量。
  • 目录作用域:在 function 外定义的变量,对当前目录及其子目录可见。
  • 缓存变量:使用 set(... CACHE ...) 设置,值在多次构建间持久化。

变量引用格式为 ${变量名},查找顺序为:函数作用域 -> 目录作用域 -> 缓存。

常用内置变量

变量名 含义与用途
PROJECT_SOURCE_DIR 工程源代码根目录,即顶层 CMakeLists.txt 所在路径。
PROJECT_BINARY_DIR 工程构建根目录,即执行 cmake 命令的路径(如 ./build)。
CMAKE_CURRENT_SOURCE_DIR 当前处理的 CMakeLists.txt 所在目录
CMAKE_CURRENT_BINARY_DIR 对应于当前源目录的构建目录。
CMAKE_RUNTIME_OUTPUT_DIRECTORY 指定可执行文件(.exe)的输出目录。
CMAKE_LIBRARY_OUTPUT_DIRECTORY 指定库文件(.dll/.so/.lib/.a)的输出目录。

平台检测与条件控制

CMake 提供了平台检测变量,便于编写跨平台脚本:

if(WIN32)
    message(STATUS "Building on Windows")
elseif(UNIX AND NOT APPLE)
    message(STATUS "Building on Linux/Unix")
elseif(APPLE)
    message(STATUS "Building on macOS")
endif()

条件判断支持丰富的关键字,如 ANDORNOTEXISTS(检查文件)、DEFINED(检查变量)、MATCHES(正则匹配)以及字符串、数字、版本号比较等。

循环、函数与宏

CMake 支持 while() 和多种 foreach() 循环,以及 break()continue() 控制。

宏 (macro)函数 (function) 用于封装可重用指令集,定义后可以像内置命令一样调用。两者主要区别在于作用域:函数内部有独立的作用域,而宏内部的操作直接影响到调用者的作用域。

定义构建目标

创建可执行文件与库

# 生成可执行文件
add_executable(MyApp main.cpp utils.cpp)

# 生成静态库
add_library(MyLib STATIC lib.cpp)
# 生成动态库/共享库
add_library(MyLib SHARED lib.cpp)

管理源文件列表

可以通过多种方式指定源文件:

  1. 直接在 add_executable/add_library 中列出。
  2. 使用 set() 命令将文件列表存入变量。
  3. 使用 file(GLOB ...) 命令通配匹配文件(需谨慎使用,可能导致新文件未被自动识别)。
# 示例:使用变量和 file(GLOB) 管理文件
set(APP_HEADERS include/utils.h)
set(APP_SOURCES src/main.cpp src/utils.cpp)
file(GLOB APP_UIS "ui/*.ui")

add_executable(MyApp ${APP_HEADERS} ${APP_SOURCES} ${APP_UIS})

file(GLOB_RECURSE) 可以递归匹配子目录中的文件。在复杂的运维与 DevOps 流水线中,清晰的文件管理是保证构建可靠性的基础。

CMake核心指南:从跨平台构建到打包发布的C++项目实战 - 图片 - 1

(图示:CMake 项目中的典型文件结构示意)

目标属性设置(现代 CMake 风格)

现代 CMake 强调将属性(如头文件路径、编译选项、链接库)关联到具体的目标上。

# 为目标指定头文件搜索目录(PRIVATE表示仅本目标使用)
target_include_directories(MyApp PRIVATE include)

# 为目标添加编译选项
target_compile_options(MyApp PRIVATE -Wall -Wextra)

# 为目标链接其他库
target_link_libraries(MyApp PRIVATE MyLib)

# 为目标添加预处理宏定义
target_compile_definitions(MyApp PRIVATE VERSION=\"1.0\")

安装与打包

安装规则

使用 install() 命令定义安装规则,DESTINATION 路径通常相对于 CMAKE_INSTALL_PREFIX

# 安装可执行文件到 bin 目录
install(TARGETS MyApp DESTINATION bin)
# 安装库文件到 lib 目录
install(TARGETS MyLib DESTINATION lib)
# 安装头文件到 include 目录
install(FILES include/utils.h DESTINATION include)

使用 CPack 打包

CPack 是 CMake 内置的打包工具,可以生成多种格式的安装包(如 .tar.gz.zip.sh.deb.rpm)。

# 在顶层 CMakeLists.txt 中设置 CPack 变量并引入模块
set(CPACK_GENERATOR "TGZ") # 设置生成器为 .tar.gz
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VERSION "1.0.0")
include(CPack)

打包命令:

# 在构建目录中
cmake --build . --target package      # 生成二进制包
cmake --build . --target package_source # 生成源码包
# 或直接使用 cpack 命令
cpack -G TGZ

对于 Linux 平台,STGZ(自解压 .sh 安装包)格式很常用。其安装脚本可以通过 CPACK_STGZ_HEADER_FILE 变量进行自定义,实现更复杂的安装逻辑,这在自动化部署脚本中尤为有用,可以视为一种特定的 Shell 脚本实践

常用命令速览

字符串操作

# 查找与替换
string(FIND "hello world" "world" pos) # pos = 6
string(REPLACE "world" "cmake" result "hello world") # result = "hello cmake"

# 拼接
string(APPEND str "Hello, ") # str = "Hello, "
string(JOIN ";" list_str a b c) # list_str = "a;b;c"

# 大小写与裁剪
string(TOLOWER "CMake" lower_str) # lower_str = "cmake"
string(STRIP "  text  " stripped) # stripped = "text"

消息打印

message() 命令用于输出信息,支持不同级别:

message(STATUS "这是状态信息")   # 最常见,用于提示进度
message(WARNING "这是一个警告")
message(SEND_ERROR "这是一个错误,但会继续")
message(FATAL_ERROR "致命错误,将停止构建")

文件与目录操作

# 复制文件或目录
file(COPY data/ DESTINATION ${CMAKE_BINARY_DIR}/bin)

# 创建目录
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated)

掌握 CMake 的这些核心概念和命令,尤其是变量作用域、条件判断和现代的目标属性设置方法,是编写高效、可维护的构建脚本的关键。这与编写高质量的 Shell 脚本有着异曲同工之妙,都需要对执行环境和逻辑流有清晰的控制。




上一篇:Windows PE文件逆向解析:详解导入表与IAT表的工作原理与遍历代码
下一篇:C++ list双向链表解析:标准库使用与模拟实现详解
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-12 02:46 , Processed in 0.283727 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表