文章

Cmake相关

Cmake相关

[TOC]

CMake简介

1. 什么是 CMake?

CMake 是一款跨平台的 构建系统生成工具(Build System Generator),而非直接的构建工具(如 Make、Ninja、Visual Studio 编译器)。它的核心作用是:通过读取开发者编写的 CMakeLists.txt 配置文件,根据不同的操作系统(Windows/macOS/Linux)、编译器(GCC/Clang/MSVC)和构建工具链,自动生成适配当前环境的构建文件(如 Makefile、Visual Studio 的 .sln 解决方案、Ninja 脚本等),最终由这些原生构建工具完成代码的编译、链接等流程。

简单来说,CMake 解决了“跨平台项目构建适配”的痛点——开发者只需写一套 CMake 配置,即可在不同平台上生成符合当地习惯的构建产物,无需手动维护多套 Makefile 或 IDE 工程。

2. CMake 的核心文件结构

CMake 项目的文件结构以 CMakeLists.txt 为核心,配合可选的辅助文件,整体结构清晰且可扩展。

以一个 centos 中的 TK 服务为例介绍基本文件结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
TKTestProj/                 # 一个 TKLinux 项目
├── .vscode/                # vscode 读取的配置文件(具体介绍见本文后面相关章节)
│   ├── c_cpp_properties.json    # C/C++ 语言配置文件
│   ├── launch.json              # 调试配置文件
│   ├── tasks.json               # 任务配置文件
│   └── settings.json            # 项目级别的 VS Code 配置(覆盖全局设置)
├── build/                  # 构建目录(推荐“_out-of-source”构建,手动创建)
├── bin/                    # TK 指定的存放编译结果的文件夹(在CMakeLists.txt中指定)
├── .gitignore              # git 管理文件
├── CMakeLists.txt          # 根级配置文件(必需,项目入口)
├── TKTestProj.cpp          # 项目内的 .cpp 文件
├── TKTestProj.h            # 项目内的 .h 文件
└── CMakePresets.json       # 预设配置文件(可选,简化构建参数)

关键文件说明

  1. CMakeLists.txt(核心)

    项目的“构建配置脚本”,是 CMake 唯一必需的文件,遵循 CMake 语法规则。根目录的 CMakeLists.txt 负责全局配置(如项目名称、最低 CMake 版本、依赖库等),子目录的 CMakeLists.txt 负责局部配置(如编译当前目录的源文件为库/可执行程序)。

  2. build/ 目录(推荐)

    用于存放 CMake 生成的构建文件(如 Makefile、.sln)、编译中间产物(.o/.obj 文件)和最终可执行程序。这种“源码与构建产物分离”的方式称为 out-of-source build,可避免污染源码目录,且支持多套构建配置(如 debug/release 各一个 build 目录)。

  3. CMakePresets.json(可选)

    CMake 3.19+ 支持的“预设文件”,用于预定义构建参数(如编译器路径、构建类型、安装目录等),开发者只需通过 cmake --preset <预设名> 即可快速启动构建,无需每次手动输入复杂参数。

3. CMake 的核心功能

CMake 的功能围绕“跨平台构建管理”展开,核心能力可归纳为以下几类:

1)项目基础配置

  • 定义项目信息:通过 project() 指令指定项目名称、支持的语言(C/C++/Python 等)、版本号。

    示例:project(MyProject VERSION 1.0 LANGUAGES CXX)(C++ 项目,版本 1.0)。

  • 指定最低 CMake 版本:通过 cmake_minimum_required(VERSION 3.15) 确保兼容所需的 CMake 特性。

  • 设置构建类型:通过 set(CMAKE_BUILD_TYPE Debug)set(CMAKE_BUILD_TYPE Release) 区分调试/发布版本(Debug 含调试符号,Release 开启优化)。

2)编译目标管理

CMake 通过“目标(Target)”管理编译产物,核心目标类型包括:

  • 可执行程序(Executable):通过 add_executable(<目标名> 源文件1 源文件2 ...) 生成,如 add_executable(myapp src/main.cpp)
  • 库文件(Library):通过 add_library(<目标名> [STATIC/SHARED] 源文件...) 生成,分为:
    • STATIC:静态库(如 Windows 的 .lib、Linux 的 .a),编译时直接嵌入可执行程序。
    • SHARED:动态库(如 Windows 的 .dll、Linux 的 .so、macOS 的 .dylib),运行时加载。

