项目配置是CMake工作流程的第一步,也是最重要的一步。
在这个阶段,CMake会检测系统环境、编译器、处理项目选项,并生成构建文件。

1. 项目目录

以一个计算加减乘除的C++程序为例:

03_CmdArgs/
├── 01
│   ├── CMakeLists.txt
│   └── src
│       ├── calc.cpp
│       ├── calc.h
│       └── main.cpp
├── 02
│   ├── CMakeLists.txt
│   └── src
│       ├── calc.cpp
│       ├── calc.h
│       └── main.cpp
└── 03
    ├── CMakeLists.txt
    └── src
        ├── calc.cpp
        ├── calc.h
        └── main.cpp

以下列出4个文件的内容。

1.1 calc.h

#ifndef CALC_H
#define CALC_H

int add(int a, int b);       // 加法
int subtract(int a, int b);  // 减法
int multiply(int a, int b);  // 乘法
int divide(int a, int b);    // 除法

#endif

1.2 calc.cpp

#include "calc.h"

int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int multiply(int a, int b) {
    return a * b;
}

int divide(int a, int b) {
    return a / b;
}

1.3 main.cpp

#include <iostream>
#include "calc.h"

int main() {

    int x, y;
    std::cout << "Input two integers: ";
    std::cin >> x >> y;

    // 加法
    std::cout << x << "+" << y << "=" << add(x, y) << std::endl;

    // 减法
    std::cout << x << "-" << y << "=" << subtract(x, y) << std::endl;

    // 乘法
    std::cout << x << "*" << y << "=" << multiply(x, y) << std::endl;

    // 除法
    std::cout << x << "/" << y << "=" << divide(x, y) << std::endl;

    return 0;
}

1.4 CMakeLists.txt

# 指定CMake的最低版本要求
cmake_minimum_required(VERSION 3.20)

# 定义项目名称
project(HelloCMake)

# 创建可执行文件
add_executable(hello src/main.cpp src/calc.cpp)

2. 最简单的配置和构建

2.1 配置

cmake -S . -B build

其中,-S指定源码目录,-B指定构建目录

选项 全称 含义 默认值
-S --source 源目录:包含CMakeLists.txt的目录 当前目录 (.)
-B --build 构建目录:生成构建文件(Makefile, 缓存,可执行文件)的目录 当前目录 (.)

默认使用Visual Studio 18 2026生成器,生成msbuild相关的构建文件,配置输出如下:

yinsh@coding4096  /f/cmake_demos/03_CmdArgs
$ cmake -S . -B build
-- Building for: Visual Studio 18 2026
-- Selecting Windows SDK version 10.0.26100.0 to target Windows 10.0.19045.
-- The C compiler identification is MSVC 19.50.35724.0
-- The CXX compiler identification is MSVC 19.50.35724.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/18/Community/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/18/Community/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (4.3s)
-- Generating done (0.0s)
-- Build files have been written to: F:/cmake_demos/03_CmdArgs/build

2.2 构建

cmake --build build

这是CMake的统一构建命令,用于编译已配置的项目。
它是对底层构建工具(如makeninjamsbuild)的抽象封装,提供跨平台一致的构建体验。

构建输出如下:

yinsh@coding4096  /f/cmake_demos/03_CmdArgs
$ cmake --build build
适用于 .NET Framework MSBuild 版本 18.3.0-release-26070-10+3972042b7

  1>Checking Build System
  Building Custom Rule F:/cmake_demos/03_CmdArgs/CMakeLists.txt
  main.cpp
  calc.cpp
  正在生成代码...
  hello.vcxproj -> F:\cmake_demos\03_CmdArgs\build\Debug\hello.exe
  Building Custom Rule F:/cmake_demos/03_CmdArgs/CMakeLists.txt

2.3 运行

yinsh@coding4096  /f/cmake_demos/03_CmdArgs
$ ./build/Debug/hello.exe 
Input two integers: 10 5
10+5=15
10-5=5
10*5=50
10/5=2

