- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
导读
FindMFC是 CMake 官方提供的查找模块(位于 Modules/FindMFC.cmake),用于在 Windows 平台检测 Visual Studio 环境中是否安装了 Microsoft Foundation Class Library(MFC),并通过find_package(MFC)与MFC_FOUND变量让构建脚本据此决定是否启用 MFC 相关目标。本文以该模块的官方文档(Help/module/FindMFC.rst)为主体,结合模块源码与 Visual Studio 生成器实现,讲解其使用方法、结果变量、底层检测原理及配套的CMAKE_MFC_FLAG变量,帮助读者在 CMake 工程中正确集成 MFC 应用。
模块概览:FindMFC 解决什么问题
MFC(Microsoft Foundation Class Library)是微软提供的 C++ 类库,用于在 Windows 上快速开发图形界面应用。它并非编译器自带的运行时组件,而是 Visual Studio 的可选安装组件——模块文档在 Modules/FindMFC.cmake 的.. note::中特别强调:
MFC is an optional component in Visual Studio and must be installed separately for this module to succeed.
即:如果安装 Visual Studio 时未勾选 "MFC 组件",那么find_package(MFC)将无法成功。这一点决定了该模块的检测语义——它本质上是在验证"开发环境中是否具备编译 MFC 程序的条件"。
模块的使用入口非常简洁:
find_package(MFC [...])一旦 MFC 的库与头文件被找到,无需手动追加链接库,因为 MFC 库(如uafxcw.lib、mfc140u.lib等)属于开发环境的一部分,由 Visual Studio 生成器在生成项目文件时自动处理。这正是FindMFC与一般Find*.cmake模块的关键差异:它不返回具体的库路径变量,只返回一个布尔结果。
结果变量:MFC_FOUND
FindMFC模块只定义了一个对外结果变量:
| 变量 | 类型 | 含义 |
|---|---|---|
MFC_FOUND | 布尔 | 是否检测到 MFC 支持 |
源码 Modules/FindMFC.cmake 中,模块初始化时先假定不支持:
# Assume no MFC support set(MFC_FOUND "NO")随后只有当检测确认具备 MFC 支持时才将其置为"YES"。调用方通过if(MFC_FOUND)即可分支处理启用或禁用 MFC 的构建逻辑。
实战用法:启用 MFC 的典型示例
模块文档 Modules/FindMFC.cmake 给出了一个完整的实战示例——先检查应用能否链接 MFC 库,再据此配置目标:
find_package(MFC) if(MFC_FOUND) # Example logic when MFC is available... set(CMAKE_MFC_FLAG 2) add_executable(app WIN32 main.cpp) target_compile_definitions(app PRIVATE _AFXDLL) endif()这段代码包含三个关键动作,逐一拆解:
find_package(MFC):触发模块执行平台检测。由于模块内部使用try_compile做真实编译验证,首次配置会耗时数秒,结果会被缓存(见下文"底层实现")。set(CMAKE_MFC_FLAG 2):告诉生成器当前目标使用共享(动态链接)MFC 库。CMAKE_MFC_FLAG是 CMake 的全局缓存变量,其取值语义在 Help/variable/CMAKE_MFC_FLAG.rst 中有明确定义:1—— 静态 MFC 库(Static MFC)2—— 共享 MFC 库(Shared MFC,即动态链接,配合_AFXDLL)- 不设置或空值 —— 不使用 MFC
target_compile_definitions(app PRIVATE _AFXDLL):为可执行目标追加_AFXDLL宏定义。文档(Help/variable/CMAKE_MFC_FLAG.rst)明确指出:Visual Studio 生成器会根据CMAKE_MFC_FLAG的值自动添加该标志,但 "Make" 系生成器(如 NMake、MinGW Makefiles 等)必须手动指定。_AFXDLL宏告诉 MFC 头文件按动态链接版本展开声明,与CMAKE_MFC_FLAG 2必须成对出现。
静态 / 共享两种变体的选择
实践中两种常见组合:
# 组合一:静态链接 MFC set(CMAKE_MFC_FLAG 1) add_executable(app WIN32 main.cpp) # 静态 MFC 不需要 _AFXDLL,且不可在 MFC 与非 MFC 模块间混链 DLL # 组合二:动态链接 MFC(最常用,便于 DLL 分发) set(CMAKE_MFC_FLAG 2) add_executable(app WIN32 main.cpp) target_compile_definitions(app PRIVATE _AFXDLL)CMAKE_MFC_FLAG还支持在值中使用生成器表达式,例如按配置区分:
set(CMAKE_MFC_FLAG "$<$<CONFIG:Debug>:2>$<$<CONFIG:Release>:2>")这符合 Help/variable/CMAKE_MFC_FLAG.rst 中"Contents ofCMAKE_MFC_FLAGmay use generator expressions"的约定。
底层实现:模块是如何检测 MFC 的
FindMFC的检测逻辑全部集中在 Modules/FindMFC.cmake,核心思路是用try_compile真实编译一段包含 MFC 头文件的代码,以编译是否成功作为存在性判据。
第一步:平台预筛选
set(MFC_ATTEMPT_TRY_COMPILE 0) if(WIN32 AND NOT UNIX AND NOT BORLAND AND NOT MINGW) set(MFC_ATTEMPT_TRY_COMPILE 1) endif()MFC 只在微软官方工具链下存在,因此模块先排除三类平台/工具链:
UNIX(含 Cygwin 等模拟环境)BORLAND(Borland 编译器)MINGW(MinGW 工具链,其windows.h体系不包含 MFC)
只有WIN32且非上述三者时才进入try_compile阶段。这也意味着模块在非 Windows 平台会立即返回MFC_FOUND = NO。
第二步:构造检测源码并缓存结果
if(NOT DEFINED MFC_HAVE_MFC) set(CHECK_INCLUDE_FILE_VAR "afxwin.h") file(READ ${CMAKE_ROOT}/Modules/CheckIncludeFile.cxx.in _CIF_SOURCE_CONTENT) string(CONFIGURE "${_CIF_SOURCE_CONTENT}" _CIF_SOURCE_CONTENT) message(CHECK_START "Looking for MFC") ... endif()检测样本取自 Modules/CheckIncludeFile.cxx.in,该模板内容为:
#include <${CHECK_INCLUDE_FILE_VAR}> int main() { return 0; }模块将CHECK_INCLUDE_FILE_VAR配置为afxwin.h(MFC 的入口头文件),于是实际编译的源码等价于:
#include <afxwin.h> int main() { return 0; }try_compile结果存放在MFC_HAVE_MFC这个CACHE INTERNAL变量中(set(MFC_HAVE_MFC 1 CACHE INTERNAL "Have MFC?")),后续配置过程直接复用缓存,不会反复编译。
第三步:依次尝试共享与静态两种链接模式
try_compile(MFC_HAVE_MFC SOURCE_FROM_VAR CheckIncludeFile.cxx _CIF_SOURCE_CONTENT CMAKE_FLAGS -DCMAKE_MFC_FLAG:STRING=2 -DCOMPILE_DEFINITIONS:STRING=-D_AFXDLL OUTPUT_VARIABLE OUTPUT) if(NOT MFC_HAVE_MFC) try_compile(MFC_HAVE_MFC SOURCE_FROM_VAR CheckIncludeFile.cxx _CIF_SOURCE_CONTENT CMAKE_FLAGS -DCMAKE_MFC_FLAG:STRING=1 OUTPUT_VARIABLE OUTPUT) endif()这里体现了模块设计上的一个精妙之处:先按共享模式(CMAKE_MFC_FLAG=2+-D_AFXDLL)编译,失败后再回退到静态模式(CMAKE_MFC_FLAG=1)。源码注释(Modules/FindMFC.cmake)解释原因:"Try both shared and static as the root project may have set the /MT flag"——如果顶层项目已经设置了/MT(多线程静态运行时)等标志,静态 MFC 库路径可能才是可链接的,因此必须双路探测。也就是说,只要任何一种链接模式下afxwin.h能编译通过,就判定 MFC 存在。
最终根据两次尝试的结果输出诊断信息:
if(MFC_HAVE_MFC) message(CHECK_PASS "found") else() message(CHECK_FAIL "not found") endif()配置日志中会出现形如-- Looking for MFC - found或-- Looking for MFC - not found的输出,便于用户快速定位。
生成器侧联动:CMAKE_MFC_FLAG 如何落到项目文件中
FindMFC之所以"找到即用、无需手动链接",是因为 Visual Studio 生成器在写出项目文件时会读取CMAKE_MFC_FLAG,并把 MFC 配置写入生成的工程中。
VS2010 及以后(.vcxproj,msbuild 格式)
在 Source/cmVisualStudio10TargetGenerator.cxx 的WriteMSToolConfigurationValues中,生成器先对CMAKE_MFC_FLAG求值(支持生成器表达式):
cmValue mfcFlag = this->Makefile->GetDefinition("CMAKE_MFC_FLAG"); if (mfcFlag) { std::string const mfcFlagValue = cmGeneratorExpression::Evaluate(*mfcFlag, this->LocalGenerator, config); std::string useOfMfcValue = "false"; if (this->GeneratorTarget->GetType() <= cm::TargetType::OBJECT_LIBRARY) { if (mfcFlagValue == "1"_s) { useOfMfcValue = "Static"; } else if (mfcFlagValue == "2"_s) { useOfMfcValue = "Dynamic"; } } e1.Element("UseOfMfc", useOfMfcValue); }即CMAKE_MFC_FLAG=1时向.vcxproj写入<UseOfMfc>Static</UseOfMfc>,=2时写入<UseOfMfc>Dynamic</UseOfMfc>。Visual Studio 的 msbuild 引擎看到该属性后会自动添加 MFC 头文件搜索路径与库链接,无需 CMake 侧再手动target_link_libraries。
更早的 VS7(.vcproj 格式)
在 Source/cmLocalVisualStudio7Generator.cxx 中,生成器读取同一变量并写出<UseOfMFC=...>属性,未设置时默认按"0"(不使用 MFC)处理。两条生成器路径共享同一个缓存变量,保证了"找到 MFC → 设置标志 → 生成器自动接线"的完整闭环。
变量注册信息
CMAKE_MFC_FLAG还被登记在缓存变量文档表中,见 Source/cmCacheDocumentationTable.cxx,其描述与 Help/variable/CMAKE_MFC_FLAG.rst 完全一致,确保cmake-gui中也能显示该变量的用途说明。
完整工作流:把 FindMFC 融入真实工程
将上述内容整合为一个可落地的完整片段:
cmake_minimum_required(VERSION 3.16) project(MfcApp) # 1) 检测 MFC find_package(MFC) # 2) 按检测结果分支 if(MFC_FOUND) message(STATUS "MFC detected, building with MFC support") # 3) 选择共享 MFC(2)或静态 MFC(1) set(CMAKE_MFC_FLAG 2) add_executable(mfc_app WIN32 src/main.cpp) # 4) 动态 MFC 需要 _AFXDLL;Makefile 系生成器必须显式给出 target_compile_definitions(mfc_app PRIVATE _AFXDLL) # 5) MFC 库由生成器自动链接,无需 target_link_libraries else() message(WARNING "MFC not found: build an alternative (non-MFC) variant") add_executable(mfc_app src/main.cpp) endif()几个使用要点总结:
- 首次配置开销:模块内部
try_compile需要启动一次真实的编译验证,配置时间略长;结果写入MFC_HAVE_MFC缓存后,后续配置直接命中缓存,不再重复编译。 - 平台限定:模块只在
WIN32(且非 Borland/MinGW)下执行检测,跨平台工程应始终用if(MFC_FOUND)包裹 MFC 相关目标,避免非 Windows 构建失败。 WIN32可执行属性:示例中add_executable(app WIN32 ...)的WIN32关键字使程序以 GUI 子系统(无控制台窗口)方式链接,是 MFC 桌面应用的常规写法。- VS 组件完整性:若
MFC_FOUND为NO,优先检查 Visual Studio Installer 中是否安装了 "适用于最新 v143 生成工具的 C++ MFC" 组件,这是模块文档明确指出的成功前提。
延伸阅读
- 模块官方文档:Help/module/FindMFC.rst
- 模块完整实现:Modules/FindMFC.cmake
- 配套变量文档:Help/variable/CMAKE_MFC_FLAG.rst
- 检测源码模板:Modules/CheckIncludeFile.cxx.in
- 生成器消费该变量:Source/cmVisualStudio10TargetGenerator.cxx、Source/cmLocalVisualStudio7Generator.cxx
- 缓存变量注册:Source/cmCacheDocumentationTable.cxx
- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
相关推荐
CMake FindALSA 模块详解:在项目中定位与链接 ALSA (asound) 库
CMake FindALSA 模块详解:在项目中定位与链接 ALSA asound 库 导读 本文基于 CMake 仓库中的 FindALSA 模块 https
构建工具开发工具CLICMake-Cookbook项目实战:检测与链接Boost库详解
CMake Cookbook项目实战:检测与链接Boost库详解 引言 在现代C++开发中,Boost库作为标准库的重要补充,提供了大量实用组件。本文将基于CM
文档教程CMake 模块 FindCURL 详解:在项目中定位与链接 libcurl 的完整指南
CMake 模块 FindCURL 详解:在项目中定位与链接 libcurl 的完整指南 导读 本指南以 CMake 官方模块 FindCURL https:/
构建工具开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考