示例:将 src/math/add.cpp 编译为静态库 math_lib

add_library(math_lib STATIC src/math/add.cpp)

3)依赖与路径管理

  • 头文件路径:通过 target_include_directories(<目标名> PUBLIC/PRIVATE <路径>) 指定编译时的头文件搜索路径:
    • PUBLIC:当前目标和依赖它的目标都能使用该路径。

    • PRIVATE:仅当前目标可用。

      示例:target_include_directories(myapp PUBLIC include)(让 myapp 能引用 include/ 下的头文件)。

  • 链接库:通过 target_link_libraries(<目标名> <依赖库1> <依赖库2>...) 链接静态/动态库。

    示例:target_link_libraries(myapp math_lib)(让 myapp 链接 math_lib 静态库)。

  • 外部依赖查找:通过 find_package(<库名>) 自动查找系统中已安装的第三方库(如 Boost、Qt、OpenCV),并获取其头文件路径和库文件路径。

    示例:find_package(OpenCV REQUIRED)(查找 OpenCV,找不到则报错)。

4)跨平台适配

  • 条件编译:通过 if(<条件>) ... else() ... endif() 指令适配不同平台/编译器。

    示例:区分 Windows 和 Linux 的头文件:

    1
    2
    3
    4
    5
    
    if(WIN32)
        target_include_directories(myapp PRIVATE include/win)
    elseif(UNIX)
        target_include_directories(myapp PRIVATE include/unix)
    endif()
    
  • 平台特定宏/编译选项:通过 target_compile_definitions() 添加宏定义,target_compile_options() 添加编译器参数(如 GCC 的 -Wall、MSVC 的 /W4)。

5)安装与打包

  • 安装配置:通过 install() 指令定义编译产物的安装路径(如可执行程序安装到 /usr/bin,库文件安装到 /usr/lib)。

    示例:install(TARGETS myapp DESTINATION bin)(将 myapp 安装到系统的 bin 目录)。

  • 打包:通过 CPack 模块(CMake 内置)生成跨平台安装包(如 Windows 的 .exe 安装器、Linux 的 .deb/.rpm、macOS 的 .dmg),只需在根 CMakeLists.txt 中添加 include(CPack) 即可启用。

6)测试集成

通过 CTest 模块(CMake 内置)集成测试框架(如 Google Test),支持自动发现测试用例、批量执行测试并生成报告。

示例:

1
2
3
4
include(CTest)
add_executable(test_math test/test_add.cpp)
target_link_libraries(test_math math_lib gtest gtest_main)
add_test(NAME TestAdd COMMAND test_math)  # 注册测试用例

CMake使用场景

1. 简单构建

CMake 的典型工作流分为 3 步,以 Linux 下生成 Makefile 为例:

  1. 创建并进入 build 目录mkdir build && cd build(out-of-source 构建)。

  2. 生成构建文件cmake .... 表示根目录的 CMakeLists.txt,CMake 会自动生成 Makefile)。

    若需要指定编译版本的话,在构建时指定参数:cmake -DCMAKE_BUILD_TYPE=Release ..

  3. 执行构建make(通过 Makefile 编译代码,生成可执行程序或库)。

若需安装产物:make install;若需执行测试:make test

2. 通过 vscode 打开 CMake 的项目

当使用 VS Code 通过 SSH 连接 Linux 进行开发时,项目根目录下自动生成的 .vscode 文件夹是 VS Code 用于存储 项目特定配置 的目录,其中的 launch.jsontasks.jsonc_cpp_properties.json 等文件分别对应调试、构建、语言支持等核心功能的配置。这些文件确保 VS Code 能理解项目的构建规则、调试方式和语言特性,从而提供匹配的开发体验。

TKLinux 的项目可以参考 pushlogic 中的文件,一般来说把这里的 .vscode 整个拷贝到其他项目就可以了。

或者将 CMakeLists.txt 文件提供给AI,让AI帮忙生成对应的配置文件即可(主要是c_cpp_properties.json)

1).vscode 目录下核心文件的作用与结构

launch.json:调试配置文件:

作用:定义调试器的启动参数,告诉 VS Code 如何启动程序、设置断点、传递命令行参数等。