2. 生成器 (Generator)

生成器负责为原生构建系统生成输入文件(如Makefile、Ninja构建文件、Visual Studio解决方案等)

2.1 查看支持哪些生成器

使用以下命令查看系统支持哪些生成器

cmake --help

# 或者
cmake -G

以下是我Windows系统中支持的生成器(带*的是默认生成器 ):

Generators

The following generators are available on this platform (* marks default):
* Visual Studio 18 2026        = Generates Visual Studio 2026 project files.
                                 Use -A option to specify architecture.
  Visual Studio 17 2022        = Generates Visual Studio 2022 project files.
                                 Use -A option to specify architecture.
  Visual Studio 16 2019        = Generates Visual Studio 2019 project files.
                                 Use -A option to specify architecture.
  Visual Studio 15 2017        = Generates Visual Studio 2017 project files.
                                 Use -A option to specify architecture.
  Visual Studio 14 2015        = Deprecated.  Generates Visual Studio 2015
                                 project files.  Use -A option to specify
                                 architecture.
  Borland Makefiles            = Generates Borland makefiles.
  NMake Makefiles              = Generates NMake makefiles.
  NMake Makefiles JOM          = Generates JOM makefiles.
  MSYS Makefiles               = Generates MSYS makefiles.
  MinGW Makefiles              = Generates a make file for use with
                                 mingw32-make.
  Green Hills MULTI            = Generates Green Hills MULTI files
                                 (experimental, work-in-progress).
  Unix Makefiles               = Generates standard UNIX makefiles.
  Ninja                        = Generates build.ninja files.
  Ninja Multi-Config           = Generates build-<Config>.ninja files.
  FASTBuild                    = Generates fbuild.bff files.
  Watcom WMake                 = Generates Watcom WMake makefiles.
  CodeBlocks - MinGW Makefiles = Generates CodeBlocks project files
                                 (deprecated).
  CodeBlocks - NMake Makefiles = Generates CodeBlocks project files
                                 (deprecated).
  CodeBlocks - NMake Makefiles JOM
                               = Generates CodeBlocks project files
                                 (deprecated).
  CodeBlocks - Ninja           = Generates CodeBlocks project files
                                 (deprecated).
  CodeBlocks - Unix Makefiles  = Generates CodeBlocks project files
                                 (deprecated).
  CodeLite - MinGW Makefiles   = Generates CodeLite project files
                                 (deprecated).
  CodeLite - NMake Makefiles   = Generates CodeLite project files
                                 (deprecated).
  CodeLite - Ninja             = Generates CodeLite project files
                                 (deprecated).
  CodeLite - Unix Makefiles    = Generates CodeLite project files
                                 (deprecated).
  Eclipse CDT4 - NMake Makefiles
                               = Generates Eclipse CDT 4.0 project files
                                 (deprecated).
  Eclipse CDT4 - MinGW Makefiles
                               = Generates Eclipse CDT 4.0 project files
                                 (deprecated).
  Eclipse CDT4 - Ninja         = Generates Eclipse CDT 4.0 project files
                                 (deprecated).
  Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files
                                 (deprecated).
  Kate - MinGW Makefiles       = Generates Kate project files (deprecated).
  Kate - NMake Makefiles       = Generates Kate project files (deprecated).
  Kate - Ninja                 = Generates Kate project files (deprecated).
  Kate - Ninja Multi-Config    = Generates Kate project files (deprecated).
  Kate - Unix Makefiles        = Generates Kate project files (deprecated).
  Sublime Text 2 - MinGW Makefiles
                               = Generates Sublime Text 2 project files
                                 (deprecated).
  Sublime Text 2 - NMake Makefiles
                               = Generates Sublime Text 2 project files
                                 (deprecated).
  Sublime Text 2 - Ninja       = Generates Sublime Text 2 project files
                                 (deprecated).
  Sublime Text 2 - Unix Makefiles
                               = Generates Sublime Text 2 project files
                                 (deprecated).

