简介:这份 CMake 3.30.3 Windows x86-64 离线文档包面向在 Windows 64 位环境下使用 C++ 的开发者,尤其适合需要频繁查阅构建配置、命令参数与跨平台项目组织方式的读者。它围绕 CMake 脚本语言、构建系统生成、测试与预设配置等主题,提供可本地检索的参考材料,帮助减少联网查文档的时间。包内共 2000 个文件,以 1147 个 txt 和 853 个 html 为主,压缩后约 43.34MB,html 适合按章节浏览,txt 便于全文搜索与摘录。内容覆盖命令手册、变量与属性索引、构建系统说明、CTest、Presets、File API 及生成器表达式等专题,可用于排查 CMakeLists 配置问题、理解生成器行为、梳理测试与工程集成流程。已有 512 人学习或下载,适合初学到中级 C++ 开发者作为案头速查资料,也便于团队离线共享统一版本参考。
1. 为一个 CMake 安装包单独写一篇:Windows 上 C++ 构建链的第一块砖
拉下一个 C++ 仓库,照着 README 敲cmake -S . -B build,屏幕弹出一行CMake 3.20 or higher is required. You are running version 3.12.4—— 这是 Windows 上做 C++ 最常见的第一道坎。IDE 里自带的那份 cmake.exe,版本总比项目要求的低一截。cmake-3.30.3-windows-x86-64 正好能解决这个问题:它是面向 64 位 Windows 的二进制分发包,装好之后命令行里就是 3.30.3 这一版 cmake、ctest、cpack,配合 MSVC、Ninja 或 MinGW 都能跑。在 Windows 上写 C++、又不想被 IDE 的升级节奏绑住的人适合它;本机与 CI 需要对齐构建版本的人适合它;刚被cmake_minimum_required挡在门外的新手也适合它。
2. 包里到底装了什么:安装器版与便携版的取舍
很多人拿到这个包的第一反应是双击、下一步、结束,然后在 PowerShell 里敲cmake --version,发现版本没变。这种翻车不是包的问题,是没搞清楚包里两种形态的差别。x86-64 这个后缀表示它自带的是一套 64 位可执行文件,跟你在 32 位机器上装的那份不是同一个东西,混着用会出现各种离奇症状,后面第 5 章会专门拆。
2.1 安装器版与便携 zip 版:同一份二进制,两条落地路径
分发包通常提供.msi安装器和.zip压缩包两种形态。二进制内容几乎一样,区别全在「系统集成」这四个字上。
| 形态 | 写注册表 | PATH 处理 | 卸载方式 | 适合场景 |
|---|---|---|---|---|
.msi安装器 | 是 | 安装时可选择写入系统或用户 PATH | 控制面板卸载 | 个人主力机、需要长期稳定可用 |
.zip便携版 | 否 | 完全手动,或用绝对路径调用 | 删目录即完成 | CI 节点、无管理员权限、多版本并存 |
安装器版的好处是省事:装完之后任何新开的终端都能直接用cmake,卸载也干净。代价是它默认往Program Files写,某些企业策略下会拦截,而且你很难在同一台机器上并行维护 3.30.3 和另一个版本。
便携版反过来,它不动系统,解压到哪个目录就在哪个目录。我在需要同时验证两个项目(一个锁 3.28、一个要求 3.30)的机器上,一律用便携版,各放一个目录,谁也别碍谁。代价是每次调用要么写全路径,要么自己管 PATH 顺序。
提示:便携版解压后真正的可执行文件在
cmake-3.30.3-windows-x86-64\bin\下面,不是解压出来的根目录。PATH 指错一层,cmake就永远找不到。
2.2 那几个 exe 各自管什么
解压开bin目录,会看到一排可执行文件。搞清楚分工,排查的时候能省很多时间。
| 可执行文件 | 职责 | 什么时候会用到 |
|---|---|---|
cmake.exe | 配置阶段生成构建系统;--build时调度底层构建器 | 几乎所有场景 |
ctest.exe | 跑测试、管理测试用例 | 加了enable_testing()的工程 |
cpack.exe | 打包成 zip/msi/nsis 等 | 要出安装包时 |
cmake-gui.exe | 图形化配置缓存变量 | 调第三方库路径、看缓存项 |
cmake -E | 内置的文件/时间/环境子命令 | 写跨平台脚本时替代cp、mkdir |
关键认知是:CMake 自己不编译。它只负责读CMakeLists.txt、探测编译器、生成一份底层构建系统描述(*.vcxproj或build.ninja),然后把编译的活派给 MSBuild 或 Ninja。所以「CMake 报错」和「编译报错」是两个完全不同的阶段,排错方向也完全不同 —— 前者看缓存和生成器,后者看编译器和头文件。
2.3 为什么不直接用 IDE 自带的那份,也不盲目追最新
IDE 自带的 CMake 有两个问题。一是版本节奏跟着 IDE 走,你升不了它,也降不了它;二是它藏得深,路径里带一串版本号,写脚本时很难引用。项目里只要有一行cmake_minimum_required(VERSION 3.25),IDE 那份 3.20 就直接罢工。
另一个极端是永远追最新版。我不建议。CMake 每个次版本都会引入新策略(policy)和新行为,某些老工程在cmake_minimum_required声明的旧版本下本来自动走兼容分支,你一升级,行为就变了,配置能过但链接参数悄悄变了,这种问题最难查。3.30.3 这种「次版本 + 补丁号」的组合,行为已经稳定,适合作为团队内的统一基线。
先确认一下你手上这份到底是哪一版、能生成什么:
# 打印版本与构建信息,确认不是 PATH 上另一份旧 cmake cmake --version # 列出这台机器上当前可用的生成器 cmake --helpcmake --version第一行是版本号,后面几行是构建信息。如果第一行不是 3.30.3,说明 PATH 上还有别的 cmake 排在前面,先别急着往下走,用where.exe cmake把所有命中项列出来。
cmake --help不带参数时输出用法,末尾会附一份生成器清单,标注哪些是当前平台可用的。清单里*号标记的是默认生成器。这份清单直接决定了你后面-G能填什么,填一个不支持的名字,配置阶段会立刻报Could not create named generator。
3. 装上去并跑通:从 PATH 到第一个可执行文件
装完不等于能用,能用不等于路径对了。这一章按「装 → 验 → 跑」三步走,每一步都有可抄的命令。新手照着敲能出结果,熟手可以跳过安装直接看 3.3 的冒烟工程。
3.1 静默安装:把 msi 挂到正确的 PATH 作用域
图形界面点下一步当然可以,但在批量部署或写脚本时,静默安装更可控:
# 以管理员身份运行 PowerShell # /qn 静默无界面,/norestart 禁止自动重启 # ADD_CMAKE_TO_PATH 支持 System / User / None 三个值 msiexec /i .\cmake-3.30.3-windows-x86-64.msi /qn /norestart ADD_CMAKE_TO_PATH=System三个参数各管一件事。/qn关掉所有交互窗口,装完没有任何提示,所以后面必须自己验版本。/norestart是防止安装器在需要重启时直接把你机器重启了 —— 装开发工具时被强制重启,是很让人头疼的事。
ADD_CMAKE_TO_PATH是最关键的一个。填System写系统级 PATH,所有用户都能用,但需要管理员权限;填User只写当前用户的 PATH,不需要提权,适合受管控的办公机;填None表示只装不配 PATH,之后靠绝对路径调用。我在共享构建机上一般用System,在个人开发机上用User,避免污染系统环境。
装完必须新开一个终端。已经开着的 PowerShell 继承的是启动那一刻的环境块,安装器改了系统变量它也不会感知 —— 这是「装完还是旧版本」最常见的成因。
3.2 便携版解压与 PATH 顺序控制
便携版的核心是「顺序」。PATH 是从左往右找的,第一个命中的就是生效的那份。
# 解压到纯 ASCII、无空格的目录,避开 OneDrive 同步目录 Expand-Archive -Path .\cmake-3.30.3-windows-x86-64.zip -DestinationPath D:\tools\ # 当前会话临时生效,追加到最前面 $env:Path = "D:\tools\cmake-3.30.3-windows-x86-64\bin;" + $env:Path # 核对到底命中了哪一个 where.exe cmake where.exe ctest第一行改动的是当前会话,关掉窗口就失效,适合临时试验。要长期生效就写用户环境变量:
# 读取现有用户 PATH,前插新目录,再写回 $old = [Environment]::GetEnvironmentVariable("Path", "User") $new = "D:\tools\cmake-3.30.3-windows-x86-64\bin;" + $old [Environment]::SetEnvironmentVariable("Path", $new, "User")注意这里是「前插」而不是「追加」。追加到末尾的话,一旦系统里已经有一份旧 cmake,你永远用的还是旧的。写回之后同样要新开终端验证。
where.exe cmake会按 PATH 顺序把所有命中项列出来,第一行才是真正生效的。如果第一行不是D:\tools\...,说明前面还挡着别的安装,要么删掉那一项,要么把新目录再往前挪。
3.3 冒烟工程:配置、构建、运行三段式
版本对了,用一个最小工程验证整条链路。三个文件就够。
# CMakeLists.txt cmake_minimum_required(VERSION 3.20) project(smoke LANGUAGES CXX) add_executable(smoke main.cpp) target_compile_features(smoke PRIVATE cxx_std_17)// main.cpp #include <iostream> int main() { std::cout << "cmake ok\n"; return 0; }# -S 源码目录,-B 构建目录(会自动创建) # -G 指定生成器,-A 指定目标平台架构 cmake -S . -B build -G "Visual Studio 17 2022" -A x64 # --build 调度底层构建器;--config 对多配置生成器指定 Debug/Release cmake --build build --config Release # 多配置生成器的产物在 build\<Config>\ 下 .\build\Release\smoke.exe逐段说清楚。-S和-B是 3.13 之后推荐的写法,它把「源码目录」和「构建目录」分开,构建产物全部落在build里,删掉就等于彻底重来。cmake_minimum_required(VERSION 3.20)里写 3.20 而不是 3.30,是为了给协作者留余量 —— 声明一个宽松的下限,比写死当前版本更利于协作。
-G后面必须是一个真实存在的生成器名,带空格时要整体加引号。-A x64只在 Visual Studio 系生成器下有意义,它决定目标是 64 位还是 32 位。这两个参数一起决定了build目录的「身份」,后面改任何一个,都必须清空缓存重来。
cmake --build是跨生成器的统一入口,换生成器不用改命令。--config Release对多配置生成器(VS、Ninja Multi-Config)有效,对单配置生成器(Ninja、MinGW Makefiles)无效 —— 单配置的构建类型在配置阶段就通过-DCMAKE_BUILD_TYPE定死了。这个差异是新手最容易混乱的点,第 4 章会展开。
最后.exe的路径也随生成器变:VS 生成器在build\Release\下,单配置的 Ninja 生成器通常在build\根下。找不到产物时别怀疑构建失败,先dir /s /b build\*.exe看一眼它到底放哪了。
4. 生成器与工具链参数:-G、-A、-T 到底怎么配
命令行能跑通之后,真正的分水岭在生成器选型上。同一份CMakeLists.txt,换一个-G,构建速度、产物目录、调试体验、依赖查找行为全都会变。这一章按「选哪个 → 参数怎么填 → 编译器怎么指 → 索引怎么导」四层拆。
4.1 生成器选型:多配置还是单配置
| 生成器 | 配置类型 | 前置环境 | 产物位置 | 典型用途 |
|---|---|---|---|---|
Visual Studio 17 2022 | 多配置 | 装了对应 VS | build\<Config>\ | 日常开发、需要 IDE 调试 |
Ninja Multi-Config | 多配置 | PATH 上有 ninja | build\<Config>\ | 想要 Ninja 速度又要多配置 |
Ninja | 单配置 | PATH 上有 ninja + 编译器 | build\ | CI、追求最快增量编译 |
MinGW Makefiles | 单配置 | MinGW 工具链在 PATH | build\ | 用 GCC 系的 Windows 工程 |
选型的判断逻辑很简单:要 IDE 调试、要一键切 Debug/Release,用 VS 生成器;嫌 MSBuild 慢、想要更快的增量编译,用 Ninja;CI 上追求确定性,用 Ninja 单配置 + 显式CMAKE_BUILD_TYPE。
多配置生成器的一个好处是 Debug 和 Release 共用一份缓存,切配置不用重新 configure。代价是它把「配置」这件事拖到了构建阶段,所以-DCMAKE_BUILD_TYPE在它下面基本是空操作 —— 你设了也不会报错,但也不会生效,这种静默失效最坑人。
4.2 -A 与 -T:把平台和工具集钉死
# -A x64 指定目标平台为 64 位 # -T host=x64 让编译过程本身用 64 位工具链,避免大工程编译时内存耗尽 cmake -S . -B build-vs -G "Visual Studio 17 2022" -A x64 -T host=x64-A的合法值跟生成器绑定:VS 系生成器接受x64、Win32、ARM64。注意Win32指的就是 32 位 x86,这个名字很有迷惑性,别以为它跟 Windows API 有关。如果省略-A,某些生成器会走默认(通常是当前宿主架构),在 64 位机器上得到 64 位目标,看起来没问题,但换一台机器就变了 —— 构建脚本里最好永远显式写出来。
-T管的是工具集。host=x64表示用 64 位的编译器宿主进程,这在链接大型工程时能明显降低内存压力;默认情况下 MSBuild 可能挑 32 位的编译器宿主,编译到一半报内存不足就是这么来的。-T还可以指定工具集版本,用于在同一台机器上切换不同版本的编译器。
还有一个容易忽略的点:-A和-T都属于「写进缓存」的参数。在已经配置过的build目录上改成-A Win32,配置阶段会直接报生成器/平台不匹配,而不是帮你重新生成。处理办法只有两个:删目录,或者用cmake --fresh(3.24 起支持)让它在保留目录的同时重建缓存。
4.3 Ninja 路线:先有开发环境,再谈编译器
Ninja 生成器不像 VS 生成器那样自己去翻注册表找编译器,它要求cl.exe已经在 PATH 上。所以必须在「x64 Native Tools 命令提示符」里跑,或者先执行开发环境初始化脚本。
# 单配置生成器:构建类型必须在配置阶段定死 cmake -S . -B build-ninja -G Ninja -DCMAKE_BUILD_TYPE=Release # -j 控制并行度,一般取物理核心数 cmake --build build-ninja -j 8-DCMAKE_BUILD_TYPE=Release是单配置生成器的命门。不写这个,默认是空字符串,编译器优化选项一个都不加,构建出来的东西慢得莫名其妙,而且不会报任何错。常见做法是在脚本里显式判断:没传就默认Release。
如果cmake报No CMAKE_CXX_COMPILER could be found,几乎可以断定当前终端不在开发环境里。这时要么换到原生工具命令提示符,要么显式指定:
# 显式指定编译器,路径按实际安装位置调整 cmake -S . -B build-ninja -G Ninja ^ -DCMAKE_BUILD_TYPE=Release ^ -DCMAKE_CXX_COMPILER=cl.exe ^ -DCMAKE_C_COMPILER=cl.exe显式指定编译器还有一个副作用:一旦写进缓存,后续更换编译器必须清空构建目录,否则 CMake 会继续用缓存里那个路径,哪怕它已经不存在了。我一般只在 CI 的干净目录上用这种方式,本地开发更倾向于老老实实进开发环境。
4.4 导出编译索引与统一运行库
VS Code 的 C++ 插件、clangd 这类工具都靠compile_commands.json做跳转和补全。这份文件不会自动生成,得手动打开开关:
# 注意:只有 Makefile 系和 Ninja 系生成器支持导出编译数据库 cmake -S . -B build-ninja -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON打开之后,compile_commands.json会落在构建目录根下。VS 系生成器不支持这个开关,所以想要索引又想要 VS 调试,常见做法是配两套构建目录:一套 Ninja 专门给编辑器读索引,一套 VS 用来调试。
运行库的统一同样值得在配置阶段处理掉:
# 统一 MSVC 运行库:Release 用静态 MT,Debug 用静态 MTd # 需要策略 CMP0091 为 NEW(3.15+ 在 cmake_minimum_required>=3.15 时自动生效) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")这个变量的默认值依赖CMP0091策略。如果工程声明的cmake_minimum_required低于 3.15,策略是 OLD,你设了也不生效 —— 又一处静默失效。用生成器表达式按配置区分,比在多个地方分散写target_compile_options干净得多,也避免了 Debug 和 Release 的 CRT 混链接。
5. 避坑与排查:五个在 Windows 上反复翻车的点
这一章记录的都是实际踩过的坑,现象、原因、解决三段式,按「环境 → 缓存 → 链接」的顺序排。遇到报错时,先按顺序对一遍,多数情况能当场定位。
5.1 环境与路径:装完立刻会撞上的两个坑
坑 1:装完cmake --version还是旧版本
现象:安装器提示成功,重新打开终端敲cmake --version,显示的仍是 3.12 或 3.20 之类的老版本号。
原因:要么没有新开终端,进程仍然持有旧的环境块;要么 PATH 上排在前面的还有另一份 cmake。安装器写 PATH 时通常是插入,但如果你之前手动配过一个路径,顺序就不好说了。
解决:先where.exe cmake把所有命中项列出来,确认第一个是不是新装的路径。如果是旧终端的问题,关掉重开即可。如果是顺序问题,去环境变量面板把新目录上移到最前面,再新开终端。还有一种情况是 IDE 内部自己维护了 CMake 路径设置,命令行对了但 IDE 里还是旧的,那要去 IDE 的设置里单独指一遍。
坑 2:工程放在中文路径或带空格的目录下
现象:配置阶段报路径解析失败,或者配置能过、编译时冒出一堆找不到.obj、无法写入文件的错误。
原因:MSBuild 和某些第三方库的构建脚本对含空格、非 ASCII 字符的路径处理并不严实。中文路径在某些代码页下会被替换成问号,路径就不存在了。放在 OneDrive 同步目录里还有另一个问题:同步进程会短暂锁住中间文件,编译器写不进去就报错。
解决:工程一律放在纯 ASCII、无空格、不被同步的目录下,比如D:\work\。构建目录也一起挪,不要只挪源码。这个坑的麻烦之处在于它时好时坏,同一天里可能编译成功一次失败一次,所以血泪经验是:一开始就别往用户目录下放。
5.2 缓存与生成器:改了参数不改缓存
坑 3:generator does not match the generator used previously
现象:换了一个-G参数重新配置,立刻报生成器不匹配,或者平台不匹配(-A改了也会报类似的错)。
原因:build目录下的CMakeCache.txt记录了首次配置时的生成器、平台、编译器路径。后续命令如果与之冲突,CMake 不会自动重建,而是直接拒绝。这是保护机制,避免两套构建系统混在同一个目录里长大。
解决:删掉整个build目录是最彻底的。不想删的可以用cmake --fresh -S . -B build ...,它会在保留目录的同时重建缓存并重新探测编译器(3.24 起支持,3.30.3 上完全可用)。记住一条:凡是-G、-A、-T、编译器路径这四类参数之一发生变化,就必须清缓存。
坑 4:32 位与 64 位包混用
现象:机器明明是 64 位,cmake --version也显示正常,但生成的产物是 32 位的,链接第三方 64 位库时报架构不匹配;或者反过来,在某些环节报The specified module could not be found。
原因:PATH 上同时存在 x86 和 x86-64 两份安装,先命中的是 32 位那份。CMake 本身能跑,但它探测到的运行环境和默认平台就会跟着偏。另一种情况是-A没写,走了生成器的默认值。
解决:where.exe cmake把全部命中项列出来,把不需要的从 PATH 里删掉,只保留cmake-3.30.3-windows-x86-64\bin这一条。构建脚本里永远显式写-A x64。这个坑的隐蔽之处在于它不会报错,只会让你在链接阶段才突然发现架构对不上。
5.3 链接与查找:构建过了但跑不起来
坑 5:运行库不匹配与找不到 DLL
现象一:链接时报LNK2038: 检测到"RuntimeLibrary"的不匹配。现象二:exe 能生成,双击启动报缺少VCRUNTIME140.dll或VCRUNTIME140D.dll。
原因:现象一是不同模块用了不同的 CRT(/MT与/MD混用,或 Debug 与 Release 混链),静态库尤其容易出这个问题。现象二是 Release 的 exe 依赖了 Debug 版运行库,或者目标机器上根本没装对应的运行库分发包。
解决:在顶层CMakeLists.txt里用CMAKE_MSVC_RUNTIME_LIBRARY统一设置,并确认CMP0091策略是 NEW。第三方静态库如果是预编译好的,先查清它是哪套 CRT,别硬链。发布前用依赖查看工具扫一遍 exe 的导入表,把缺的运行库随包分发,或者统一改用静态 CRT。
find_package找不到库是同一片区的另一个高频问题。现象是配置阶段报Could not find a package configuration file。原因通常是库装在非标准前缀下。解决是配置时加-DCMAKE_PREFIX_PATH="D:/libs/foo",而不是去改CMakeLists.txt硬编码路径 —— 后者换台机器就废了。
排查顺序上,我习惯从下往上走:先where.exe cmake确认版本,再看CMakeCache.txt里的生成器和编译器,再看compile_commands.json里的编译参数,最后才看链接。倒着查经常会在一个已经被缓存固化的错误前提上浪费半小时。
6. 进阶:用 Presets 把 3.30.3 的配置固化成文件
命令行参数一多,人就记不住。-G、-A、-DCMAKE_BUILD_TYPE、-DCMAKE_EXPORT_COMPILE_COMMANDS这几项凑在一起,每次手敲既慢又容易漏。3.30.3 这一代对CMakePresets.json的支持已经很完整,把配置固化成文件是性价比最高的一个习惯。
{ "version": 3, "configurePresets": [ { "name": "vs-x64", "generator": "Visual Studio 17 2022", "architecture": "x64", "binaryDir": "${sourceDir}/build/vs" }, { "name": "ninja-release", "generator": "Ninja", "binaryDir": "${sourceDir}/build/ninja", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" } } ], "buildPresets": [ { "name": "vs-release", "configurePreset": "vs-x64", "configuration": "Release" }, { "name": "ninja", "configurePreset": "ninja-release" } ] }version字段决定可用的特性集合,写 3 兼容面最宽。每个 preset 的name就是命令行里要引用的标识,binaryDir用${sourceDir}拼接,保证不同机器上路径自动适配。VS 那条用architecture表达-A x64,Ninja 那条把单配置的构建类型塞进cacheVariables—— 这个字段是-D的等价物,写错类型(比如把布尔值写成字符串"ON")不会报错但可能不生效,注意保持字符串形式。
配套命令很清爽:
# 列出所有可用的 configure preset cmake --list-presets # 按 preset 配置,等价于手敲 -G/-A/-D 一长串 cmake --preset vs-x64 # 按 build preset 构建 cmake --build --preset vs-release # 缓存脏了,重建但保留目录 cmake --preset vs-x64 --fresh验证方法很直接:删掉整个build目录,只用cmake --preset xxx加cmake --build --preset xxx两条命令,看能不能从零跑出可执行文件。能跑通,说明这份 preset 文件是自洽的,可以进版本库;跑不通,说明还有隐含的本地环境依赖没被写进去。
一个小技巧:CMakeUserPresets.json会被自动合并进来,且可以加进.gitignore。把机器相关的路径(比如某个库的安装前缀)放这里,团队共享的CMakePresets.json就能保持干净。从那以后我每次接手一个新工程,第一件事都是先写一份 preset 再动代码 —— 与其在聊天窗口里跟人解释该敲哪几条命令,不如让人git clone完直接--preset一把梭。希望帮到你。
本文还有配套的精品资源,点击获取