适用场景:C/C++、Python、Go 等几乎所有支持调试的语言。

典型结构(以 C++ 为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Program",  // 调试配置名称(下拉菜单可选)
      "type": "cppdbg",          // 调试器类型(cppdbg 对应 C++)
      "request": "launch",       // 启动方式(launch 启动程序,attach 附加到已运行进程)
      "program": "${workspaceFolder}/build/myapp",  // 待调试程序路径
      "args": [],                // 传递给程序的命令行参数
      "stopAtEntry": false,      // 是否在入口处(main 函数)自动暂停
      "cwd": "${workspaceFolder}",  // 程序运行的工作目录
      "environment": [],         // 环境变量(如 PATH、LD_LIBRARY_PATH)
      "externalConsole": false,  // 是否使用外部控制台
      "MIMode": "gdb",           // 调试器后端(Linux 常用 gdb,Windows 常用 lldb)
      "setupCommands": [
        {
          "description": "Enable pretty-printing for gdb",
          "text": "-enable-pretty-printing",
          "ignoreFailures": true
        }
      ],
      "preLaunchTask": "build",  // 调试前执行的任务(如关联 tasks.json 中的构建任务)
      "miDebuggerPath": "/usr/bin/gdb",  // 调试器可执行文件路径
      "setupCommands": []
    }
  ]
}
tasks.json:任务配置文件

作用:定义可自动化执行的任务(如编译、构建、格式化代码等),可通过快捷键或调试前自动触发。

适用场景:配合 CMake 构建、Make 编译、脚本执行等。

典型结构(以 CMake 构建为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build",  // 任务名称(供 launch.json 或命令面板调用)
      "type": "shell",   // 任务类型(shell 执行命令行,process 直接运行程序)
      "command": "cmake --build build",  // 构建命令(此处为 CMake 构建)
      "args": [],        // 命令参数(如 --config Release 指定构建类型)
      "group": {
        "kind": "build",
        "isDefault": true  // 是否为默认构建任务
      },
      "problemMatcher": "$gcc",  // 错误匹配规则(解析 gcc 等编译器的报错输出)
      "detail": "Generated task for CMake build"
    }
  ]
}
c_cpp_properties.json:C/C++ 语言配置文件

作用:为 C/C++ 扩展提供代码分析所需的配置(如头文件路径、编译器标准、宏定义等),确保语法高亮、智能提示、错误检查正常工作。

适用场景:仅针对 C/C++ 项目(由 VS Code 的 C/C++ 扩展自动生成)。

典型结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "configurations": [
    {
      "name": "Linux",  // 配置名称(对应不同平台)
      "includePath": [
        "${workspaceFolder}/**",  // 递归包含项目内所有头文件
        "/usr/include",           // 系统头文件路径
        "/usr/local/include"      // 第三方库头文件路径
      ],
      "defines": [],               // 全局宏定义(如 DEBUG=1
      "compilerPath": "/usr/bin/gcc",  // 编译器路径(用于解析标准库)
      "cStandard": "c17",         // C 标准版本
      "cppStandard": "c++17",     // C++ 标准版本
      "intelliSenseMode": "linux-gcc-x64"  // 智能提示适配的编译器架构
    }
  ],
  "version": 4
}
其他可能的文件
  • settings.json:项目级别的 VS Code 配置(覆盖全局设置),如缩进空格数、文件编码等。
  • extensions.json:推荐扩展列表(提示开发者安装项目所需的扩展,如 C/C++、CMake Tools 等)。

2)其他 IDE 的类似配置文件结构

不同 IDE 也会通过特定目录或文件存储项目配置,但形式和命名略有差异,核心功能与 VS Code 的 .vscode 目录类似:

Visual Studio(Windows 桌面版)
  • 核心配置目录.vs/(隐藏目录,存储解决方案缓存、调试配置等,不建议手动修改)。
  • 关键文件
    • .sln:解决方案文件(类似项目入口,管理多个工程)。
    • .vcxproj:工程文件(对应单个可执行程序或库,包含编译选项、依赖等)。
    • .vcxproj.filters:文件组织结构(定义 IDE 中显示的文件夹结构)。
  • 特点:配置与 IDE 深度绑定,可视化界面修改为主,较少手动编辑文件。