2.2 指定生成器

默认使用Visual Studio 18 2026生成器,接下来配置为Unix MakefilesNinja生成器

2.2.1 Unix Makefiles

使用Unix Makefiles生成器,会生成make需要的构建文件

yinsh@coding4096  /f/cmake_demos/03_CmdArgs
$ cmake -S . -B build -G "Unix Makefiles"
-- The C compiler identification is GNU 15.2.0
-- The CXX compiler identification is GNU 15.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/msys64/ucrt64/bin/cc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/msys64/ucrt64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (3.2s)
-- Generating done (0.0s)
-- Build files have been written to: F:/cmake_demos/03_CmdArgs/build

2.2.2 Ninja

使用Ninja生成器,会生成ninja需要的构建文件

yinsh@coding4096  /f/cmake_demos/03_CmdArgs
$ cmake -S . -B build -G "Ninja"
-- The C compiler identification is GNU 15.2.0
-- The CXX compiler identification is GNU 15.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/msys64/ucrt64/bin/cc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/msys64/ucrt64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (2.4s)
-- Generating done (0.0s)
-- Build files have been written to: F:/cmake_demos/03_CmdArgs/build

3. 编译器

编译器路径既可以是:

  • 简单的命令名(如g++):前提是该命令已添加到系统的PATH环境变量中
  • 完整的绝对路径

配置完成后,可以使用以下命令看到配置的编译器:

# 列出所有缓存变量
cmake -LA <构建目录>

# 列出所有缓存变量,包括帮助文本
cmake -LAH <构建目录>

3.1 使用g++

yinsh@coding4096  /f/cmake_demos/03_CmdArgs
$ cmake -S . -B build -G "Ninja" -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
-- The C compiler identification is GNU 15.2.0
-- The CXX compiler identification is GNU 15.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/msys64/ucrt64/bin/gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/msys64/ucrt64/bin/g++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (2.4s)
-- Generating done (0.0s)
-- Build files have been written to: F:/cmake_demos/03_CmdArgs/build

3.2 使用clang++

yinsh@coding4096  /f/cmake_demos/03_CmdArgs
$ cmake -S . -B build -G "Ninja" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
-- The C compiler identification is Clang 21.1.8
-- The CXX compiler identification is Clang 21.1.8
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/msys64/ucrt64/bin/clang.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/msys64/ucrt64/bin/clang++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (2.6s)
-- Generating done (0.0s)
-- Build files have been written to: F:/cmake_demos/03_CmdArgs/build

4. 编译类型

4.1 简介

CMake支持多种预定义的构建类型,每种类型有不同的编译优化和调试选项,如下:

构建类型 说明 优化 调试信息 适用场景
Debug 调试版本 无优化 完整 开发和调试
Release 发布版本 最大优化 生产环境
RelWithDebInfo 带调试信息的发布版 优化 性能分析
MinSizeRel 最小体积发布版 体积优化 嵌入式、移动端

编译标志对比:

# GCC/Clang编译器
Debug:          -g
Release:        -O3 -DNDEBUG
RelWithDebInfo: -O2 -g -DNDEBUG
MinSizeRel:     -Os -DNDEBUG

# MSVC编译器
Debug:          /MDd /Zi /Ob0 /Od /RTC1
Release:        /MD /O2 /Ob2 /DNDEBUG
RelWithDebInfo: /MD /Zi /O2 /Ob1 /DNDEBUG
MinSizeRel:     /MD /O1 /Ob1 /DNDEBUG

可以通过-DCMAKE_BUILD_TYPE来指定构建类型,如下:

# Debug构建(开发阶段)
cmake -S . -B build-debug -DCMAKE_BUILD_TYPE=Debug
cmake --build build-debug
# 生成带调试符号的可执行文件,未优化

