ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Windows下CMake 3.14.2实战:生成器、编译器与踩坑指南

Windows下CMake 3.14.2实战:生成器、编译器与踩坑指南 简介CMake 3.14.2 Windows 64位版是一款跨平台自动化构建系统专门面向需要在Windows环境下高效管理C/C项目、尤其是OpenCV相关工程的开发者。它通过平台无关的CMakeLists.txt生成Visual Studio解决方案或Makefile帮助开发者从复杂依赖中抽身关注项目自身的构建逻辑。压缩包共5681个文件约29.58MB文件以html、rst、txt等格式的技术文档和cmake、in等模块脚本为主同时包含少量exe可执行程序及c、cpp示例代码兼顾安装部署与学习扩展。已有165人学习下载。包内提供的cmake命令手册、属性变量说明等内容预览配合ctest、cpack等辅助工具既能稳定搭建CMake环境也能系统理解OpenCV项目配置思路与跨平台构建流程适合Windows下的C开发者和计算机视觉学习者直接上手无论是初次接触构建工具的新手还是需要整合OpenCV的进阶开发者都能从中获得清晰指引。2025年了还在用3.14.2这套老版本聊点Windows下CMake的实际体验说句实话看到“cmake-3.14.2-win64-x64”这个标题时我第一反应是这版本有点年头了。3.14.2是2019年4月发布的版本放在今天确实不是新闻。但换个角度想很多生产环境、老项目、依赖链比较固定的工程至今还锁在3.14.x上——它不是最新但它是稳的代名词。CMake 3.14这个系列引入了不少对Windows开发者很关键的能力比如更完善的cmake --build跨生成器构建、对VS 2019生成器的初步适配、source_group的改进等等。在那个时间点它几乎是Windows上配合Visual Studio做C项目最顺手的版本之一。这篇文章不打算做成CMake保姆级教程而是围绕这个特定版本、在Windows x64环境下把我自己实际踩过、验证过、解决过的东西一次说清楚。适合三类人看刚下载完cmake-3.14.2-win64-x64.zip不知道怎么装的新手从Linux转Windows、被生成器折腾到怀疑人生的跨平台开发者以及那些因为历史工程被迫锁定老版本、想尽量把日子过得好一点的维护者。顺便说一句虽然我标题写的是3.14.2但后面讲的大部分东西在3.14到3.16之间都是通用的。版本差异之处我会单独指出来不会让你在别处看到3.16的写法拿回来跑不动然后再回来骂我。1. 安装与版本选择的那些事别双击exe就完事了1.1 下载之后你手里拿的到底是个什么东西从CMake官网下载cmake-3.14.2-win64-x64你会得到两种格式.msi安装包和.zip压缩包。很多新手习惯性双击.msi一路Next这没问题但不一定是最优解。这里有个关键区别.zip是绿色版解压就能用不写注册表不污染系统.msi会帮你写注册表项和开始菜单快捷方式但也会在系统里留下卸载信息。我个人在Windows上做C构建时更倾向用.zip版本。原因很朴素CI机器和Docker容器里你不可能跑图形界面的msi安装向导就算在本地开发机zip版本也方便同时保留多个CMake版本——切换到新版本只需改环境变量路径删掉旧版本也只需删文件夹。CMake这种工具版本切换的代价越低你越敢去升级。不过.zip版本有一个要注意的小坑——它默认不会自动把bin目录加进PATH。如果你打开命令行敲cmake提示不是内部或外部命令十有八九就是环境变量没配。配置路径也很常规对着cmake-3.14.2-win64-x64/bin这一层设置就可以了。提示如果你的机器上同时装了多个CMake命令行里执行的到底哪个版本用where cmake查一下全路径就知道了。这个命令救过我很多次。1.2 3.14这个版本在Windows上的特殊分量选3.14.2在2019年那会儿有一个很现实的原因——它对Visual Studio 2019的支持是当时最稳的。VS 2019的生成器在3.14里还是带-deprecated警告的试验状态但实际用下来问题不大。VS 2019的v142工具集配合CMake 3.14.2跑中小型C工程的Configure、Build、Install全流程基本是零摩擦。另外3.14版本把JSON诊断输出--debug-output之类的能力做得更像样了一些cmake --build也在这个时期开始成为跨生成器的统一构建入口。这意味着你可以在Windows上用同样的命令习惯去构建Makefile工程和VS工程减少记忆负担。后来的3.15、3.16虽然更强但对锁版本的工程来说3.14.2其实已经覆盖了绝大多数日常构建需求没必要为了功能升级去承担生成器变更带来的风险。如果你是非要用新特性的朋友我建议你至少升到3.16因为3.16的VS生成器对Unity BuildCMAKE_UNITY_BUILD的支持才真正好用3.14.2虽然能开Unity Build但只能在Makefile和Ninja生成器下生效VS生成器是忽略这个选项的。这一点藏得比较深很多人配置了没效果其实是版本和生成器共同导致的。2. 用3.14.2在Windows上配置C工程生成器与工具链的调度逻辑2.1 先想清楚你要用哪种生成器Windows上跑CMake最常见的生成器选择就三样Visual Studio生成器、MinGW Makefiles、Ninja。三者的关系和使用场景完全不同很多人一开始就在这里栽跟头。Visual Studio生成器-G Visual Studio 16 2019生成的不是Makefile而是一个.sln解决方案。它的特点是不直接调用编译器而是生成VS工程文件由VS或者MSBuild完成编译。这个生成方式对新手最友好因为错误格式、调试体验都跟VS原生项目一致而且不用手动管理编译器路径——CMake会自己去探测VS安装位置。缺点是Configure速度相对慢生成的中间文件也更多。MinGW Makefiles依赖你安装MinGW-w64或MSYS2里的GCC工具链。它适合那些习惯了make命令的人也适合需要快速产出。代价是你必须自己保证gcc、g、mingw32-make在PATH里CMake不负责给你找。Ninja到了今天已经是很多人的首选但在3.14那个年代Ninja搭配VS生成的组合在Windows上还不是特别普及。Ninja的好处是增量构建快得明显坏处是如果配VS工具链需要额外确认cl.exe的环境——通常是先用vcvars64.bat开一个开发者命令行再在同一个终端里跑CMake否则CMake找不到MSVC编译器。我自己在Windows上维护跨平台库时本地调试用VS生成器CI里跑Ninja。这种组合最省心也最能暴露两种构建路径下各自的问题。2.2 从Linux迁过来最容易忽略的编译器探测问题很多从Linux转过来的朋友第一次在Windows上运行CMake会突然看到类似CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage这种报错。这个报错的本质是CMake在Configure阶段需要确定C/C编译器但它在当前环境PATH里找不到合适的编译器。在Linux上gcc、g通常在/usr/bin里PATH天然包含Windows上则完全不一样——MSVC的cl.exe藏在VS安装目录的VC/Tools/MSVC/版本/bin/Hostx64/x64下面默认不在PATH里。如果你不先启动x64 Native Tools Command Promptvcvars64而是直接开一个普通cmd或PowerShell去跑CMakeCMake就找不到编译器。解决方案有两个方向。第一老老实实用cmake-gui或者在开始菜单 - Visual Studio 2019 - x64 Native Tools Command Prompt里工作让CMake能继承cl.exe的环境。第二给CMake显式指定编译器路径cmake -G Visual Studio 16 2019 -A x64 ..用VS生成器时-A x64可以自动定位到正确的架构工具集很大程度上规避了PATH问题。这也是我为什么推荐新手在Windows上第一优先用VS生成器的原因——它不是最快的但它帮你处理掉了最脏的环境问题。2.3 source_group与文件分组3.14时代组织IDE项目结构的方式如果你从Linux项目迁移过来有一件事Linux下的Makefile工程是不用管的但Windows上的VS工程很在意——源文件在解决方案资源管理器里的目录分组。CMake在3.14里没有后来3.23的FILE_SET机制源文件分组主要靠source_group命令。source_group(src FILES ${SRC_FILES}) source_group(include FILES ${INC_FILES})这个命令不参与编译逻辑只影响生成器输出的工程结构。用VS生成器构建时设置好source_group可以让头文件和源文件按逻辑目录展示维护体验好很多。如果你不管它所有文件会铺平摊在解决方案里文件一多根本没法看。另外3.14还有一个我经常用的能力target_sources可以配合source_group按子目录分批添加文件。这在大工程里比一次性把几百个文件add_executable(${ALL_FILES})清爽得多也方便后续用set_property给单个文件打标记——比如下面要说的预编译头。3. 预编译头、CUDA、MPI3.14.2上高频踩坑的三个配置点3.1 指定预编译头文件3.14没有target_precompile_headers怎么办外部编辑器如果你搜cmake 指定precompiledheaderfile大概率会看到很多博客教你写target_precompile_headers。这个命令是CMake 3.16才引入的。3.14.2的CMakeLists.txt里直接写target_precompile_headers会得到Unknown CMake command报错——这是网上教程最坑的地方版本不对照着抄必死。那么3.14上怎么指定预编译头我用的方案是先用add_library或add_executable把目标建好再用set_property给目标设置VS的PCH相关属性add_executable(my_app main.cpp src/util.cpp) target_compile_definitions(my_app PRIVATE MY_PCH_H_INCLUDE) set_property(TARGET my_app PROPERTY VS_PREPROCESS_FORCE_INCLUDE $(SolutionDir)src/pch.h) set_property(SOURCE src/util.cpp PROPERTY VS_PREPROCESS_FORCE_INCLUDE $(SolutionDir)src/pch.h)严格来说VS_PREPROCESS_FORCE_INCLUDE这个属性是CMake 3.14新增的它本质上是给MSVC加/FI参数——强制包含指定头文件。所以即使你是3.14.2也可以用类似思路给MSVC开预编译头编译选项加/Yupch.h源文件加/FIpch.hpch.cpp加/Ycpch.h产出.pch文件。if(MSVC) target_compile_options(my_app PRIVATE /Yu\pch.h\) target_compile_options(my_app PRIVATE /FI\pch.h\) endif()这种手动方式比target_precompile_headers繁琐但它是老版本上最接近官方体验的做法。如果这个工程未来有条件升级到3.20以上的版本你再一次性切到target_precompile_headers也不迟——毕竟那个方案跨编译器一致性更高Clang和GCC也能用。3.2 CUDA编译器探测失败问题热搜词里有一条非常典型的cmake error: cmake_cuda_compiler not set, after enablelanguage。这个问题在3.14.2上出现的频率相当高尤其是在只装了CUDA Toolkit但没用VS装CUDA组件或者CUDA版本和VS工具集版本不匹配的情况下。CMake启用CUDA的方式很简单project(my_cuda_app LANGUAGES CXX CUDA)但Windows上CMake找CUDA编译器nvcc不是凭空的它要能同时探测到VS的C工具链。因为nvcc在Windows上本质上还是调用cl.exe做宿主编译如果cl.exe不在当前环境中CMake就算找到了nvcc也过不了最终的检测。我那次遇到的报错细节是CUDA Toolkit 10.1 VS2019v142工具集CMake 3.14.2在Configure阶段报CMAKE_CUDA_COMPILER not set。排查链路是这样的先确认nvcc -V能正常输出版本——OK再确认cl.exe在PATH——不在启动x64 Native Tools Command Prompt后重新Configure——依然报错。到这一步基本能判断是VS工具集兼容性问题CUDA 10.1对VS2019 v142的支持还很差需要给CMake指定一个它能信任的-T工具集版本。我用-T v142不行换成-T version14.21VS2019早期版本也不行。最后还是回到根上——升级CUDA到10.1 update2问题直接消失。注意如果你在Windows上做CUDA CMake强烈建议先查一下CUDA Toolkit对应版本的Release Notes里是否明确支持你当前用的VS版本。这比在CMakeLists里折腾任何set(CMAKE_CUDA_COMPILER ...)都靠谱。3.3 MPIMS-MPI的引入方式另外一个热门词是cmake 引入mpi。Linux上找MPI通常靠find_package(MPI)就能自动定位OpenMPI或MPICHWindows上事情就没那么顺——最常见的是mpicc不在PATH里或者CMake找不到MS-MPI的头文件目录。MS-MPI装好后默认头文件在C:\Program Files (x86)\Microsoft SDKs\MPI库文件在C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x64。CMake的FindMPI模块在3.14上对Windows的支持还比较原始经常出现找到了mpiexec.exe却找不到mpi.h的情况。一个比较稳的写法是手动指定MPI路径find_package(MPI REQUIRED) if(MPI_CXX_FOUND) include_directories(${MPI_CXX_INCLUDE_PATH}) target_link_libraries(my_app ${MPI_CXX_LIBRARIES}) endif()如果find_package(MPI)失败回退方案是直接手动指定set(MPI_CXX_INCLUDE_PATH C:/Program Files (x86)/Microsoft SDKs/MPI/Include) set(MPI_CXX_LIBRARIES C:/Program Files (x86)/Microsoft SDKs/MPI/Lib/x64/msmpi.lib)用MSVC编译MPI程序还有一个老生常谈的坑因为MS-MPI的函数符号既有32位也有64位你要是用-A x64生成64位工程链接的必须是Lib/x64下的库用32位配置的就必须切到Lib目录下的x86库。混着用会出现一堆无法解析的外部符号。4. 从3.14.2出发的环境变量与日常排错4.1 “3.1.3...3.26 or higher is required. you are running version 2.8.12.2”这类报错的根因搜这个词的人大概率是装了某个软件包它的CMakeLists写着cmake_minimum_required(VERSION 3.1.3)但更高层的依赖又要求3.26而系统里实际跑的是2.8.12.2。这个报错信息拆开看有三层意思第一你的CMake版本确实太老。2.8.12.2这个版本在2013年左右发布的很多新语法完全不支持比如target_link_libraries的PRIVATE/PUBLIC关键字、if(TARGET)等。第二你的PATH里有一个老版本CMake抢在了新版本前面。很多人电脑上装过Anaconda或某些老软件它们自带一个CMake并且把自己的路径排在了系统路径前面。你敲cmake --version看到的版本未必是你在应用列表里看到已安装的那个版本。第三如果你明确用的是3.14.2这个zip版本但报错还是2.8.12.2几乎可以断定是PATH顺序问题。排查手法很简单先看where cmake找出所有cmake.exe的路径找到那个老版本所在的目录常见于Anaconda3\Library\bin或者某个软件的内嵌目录然后调整环境变量把cmake-3.14.2-win64-x64\bin提到最前面或者直接把老目录从PATH中去掉。改完记得新开一个终端再验证Windows的环境变量不会自动刷新到已打开的窗口。4.2 如何验证PATH里的cmake真的指向你想要的版本这里分享一个我经常用的验证组合where cmake cmake --version第一条命令确认路径第二条命令确认版本。如果你在命令行工具里用的是PowerShell也可以改用Get-Command cmake信息更丰富。要是发现两条命令输出的路径对不上就是PATH解析的优先级问题。Windows的PATH是从前往后找第一个匹配的所以早期安装的软件如果把旧CMake路径插在前面你后面的新版本就永远不会被用到。还有一个隐藏问题如果你同时安装了.msi版和.zip版包的安装器可能会在注册表里写入App Paths这会让某些GUI工具绕过PATH直接找到老版本。不过在用CMake命令行和VS集成的场景下PATH还是最核心的解析逻辑。4.3 CMAKE_BUILD_PARALLEL_LEVEL 和 CPU核数最后顺手提一个关于构建速度的小技巧因为Windows上用CMake VS生成器很多人会奇怪为什么Configure那么慢、Build也不如Linux下顺畅。Visual Studio生成器默认构建是并行调度的但你如果跑到命令行里用cmake --build . --config Release可以指定并行级别cmake --build . --config Release -- /m:8/m:8会直接透传给MSBuild限制并行编译的进程数。或者设置环境变量CMAKE_BUILD_PARALLEL_LEVEL8效果类似。这个环境变量是3.12开始支持跨生成器的3.14.2完全适用。对于16核机器配置不高的场景设成/m:8往往比默认全部核心都拉满更稳定内存占用也更可控。5. 排错链路复盘Viewer模式去拆一个典型报错这里我想把排查过程完整还原一遍因为我发现很多人遇到问题喜欢直接搜报错名的解决方案但往往忽略为什么这个问题会走到这一步。我拿最近的经历做示例。5.1 现场新装的CMake 3.14.2配置VS2019工程时意外失败当时我在一台新机器上配置一个老项目环境是Windows 10 x64 CMake 3.14.2 zip版 VS2019 Community。命令行执行cmake -G Visual Studio 16 2019 -A x64 ..预期的行为是CMake找到VS自动配上MSVC生成sln。实际输出出现了CMake Error at CMakeLists.txt:4 (project): The CMAKE_CXX_COMPILER: cl.exe is not a full path to an existing compiler tool.这台机器是新装的VSVS Build Tools也装了。第一反应是把Command Prompt换成x64 Native Tools Command Prompt再跑。但这台机器上只要先跑vcvars64.bat再执行CMake问题确实消失了给出的完整错误路径也正常了。到这里很多人的处理方式就是以后都在开发者命令行里跑CMake。但从工程化角度这样不够稳因为CI和脚本环境不一定会预置vcvars。于是继续排查发现另一个细节直接用普通cmd跑报的cl.exe不是完整路径——这是CMake在未找到cl.exe时的模糊提示换成开发者命令行后报错消失表示VS生成器在开发者环境里能正确继承编译器信息。5.2 为什么之前老机器上没这个问题对比另一台老机器发现它装了VS2019 CMake 3.14.2 msi版并且安装.msi时勾选了添加到系统PATH。新版zip版没动PATH导致普通cmd启动时继承了系统PATH里的一切但少了VS提供的临时PATH块。所以问题的根源不在CMake而在VS环境变量的初始化。对于这种情况稳妥做法是要么规范流程统一在开发者命令行里跑CMake配置要么用CMake的-DCMAKE_CXX_COMPILERcl的完整路径锁死编译器。但锁死路径在VS升级后可能失效所以我通常不推荐后者而是倾向于封装一个vcx64.bat调用链把vcvars64.bat和cmake命令放在同一个脚本里跑。这样任何人拿到脚本都能复现不依赖人工记忆。5.3 经验沉淀Windows上CMake排错的三板斧这个例子沉淀下来我在Windows上排CMake问题基本就是三板斧先看where cmake确认用的哪个版本再确认当前终端是否初始化了编译器环境echo %VCToolsInstallDir%看看有没有值最后用cmake -LAH看缓存变量确认CMake实际探测到的编译器路径、架构和工具集信息。这三个动作做完八成的问题都能定位出是环境问题还是工程配置问题。6. 一点个人体会CMake在Windows上的体验说到底是环境感知的问题。Linux下编译器位置基本固定PATH天然干净Windows下编译器、架构、SDK版本、工具集版本各有各的安装逻辑CMake夹在中间只能靠生成器和开发者共同创造条件。3.14.2这个版本确实不是最新的但在VS2019Windows x64这套组合下它的稳定性和社区经验沉淀是我目前认为性价比最高的。如果你打算长期在Windows上做C开发我的建议是别盲目追新也别固守太旧的版本。遇到版本太老导致不支持某条命令的坑先查一下当前版本的手册遇到新版本行为变化的坑先看Release Notes。CMake升级的断裂感主要来自语法糖核心概念十几年来一直稳定——所以先把target_*、生成器表达式、toolchain文件这几块吃透版本更替就不会困扰你了。最后再分享一个小技巧cmake-3.14.2-win64-x64的bin目录里其实不止cmake.exe还有cmake-gui.exe和cpack.exe、ctest.exe。新手可以多打开cmake-gui看看自动探测到的各种字段——它能实时展示生成器、编译器、缓存变量之间的关系这个直观感受比看十篇教程都有用。配置完一遍GUI再回到命令行手写你会发现整个思维通了。本文还有配套的精品资源点击获取
返回列表