CLion(JetBrains 系列,跨平台)
  • 核心配置目录.idea/(隐藏目录,存储项目所有配置)。
  • 关键文件
    • CMakeLists.txt:CLion 原生支持 CMake,直接使用 CMake 配置构建(无需额外构建文件)。
    • .idea/workspace.xml:工作区配置(窗口布局、打开的文件等)。
    • .idea/runConfigurations/:调试/运行配置(类似 launch.json)。
    • .idea/compiler.xml:编译选项配置(类似 tasks.json)。
  • 特点:配置文件自动生成,支持通过图形界面修改后同步到文件。
Xcode(macOS 平台)
  • 核心配置目录.xcodeproj/(项目包,本质是目录)。
  • 关键文件
    • project.pbxproj:项目核心配置(存储目标、编译选项、依赖等,二进制格式,不建议手动编辑)。
    • .xcworkspace/:工作区文件(管理多个项目的依赖关系)。
  • 特点:配置文件为二进制格式,完全通过 Xcode 图形界面操作,几乎不手动编辑。
Eclipse(跨平台,多语言支持)
  • 核心配置目录.settings/(存储项目特定设置)、.project.cproject(项目元数据)。
  • 关键文件
    • .project:项目基本信息(名称、关联的插件等)。
    • .cproject:C/C++ 项目的编译配置(类似 c_cpp_properties.json)。
    • .settings/org.eclipse.debug.core.launchConfiguration:调试配置(类似 launch.json)。
  • 特点:配置文件多为 XML 格式,可手动编辑但更推荐图形界面操作。

3)修改 .vscode 后生效

  1. 保存 c_cpp_properties.json 到项目的 .vscode 目录下。
  2. 重新生成 CMake 缓存(在 build 目录执行 cmake ..),确保 compile_commands.json 生成(如果启用了该选项)。
  3. 重启 VS Code 或重新加载窗口(Ctrl+Shift+PReload Window),使配置生效。

这样配置后,VS Code 的 C/C++ 扩展就能正确识别头文件、语法和编译选项,提供准确的代码提示和跳转功能。

3. 用CMake构建项目的例子

4. 使用CMake构建release版本

1)查看 CMakeList.txt 配置

以一个 TKLinux 项目为例,在 CMakeList.txt 中已经定义了如下内容:(拷贝自 PushLogic )

1
2
3
4
5
6
7
8
9
10
11
12
13
if (CMAKE_BUILD_TYPE MATCHES "Debug" )
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g ")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}  -g ")
    add_executable(${PROJECT_NAME}_d ${DIR_SRCS})
    target_link_libraries(${PROJECT_NAME}_d tkmessage.debug tkmysqlpool.debug  tkredispool.debug tksocketlibrary.debug tkasynlog.debug tkthreadpool.debug tkcommon.debug mysqlclient ssl crypto pthread dl uuid)

elseif (CMAKE_BUILD_TYPE MATCHES "Release" )
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 ")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 ")
    add_executable(${PROJECT_NAME} ${DIR_SRCS})
    target_link_libraries(${PROJECT_NAME} tkmessage tkmysqlpool  tkredispool tksocketlibrary tkasynlog tkthreadpool tkcommon mysqlclient ssl crypto pthread dl uuid)

endif (CMAKE_BUILD_TYPE MATCHES "Debug")

CMake 的 CMAKE_BUILD_TYPE 变量决定了构建模式。默认是 Debug(取决于你的环境)的话,直接 cmake .. && make 会生成 Debug 版本;上面那个例子中没有默认项,因此必须先指定编译类型。

若想编译出 Release 版本,必须在 CMake 配置阶段 显式指定 CMAKE_BUILD_TYPERelease(见后面的步骤),因为 CMake 是基于预定义的构建类型(Debug/Release 等)来生成对应 Makefile 的。

2)删除旧的构建产物

避免 Debug 版本的缓存影响

1
2
3
# 进入构建目录(通常是 build 目录,若没有则创建)
cd /path/to/your/project/build       # 替换为你的项目构建目录
rm -rf *       # 清除旧的 CMake 缓存和编译产物

3)指定构建类型编译

用 CMake 指定 Release 模式重新生成 Makefile :