# Release构建(生产环境)
cmake -S . -B build-release -DCMAKE_BUILD_TYPE=Release
cmake --build build-release
# 生成优化的可执行文件,无调试符号

# RelWithDebInfo构建(性能分析)
cmake -S . -B build-profile -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build-profile
# 生成优化的可执行文件,保留调试符号

# MinSizeRel构建(嵌入式)
cmake -S . -B build-small -DCMAKE_BUILD_TYPE=MinSizeRel
cmake --build build-small
# 生成最小体积的可执行文件

配置完成后,可以使用以下命令查看编译标志:

# 列出所有缓存变量
cmake -LA <构建目录>

# 列出所有缓存变量,包括帮助文本
cmake -LAH <构建目录>

查看效果如下:

yinsh@coding4096  /f/cmake_demos/03_CmdArgs/02
$ cmake -LA build/Debug/
-- Configuring done (0.1s)
-- Generating done (0.0s)
-- Build files have been written to: F:/cmake_demos/03_CmdArgs/02/build/Debug
-- Cache values
CMAKE_ADDR2LINE:FILEPATH=C:/msys64/ucrt64/bin/addr2line.exe
CMAKE_AR:FILEPATH=C:/msys64/ucrt64/bin/ar.exe
CMAKE_BUILD_TYPE:STRING=Debug
CMAKE_CXX_COMPILER:STRING=C:/msys64/ucrt64/bin/g++.exe
CMAKE_CXX_COMPILER_AR:FILEPATH=C:/msys64/ucrt64/bin/gcc-ar.exe
CMAKE_CXX_COMPILER_RANLIB:FILEPATH=C:/msys64/ucrt64/bin/gcc-ranlib.exe
CMAKE_CXX_FLAGS:STRING=
CMAKE_CXX_FLAGS_DEBUG:STRING=-g
CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
...

4.2 Debug和Release文件大小

首先,配置Debug版本和Release版本,如下:

# Debug 版本
cmake -S . -B build/Debug -G "Ninja" -DCMAKE_C_COMPILER=gcc \
      -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Debug

# Release 版本
cmake -S . -B build/Release -G "Ninja" -DCMAKE_C_COMPILER=gcc \
      -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release

配置之后,可以使用cmake -LA <构建目录>来查看4个编译类型对应的编译选项。

然后,构建编译出可执行文件:

# Debug 版本
cmake --build build/Debug

# Release 版本
cmake --build build/Release

最后,比较文件大小

yinsh@coding4096  /f/cmake_demos/03_CmdArgs/01
$ ls -l build/Debug/hello.exe build/Release/hello.exe 
-rwxr-xr-x 1 yinsh yinsh 175434  225 12:31 build/Debug/hello.exe
-rwxr-xr-x 1 yinsh yinsh 144876  225 12:31 build/Release/hello.exe

4.3 -DNDEBUG宏与assert()断言

-DNDEBUGC/C++标准库约定的宏,用于控制断言(assertions)的行为,如果定义了NDEBUG这个宏:

  • assert语句在预处理(-E)阶段被移除
  • 编译器也就不会编译到assert语句
  • 生成的二进制文件中完全没有assert相关的代码

在四种预定义的编译类型中,Debug类型下没有NDEBUG这个宏定义,其他3种类型下有。

4.3.1 修改源码

  • calc.cpp
#include <cassert>
#include <iostream>
#include <stdexcept>

int divide(int a, int b) {

#ifdef NDEBUG
    std::cout << "NDEBUG defined, assert() will be removed" << std::endl;
#endif

    // 断言检查(仅 Debug 模式,辅助发现逻辑错误)
    assert(b != 0);

    if (b == 0) {
        throw std::invalid_argument("Divisor is zero");
    }

    return a / b;
}
  • main.cpp
