资讯动态

CMake FortranCInterface 模块深度解析:C 与 Fortran 混合编程的符号名修饰(Name Mangling)自动检测与头文件生成

发布时间:2026/10/9 12:13:08 来源:尧图企业网站定制
构建工具开发工具CLI【免费下载链接】CMakeMirror of CMake upstream repository项目地址https://gitcode.com/gh_mirrors/cm/CMake点击查看免费下载FortranCInterface 是 CMake 内置的核心模块负责自动检测当前工具链下 Fortran 与 C/C 交互时使用的符号名修饰Name Mangling规则并生成可直接在 C 源码中使用的头文件宏从而让 C 侧无需关心 Fortran 编译器是 GNU、Intel、PGI 还是其他厂商。本文以 Modules/FortranCInterface.cmake 的官方文档为骨架结合 Modules/FortranCInterface/ 目录下的检测工程源码完整讲解该模块的变量体系、FortranCInterface_HEADER与FortranCInterface_VERIFY两个命令的用法以及底层符号解析与宏构造的完整原理。读完本文你将能在自己的混合语言项目里直接复用它生成可靠的跨编译器 C 头文件。一、为什么需要 FortranCInterfaceFortran 符号修饰问题Fortran 编译器在生成 C 可链接的符号名时没有统一标准有的在子程序名后追加一个下划线mysub_GNU/Intel有的前后加双下划线my_sub__GNU g77有的全部大写MYSUBWindows 上的 Intel有的用模块前缀__mymodule_MOD_mysubGNU 4.3还有$分隔HP、Cray等五花八门的规则。若在 C 源码里硬编码某个特定编译器的修饰名项目一旦更换 Fortran 编译器就会链接失败。FortranCInterface 模块解决的正是在配置阶段自动探测当前 Fortran 编译器的修饰规则并将结果以 C 预处理器宏的形式固化到生成的头文件中使 C 代码始终以统一的宏调用方式访问 Fortran 例程。模块加载与前置条件在 CMake 工程中通过一行命令加载include(FortranCInterface)从 Modules/FortranCInterface.cmake 的源码可以看到模块加载后有明确的硬性前置条件它要求当前工程已经启用C 和 Fortran 两种语言project(... C Fortran)否则直接以FATAL_ERROR终止foreach(lang C Fortran) if(NOT CMAKE_${lang}_COMPILER_LOADED) message(FATAL_ERROR FortranCInterface requires the ${lang} language to be enabled.) endif() endforeach()加载后模块把FortranCInterface_SOURCE_DIR指向 Modules/FortranCInterface 目录并在${CMAKE_BINARY_DIR}/CMakeFiles/FortranCInterface下创建/复用检测工程最终通过include(.../Output.cmake)把检测结果变量读入当前作用域。模块通过if(FortranCInterface_SOURCE_DIR) return()保证在同一个工程中只执行一次检测逻辑。二、检测流程一个小型 CFortran 探针工程如何自曝符号模块核心的检测动作并不神秘它用try_compile构建一个内含 C 与 Fortran 的迷你工程源码见 Modules/FortranCInterface/CMakeLists.txt工程生成的可执行文件内部埋着各种可能的修饰名符号然后 CMake 读取该可执行文件中的字符串反向解析出真实的修饰规则。完整驱动逻辑在 Modules/FortranCInterface/Detect.cmake。2.1 探针符号的注入检测工程会按symbol.c.in模板为每个候选修饰名生成一个 C 函数const char* symbol(void) { return INFO:symbol[symbol]; }即每个候选符号对应的 C 函数体内都嵌有一段INFO:symbol[...]标记字符串模板见 Modules/FortranCInterface/symbol.c.in。全局符号候选列表覆盖了各主流编译器见 Modules/FortranCInterface/CMakeLists.txtset(global_symbols my_sub # VisualAge my_sub_ # GNU, Intel, HP, SunPro, PGI my_sub__ # GNU g77 MY_SUB # Intel on Windows mysub # VisualAge mysub_ # GNU, Intel, HP, SunPro, PGI MYSUB # Intel on Windows ${FortranCInterface_GLOBAL_SYMBOLS} )模块符号候选列表同样按编译器厂商注释Modules/FortranCInterface/CMakeLists.txtset(module_symbols __my_module_MOD_my_sub # GNU 4.3 __my_module_NMOD_my_sub # VisualAge __my_module__my_sub # GNU 4.2 __mymodule_MOD_mysub # GNU 4.3 ... my_module_mp_my_sub_ # Intel MY_MODULE_mp_MY_SUB # Intel on Windows mymodule$mysub # HP mysub$mymodule_ # Cray _QMmy_modulePmy_sub # LLVMFlang __module_my_module_my_sub # LFortran ${FortranCInterface_MODULE_SYMBOLS} )值得注意的边界处理$字符C 标准不允许$出现在标识符中检测工程会先用check_source_compiles探测当前 C 编译器是否支持$不支持则跳过含$的候选符号Modules/FortranCInterface/CMakeLists.txt 与 L80-L93。模块符号仅当 Fortran 编译器支持 F90 时才启用if(CMAKE_Fortran_COMPILER_SUPPORTS_F90)否则清空module_symbols。部分编译器的修饰规则根本无法从 C 侧调用例如 SunPro 的my_module.my_sub_、PathScale 的MY_SUB.in.MY_MODULE源码中明确注释了这些例外Modules/FortranCInterface/CMakeLists.txt。Fortran 侧由mysub.f、my_sub.f及mymodule.f90、my_module.f90等文件提供真实符号如 Modules/FortranCInterface/mymodule.f90 定义module mymodule并声明module procedure mysubC 侧symbols静态库链接 Fortran 静态库myfort最终可执行文件FortranCInterfaceModules/FortranCInterface/main.F调用call_sub/call_mod保证所有符号都被链接进产物且用!DIR$ NOINLINE阻止编译器内联优化掉调用Modules/FortranCInterface/call_sub.f。能实际链接成功的候选符号其INFO:symbol[...]字符串就会出现在可执行文件里。2.2 正则解析从符号反推修饰规则检测阶段Modules/FortranCInterface/Detect.cmake用file(STRINGS ... REGEX INFO:[A-Za-z0-9_]\\[[^]]*\\])从可执行文件中提取至多 8 个INFO:symbol[...]标记然后用四组正则分别匹配set(_global_regex ^(_*)(mysub|MYSUB)([_$]*)$) set(_global__regex ^(_*)(my_sub|MY_SUB)([_$]*)$) set(_module_regex ^([A-Za-z_$]*)(mymodule|MYMODULE)([A-Za-z_$]*)(mysub|MYSUB)([_$]*)$) set(_module__regex ^([A-Za-z_$]*)(my_module|MY_MODULE)([A-Za-z_$]*)(my_sub|MY_SUB)([_$]*)$)以及两套符号在前、模块在后的反向正则Cray 编译器把符号放在模块名之前从而支持MODULE_THEN_SYMBOL与SYMBOL_THEN_MODULE两种顺序。匹配成功的符号被拆解为 PREFIX / MIDDLE / SUFFIX / CASE 等组成部分并据此构造宏Modules/FortranCInterface/Detect.cmake。检测完成后打印总结信息例如-- Detecting Fortran/C Interface -- Found GLOBAL and MODULE mangling结果写入${CMAKE_BINARY_DIR}/CMakeFiles/FortranCInterface/Output.cmake供模块加载时include模板见 Modules/FortranCInterface/Output.cmake.in。检测只在首次配置或相关输入变化时重新执行Detect.cmake通过比较 Output.cmake 与 Input.cmake、模板及自身源码的时间戳决定是否跳过。三、结果变量如何读取检测到的修饰规则3.1 结果变量Result Variablesinclude(FortranCInterface)之后以下两个布尔变量标识修饰规则是否被识别变量含义FortranCInterface_GLOBAL_FOUND全局子程序和函数global subroutines/functions的修饰规则是否已知FortranCInterface_MODULE_FOUND模块子程序和函数由MODULE PROCEDURE声明的修饰规则是否已知两者都需要对应范畴的无下划线与带下划线两类符号同时被识别才置 1见 Modules/FortranCInterface/Detect.cmake。典型结果是GLOBAL找到而MODULE未找到例如不支持 F90 的编译器此时FortranCInterface_MODULE_FOUND0。3.2 输入/检测变量Input Variables以下变量描述检测到的修饰规则细节常规用法不需要直接引用它们直接用后面的命令即可但理解它们有助于读懂生成的宏全局符号global无下划线形式变量说明FortranCInterface_GLOBAL_PREFIX无下划线全局符号的前缀FortranCInterface_GLOBAL_SUFFIX无下划线全局符号的后缀FortranCInterface_GLOBAL_CASE大小写形式UPPER或LOWER全局符号global带下划线形式变量说明FortranCInterface_GLOBAL__PREFIX带下划线全局符号的前缀FortranCInterface_GLOBAL__SUFFIX带下划线全局符号的后缀FortranCInterface_GLOBAL__CASE大小写形式UPPER或LOWER模块符号module无下划线形式变量说明FortranCInterface_MODULE_PREFIX无下划线模块符号的前缀FortranCInterface_MODULE_MIDDLE出现在模块名与符号名之间的中间部分FortranCInterface_MODULE_SUFFIX无下划线模块符号的后缀FortranCInterface_MODULE_CASE大小写形式UPPER或LOWERFortranCInterface_MODULE_ORDER组件顺序CMake 4.1 起MODULE_THEN_SYMBOLPREFIXmoduleMIDDLEsymbolSUFFIX或SYMBOL_THEN_MODULEPREFIXsymbolMIDDLEmoduleSUFFIX模块符号module带下划线形式对应变量为FortranCInterface_MODULE__PREFIX、FortranCInterface_MODULE__MIDDLE、FortranCInterface_MODULE__SUFFIX、FortranCInterface_MODULE__CASE、FortranCInterface_MODULE__ORDER语义同上。此外Output.cmake.in 还会回填每个范畴实际识别出的原始符号FortranCInterface_GLOBAL_SYMBOL等和可直接展开的宏体FortranCInterface_GLOBAL_MACRO等。3.3 扩展修饰规则FortranCInterface_GLOBAL_SYMBOLS/FortranCInterface_MODULE_SYMBOLS模块内置的候选列表无法覆盖未来或小众编译器因此提供两个在include(FortranCInterface)之前设置的列表变量用于追加候选修饰名针对MySub、My_Sub、MyModule:MySub、My_Module:My_Sub四类符号set(FortranCInterface_GLOBAL_SYMBOLS mysub_ my_sub__ MYSUB_) # ^^^^^ ^^^^^^ ^^^^^ set(FortranCInterface_MODULE_SYMBOLS __mymodule_MOD_mysub __my_module_MOD_my_sub) # ^^^^^^^^ ^^^^^ ^^^^^^^^^ ^^^^^^ include(FortranCInterface)示例中的^仅为标注原始符号名位置实际书写不需要。这两个变量会被 Modules/FortranCInterface/Input.cmake.in 透传给检测工程追加进global_symbols/module_symbols候选列表并参与符号解析。可以推断这是扩展模块以支持新编译器的最主要途径。四、FortranCInterface_HEADER一键生成 C 头文件宏这是模块最常用的命令作用是根据检测结果生成一个包含符号修饰宏的 C 头文件。命令签名FortranCInterface_HEADER( file [MACRO_NAMESPACE macro-ns] [SYMBOL_NAMESPACE ns] [SYMBOLS [module:]function ...] )生成的file默认位于${CMAKE_CURRENT_BINARY_DIR}绝对路径则原样使用见 Modules/FortranCInterface.cmake内容基于 Modules/FortranCInterface/Macro.h.in 模板用configure_file展开。头文件会定义四类宏#define FortranCInterface_GLOBAL (name,NAME) ... #define FortranCInterface_GLOBAL_(name,NAME) ... #define FortranCInterface_MODULE (mod,name, MOD,NAME) ... #define FortranCInterface_MODULE_(mod,name, MOD,NAME) ...四类宏分别对应四类 Fortran 符号的修饰宏对应 Fortran 调用形式GLOBAL无下划线的全局符号call mysub()GLOBAL_带下划线的全局符号call my_sub()MODULE无下划线的模块符号use mymod; call mysub()MODULE_带下划线的模块符号use mymod; call my_sub()如果某一类别的修饰规则未被检测到对应宏不会被定义生成逻辑见 Modules/FortranCInterface.cmake调用方代码需用#ifdef防护。所有宏要求同时传入小写与大写两种形式的原始名宏体内部按检测到的CASE选用name或NAME。4.1 三个选项详解MACRO_NAMESPACE macro-ns替换默认的FortranCInterface_前缀。例如MACRO_NAMESPACE FC_生成FC_GLOBAL()、FC_GLOBAL_()、FC_MODULE()、FC_MODULE_()。SYMBOL_NAMESPACE ns为SYMBOLS生成的每个预处理器定义加上统一前缀。SYMBOLS给出需要自动修饰的符号列表为每个符号生成#definefunction #define nsfunction ... module:function #define nsmodule_function ...如果某个符号的修饰规则未知则不生成定义并输出AUTHOR_WARNING警告No FortranCInterface mangling known for ...见 Modules/FortranCInterface.cmake。从源码看SYMBOLS中的符号按是否含_自动归类到带下划线/不带下划线形式if(${f} MATCHES _)含:的条目按module:function拆分为模块符号随后直接复用对应的GLOBAL/MODULE宏生成一行#define。4.2 生成的宏展开效果以 GNU gfortran 为例检测到的典型规则为mysub_、my_sub_、__mymodule_MOD_mysub、__my_module_MOD_my_sub默认命名空间下宏展开约等价于#define FortranCInterface_GLOBAL(name,NAME) name##_ #define FortranCInterface_GLOBAL_(name,NAME) name##_ #define FortranCInterface_MODULE(mod_name,name, mod_NAME,NAME) __##mod_name##_MOD_##name##_ #define FortranCInterface_MODULE_(mod_name,name, mod_NAME,NAME) __##mod_name##_MOD_##name##_实际宏体由 Modules/FortranCInterface/Detect.cmake 依据解析出的 PREFIX/SUFFIX/MIDDLE/CASE/ORDER 动态拼接含##连接符因此不同编译器下完全自适应。4.3 基本用法示例示例 1生成仅含四类修饰宏的头文件FC.hinclude(FortranCInterface) FortranCInterface_HEADER(FC.h MACRO_NAMESPACE FC_)示例 2在宏之外再为具体符号生成自动修饰定义FC_mysub和FC_mymod_my_subinclude(FortranCInterface) FortranCInterface_HEADER( FCMangle.h MACRO_NAMESPACE FC_ SYMBOL_NAMESPACE FC_ SYMBOLS mysub mymod:my_sub )C 侧用法示意生成头文件后 include 并调用#include FCMangle.h /* 调用全局子程序 mysub */ FC_GLOBAL(mysub, MYSUB)(); /* 调用模块 mymod 中的子程序 my_sub */ FC_MODULE(mymod, my_sub, MYMOD, MY_SUB)(); /* 或者直接使用 SYMBOLS 生成的符号定义 */ FC_mysub(); FC_mymod_my_sub();五、FortranCInterface_VERIFY编译期验证编译器互操作性Fortran 与 C 编译器即使各自正常组合在一起也可能因 ABI、运行时库等差异而链接失败。FortranCInterface_VERIFY用于在构建早期就验证这一组合可用避免错误在构建末期才爆发。签名FortranCInterface_VERIFY([CXX] [QUIET])5.1 行为说明不带CXX验证 Fortran 与 C 的组合带CXX验证 Fortran 与 C 的组合要求工程已启用 C。验证方式用try_compile构建 Modules/FortranCInterface/Verify 目录下的样例工程一个用 C 语言写main、链接 Fortran 静态库的小程序见 Modules/FortranCInterface/Verify/CMakeLists.txtC 模式下额外编译 VerifyCXX.cxx 并enable_language(CXX)。该样例工程内部同样会include(FortranCInterface)并生成头文件验证宏。结果存入缓存变量FortranCInterface_VERIFIED_C或FortranCInterface_VERIFIED_CXX布尔值同一配置只验证一次if(NOT DEFINED FortranCInterface_VERIFIED_${lang})短路。验证工程按Release配置构建set(CMAKE_TRY_COMPILE_CONFIGURATION Release)见 Modules/FortranCInterface.cmake。失败行为不带QUIET时命令以FATAL_ERROR终止构建错误信息包含 Fortran 编译器路径、C/C 编译器路径以及完整构建输出Modules/FortranCInterface.cmake带QUIET时仅记录失败结果由调用方自行处理。5.2 典型用法include(FortranCInterface) # 早期停止不兼容组合失败则报错退出 FortranCInterface_VERIFY() # 或者静默检查 C 组合由业务逻辑决定后续行为 FortranCInterface_VERIFY(CXX QUIET) if(NOT FortranCInterface_VERIFIED_CXX) message(WARNING Fortran/C combination not supported; skipping feature) endif()验证过程中的中间输出构建日志会被写入${FortranCInterface_BINARY_DIR}/Verify${lang}/output.txt便于排查失败原因Modules/FortranCInterface.cmake。六、平台与编译器适配细节源码级补充6.1 MinGW 路径特殊处理MinGW 的 make 工具无法正确处理路径中的()因此当检测工程目录含括号时模块会把 Modules/FortranCInterface 目录整体复制到${CMAKE_BINARY_DIR}/CMakeFiles/FortranCInterfaceMinGW后再使用Modules/FortranCInterface.cmake。6.2 macOS 多架构限制无论检测Detect.cmake还是验证FortranCInterface_VERIFY都会从CMAKE_OSX_ARCHITECTURES中只取第一个架构传给try_compile以避免重复输出信息并简化符号解析如 Modules/FortranCInterface/Detect.cmake。源码中以FIXME注释标明目前尚无把全部架构转发给try_compile的选项。6.3 GNU gfortran ≥ 12 的 IPO 规避GCC gfortran 12 及以上版本若启用 IPOLTO会混淆可执行文件中用于正则匹配的返回字符串导致符号解析失败。因此检测工程针对 GNU Fortran ≥ 12 做了专门处理给FortranCInterface目标加-fno-lto给静态库加-fltoauto -ffat-lto-objects非 Apple 平台既保证探测字符串不被混淆又确保验证环节作用于 IPO 对象Modules/FortranCInterface/CMakeLists.txt。6.4 模块初始化符号PGI 与 Cray≥ 7.3.2的 Fortran 编译器需要模块初始化符号因此检测工程额外提供mymodule_、my_module_、MYMODULE、MY_MODULE等 C 侧符号PathScale 除外因为其模块符号含.in.无法从 C 提供Modules/FortranCInterface/CMakeLists.txt。6.5 链接器语言验证工程中main由 C 定义因此不启用 C 时显式把VerifyFortranC目标的LINKER_LANGUAGE设为 C保证用 C 链接器完成链接Modules/FortranCInterface/Verify/CMakeLists.txt。七、完整实战模板把以上内容组合进一个真实的混合语言项目cmake_minimum_required(VERSION 3.28) project(NumLib VERSION 1.0 LANGUAGES C Fortran) # 1) 加载模块并启用 C 验证可选 include(FortranCInterface) FortranCInterface_VERIFY(CXX) # 2) 生成头文件定义 FC_ 命名空间的四类宏 # 并为具体符号生成 FC_foo / FC_bar_my_sub 定义 FortranCInterface_HEADER( ${CMAKE_CURRENT_BINARY_DIR}/FortranMangle.h MACRO_NAMESPACE FC_ SYMBOL_NAMESPACE FC_ SYMBOLS foo bar:my_sub ) # 3) 在 C/C 侧 include 该头文件并调用 add_executable(app main.c) target_include_directories(app PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(app PRIVATE numlib)对应 C 源码main.c#include FortranMangle.h /* 调用全局子程序 foo() */ FC_foo(); /* 调用模块 bar 中的 my_sub()宏内部自动按编译器规则修饰 */ FC_bar_my_sub(); /* 通用写法手工展开形式供没有 SYMBOLS 的场景使用 */ FC_GLOBAL(foo, FOO)(); FC_MODULE(bar, my_sub, BAR, MY_SUB)();在构建目录执行cmake .. cmake --build .配置阶段即可看到探测信息如-- Found GLOBAL and MODULE mangling与验证结果头文件按当前工具链自动生成切换编译器后重新配置即可无缝适配。八、小结与进一步探索FortranCInterface 的价值在于把Fortran 编译器符号修饰差异这一平台性问题从源码层彻底隔离检测期通过探针工程自动识别规则使用期通过 C 预处理器宏透明适配。关键结论速查使用前提工程必须启用 C 与 Fortran 两种语言结果判定FortranCInterface_GLOBAL_FOUND/FortranCInterface_MODULE_FOUND常用命令FortranCInterface_HEADER生成头文件FortranCInterface_VERIFY提前验证编译器互操作扩展机制在include前设置FortranCInterface_GLOBAL_SYMBOLS/FortranCInterface_MODULE_SYMBOLS追加候选修饰名失败语义未识别类别的宏不生成、未识别符号不生成定义并告警、验证失败默认FATAL_ERROR。若需深入研读可继续查看 Modules/FortranCInterface/Detect.cmake检测与解析、Modules/FortranCInterface/CMakeLists.txt候选符号与编译器特例、Modules/FortranCInterface/Verify验证样例工程以及模块在 CMake 自身测试套件中的集成用法可检索Tests目录下对FortranCInterface的引用。赞分享构建工具开发工具CLI【免费下载链接】CMakeMirror of CMake upstream repository项目地址https://gitcode.com/gh_mirrors/cm/CMake点击查看免费下载相关推荐CMake 模块 CheckFortranFunctionExists 深度解析编译期检测 Fortran 函数可用性CMake 模块 CheckFortranFunctionExists 深度解析编译期检测 Fortran 函数可用性 本篇技术指南围绕 CMake 官方模块构建工具开发工具CLICMake-Cookbook实战构建Fortran与C/C混合项目CMake Cookbook实战构建Fortran与C/C混合项目 引言 在现代科学计算领域Fortran因其优秀的数值计算性能仍然占据重要地位。然而文档教程基于 CMake 源码深度解析 CheckSymbolExists 模块C 符号存在性检测的完整实战指南基于 CMake 源码深度解析 CheckSymbolExists 模块C 符号存在性检测的完整实战指南 导读 CheckSymbolExists 是 C构建工具开发工具CLI上一篇serial-display-module图片显示完全教程用show_pic快速切换屏幕图片ID下一篇Godot-MCP 故障排查清单连接失败、命令报错、更改不生效的 8 种解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