1
2
3
mkdir build
cd build    # 在 build 目录下执行,指定构建类型为 Release
cmake -DCMAKE_BUILD_TYPE=Release ..
  • -DCMAKE_BUILD_TYPE=Release:这是关键参数,告诉 CMake 生成 Release 模式的配置(对应你 CMakeLists.txt 中 elseif (CMAKE_BUILD_TYPE MATCHES "Release") 分支)。
  • .. 表示 CMakeLists.txt 所在的上级目录(根据你的实际目录结构调整,确保指向项目根目录)。

执行 make 编译

1
make  # 此时会根据 Release 配置编译,生成不带 _d 后缀的可执行文件

4)验证是否为 Release 版本:

编译完成后,可查看结果文件的生成路径(这个路径也是在 CMakeList 中指定的)中是否有 release 版本的可执行文件。

或通过以下命令确认: (以 tkmsgmisconfigservice 为例)

1
2
# 查看可执行文件信息
file ../bin/tkmsgmisconfigservice
  • 若输出包含 optimizedwith debug_info(若 Release 配置加了 -g,所以会有调试信息),说明是 Release 版本。
  • 对比 Debug 版本(tkmsgmisconfigservice_d),Release 版本通常体积更小,且不带 _d 后缀(符合 CMakeLists.txt 中的配置)。

5)思考:

如果需要频繁切换 Debug/Release,建议分别创建两个构建目录(如 build_debugbuild_release),避免每次清除缓存。

Cmake和xmake和nmake之间的关系

CMake、XMake 和 NMake 是三种不同的构建系统,它们都用于自动化编译过程,但它们在设计理念、功能、使用方式以及支持的平台等方面存在差异。下面是它们之间的一些主要关联和区别:

CMake

  • 设计理念:CMake 是一个跨平台的自动化构建系统,它使用配置文件(CMakeLists.txt)来指定构建过程。CMake 可以生成多种构建系统的配置文件,例如 Makefile、Visual Studio 解决方案、Ninja 文件等。
  • 功能:CMake 支持大型项目,可以管理多目录、多应用程序的构建,还支持多种编译器和工具链。
  • 使用方式:用户通过编写CMakeLists.txt 脚本来配置项目,然后 CMake 会根据这些脚本生成实际的构建文件。
  • 平台支持:CMake 支持 Windows、macOS 和 Linux,生成的构建配置可以在各种平台上使用。

XMake

  • 设计理念:XMake 是一个基于 Lua 的轻量级跨平台构建工具,它贴近现代编程语言的设计,提供了丰富的API接口和模块,使构建过程更加方便快捷。
  • 功能:XMake 提供了包管理的功能,并且可以很容易地集成第三方库。XMake 还针对不同的编程语言和平台提供了专门的支持。
  • 使用方式:用户编写xmake.lua脚本文件来描述编译规则,XMake 解析这些脚本并执行构建任务。
  • 平台支持:XMake 同样支持 Windows、macOS 和 Linux,并且致力于简化构建过程和提高构建速度。

NMake

  • 设计理念:NMake 是微软提供的一个构建工具,通常与 Visual Studio 配合使用,用于基于 Makefile 的项目。
  • 功能:功能相对基础,主要用于执行 Makefile 中描述的命令,可以处理项目的依赖关系和构建顺序。
  • 使用方式:用户通过编写 Makefile 文件来配置项目,然后使用 NMake 工具按照 Makefile 的描述来编译项目。
  • 平台支持:NMake 主要用于 Windows,它是 Visual Studio 的一部分。

关联和区别

  • 关联:三者都用于自动化编译和构建软件项目,提供了一种方式来描述和执行构建过程。
  • 区别
    • 使用语言:CMake 使用自己的脚本语言,XMake 使用 Lua,NMake 使用类 Unix Makefile。
    • 跨平台:CMake 和 XMake 设计为跨平台工具,而 NMake 主要用于 Windows。
    • 灵活性:CMake 和 XMake 提供了更多高级特性和灵活性,NMake 相对基础。
    • 易用性:XMake 强调简单易用,而 CMake 提供了丰富的功能,但配置相对复杂;NMake 对于习惯使用 Visual Studio 的开发者来说比较熟悉。
    • 生成方式:CMake 生成多种构建系统的文件,而 XMake 和 NMake 是直接执行构建的。
本文由作者按照 CC BY 4.0 进行授权