int main() {

    // ...

    // 除法(包含异常处理)
    try {
        int result = divide(x, y);
        std::cout << x << "/" << y << "=" << result << std::endl;
    }
    catch (const std::invalid_argument &e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }

    return 0;
}

4.3.2 编译

# Debug编译
cmake -S . -B build/Debug -G "Ninja" -DCMAKE_C_COMPILER=gcc \
      -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Debug

cmake --build build/Debug

# Release编译
cmake -S . -B build/Release -G "Ninja" -DCMAKE_C_COMPILER=gcc \
      -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release

cmake --build build/Release

4.3.3 运行

  • 运行Debug版本(除数不为零)
yinsh@coding4096  /f/cmake_demos/03_CmdArgs/02
$ ./build/Debug/hello.exe 
Input two integers: 10 5
10+5=15
10-5=5
10*5=50
10/5=2
  • 运行Debug版本(除数为零)
yinsh@coding4096  /f/cmake_demos/03_CmdArgs/02
$ ./build/Debug/hello.exe 
Input two integers: 10 0
10+0=10
10-0=10
10*0=0
Assertion failed: b != 0, file F:/cmake_demos/03_CmdArgs/02/src/calc.cpp, line 27
  • 运行Release版本(除数不为零)
yinsh@coding4096  /f/cmake_demos/03_CmdArgs/02
$ ./build/Release/hello.exe 
Input two integers: 10 5
10+5=15
10-5=5
10*5=50
NDEBUG defined, assert() will be removed
10/5=2
  • 运行Release版本(除数为零)
yinsh@coding4096  /f/cmake_demos/03_CmdArgs/02
$ ./build/Release/hello.exe 
Input two integers: 10 0
10+0=10
10-0=10
10*0=0
NDEBUG defined, assert() will be removed
Error: Divisor is zero

5. 编译选项

可以在执行cmake命令时,在命令行中指定一个自定义的ENABLE_LOG宏,并且在代码中使用这个宏。

5.1 修改main.cpp

main函数中,根据ENABLE_LOG这个宏是否定义,执行打印语句,如下:

int main() {

    int x, y;
    std::cout << "Input two integers: ";
    std::cin >> x >> y;

#ifdef ENABLE_LOG
    std::cout << "===Calc Begin===" << std::endl;
#endif

    // 加法
    std::cout << x << "+" << y << "=" << add(x, y) << std::endl;

    // 减法
    std::cout << x << "-" << y << "=" << subtract(x, y) << std::endl;

    // 乘法
    std::cout << x << "*" << y << "=" << multiply(x, y) << std::endl;

    // 除法(包含异常处理)
    try {
        int result = divide(x, y);
        std::cout << x << "/" << y << "=" << result << std::endl;
    }
    catch (const std::invalid_argument &e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }

#ifdef ENABLE_LOG
    std::cout << "===Calc End===" << std::endl;
#endif

    return 0;
}

接下来,在Debug配置时,定义ENABLE_LOG宏,在Release配置时,不定义ENABLE_LOG

5.2 定义ENABLE_LOG宏

  • 配置、构建
# Debug
cmake -S . -B build/Debug -G "Ninja" -DCMAKE_C_COMPILER=gcc \
      -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-DENABLE_LOG"

cmake --build build/Debug
  • 运行
yinsh@coding4096  /f/cmake_demos/03_CmdArgs/03
$ ./build/Debug/hello.exe
Input two integers: 10 5
===Calc Begin===
10+5=15
10-5=5
10*5=50
10/5=2
===Calc End===

5.3 未定义ENABLE_LOG宏

  • 配置、构建
cmake -S . -B build/Release -G "Ninja" -DCMAKE_C_COMPILER=gcc \
      -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release

cmake --build build/Release
  • 运行
yinsh@coding4096  /f/cmake_demos/03_CmdArgs/03
$ ./build/Release/hello.exe
Input two integers: 10 5
10+5=15
10-5=5
10*5=50
NDEBUG defined, assert() will be removed
10/5=2