使用 meson-python 打包分发 F2PY 扩展:从 Fortran 源码到可安装的 Wheel
使用 meson-python 打包分发 F2PY 扩展从 Fortran 源码到可安装的 Wheel【免费下载链接】numpyThe fundamental package for scientific computing with Python.项目地址: https://gitcode.com/gh_mirrors/nu/numpy这篇技术指南讲解如何在 NumPy 生态中用meson-python作为 PEP 517 构建后端把 F2PY 包装的 Fortran 代码打包成可安装的 Python 发行包sdist 与 wheel。你将从零搭建一个fib_wrapper示例项目掌握pyproject.toml、meson.build的完整配置学会可编辑安装、构建 wheel、定制 Fortran 编译器如 Intel 的ifx/icx以及链接 BLAS/LAPACK 等系统库——这套流程正是 NumPy 与 SciPy 自身采用的编译扩展分发标准方案。背景为什么是 meson-pythonF2PYFortran to Python interface generator用于把 Fortran 或 C 函数包装成 Python 可调用的扩展模块。长期以来NumPy 生态通过setuptools/numpy.distutils构建这类编译扩展但随着 Python 3.12 移除distutils这一路径已不可持续。从 NumPy 1.26.x 开始F2PY 的默认构建系统切换为mesonnumpy.distutils在 NumPy 2.5.0 中被正式移除参见 F2PY 与构建系统索引。在此基础上meson-python 指南 更进一步不直接调用裸meson命令而是通过meson-python这个 PEP 517 构建后端把扩展编译、打包、安装全流程接入pip install与python -m build从而支持发布到 PyPI 或本地安装。这是当前向 PyPI 分发 F2PY 包装的 Fortran 代码的推荐路径。这一选择有仓库自身的实践佐证NumPy 仓库的 pyproject.toml 同样声明了build-backend mesonpy并在[tool.meson-python]段指定了自己的 meson 解释器与安装参数。也就是说你将要配置的每一行都与 NumPy 本身的做法同源。前置条件开始之前需要准备C 编译器用于编译 F2PY 生成的 C 包装代码与fortranobject.cFortran 编译器gfortran、ifort、ifx、flang-new等只要包内含 Fortran 代码就必须Python 3.10meson、meson-python、numpy当它们被列在build-system.requires中时会在构建期间自动安装无需手动预装除非使用--no-build-isolation。关于各编译器相关环境变量的行为可参考 从 distutils 迁移到 meson 中的说明FC/CC等环境变量在类 Unix 系统上通常有效但 Windows 上可靠性较差此时更推荐使用 Meson 的 native file 直接指定编译器。最小示例把 Fortran 斐波那契子程序包装成 Python 包下面这个项目把一段 Fortranfib子程序包装成可导入的 Python 包fib_wrapper。项目结构如下fib_wrapper/ # 项目根目录 ├── fib.f90 # Fortran 源码 ├── fib_wrapper/ # Python 包目录 │ └── __init__.py ├── meson.build └── pyproject.toml1. Fortran 源码fib.f90保存以下内容为fib.f90本仓库中对应文件为 fib_mesonpy.f90subroutine fib(a, n) use iso_c_binding integer(c_int), intent(in) :: n integer(c_int), intent(out) :: a(n) integer :: i do i 1, n if (i 1) then a(i) 0 else if (i 2) then a(i) 1 else a(i) a(i - 1) a(i - 2) end if end do end subroutine fib这段代码使用iso_c_binding声明integer(c_int)保证与 C/Python 侧的整数类型严格一致intent(out)让 F2PY 明确知道a是输出数组从而生成更符合 Python 习惯的签名这一点与 f2py.getting-started 中smart way的结论一致显式声明意图能避免默认全输入语义带来的意外行为。2.pyproject.toml本仓库中的完整示例见 pyproj_mesonpy.toml[build-system] # numpy2.0 是 meson.build 中 dependency(numpy) 支持所必需的 requires [meson-python0.15.0, numpy2.0] build-backend mesonpy [project] name fib_wrapper version 0.1.0 requires-python 3.10 dependencies [numpy]两个关键点build-backend mesonpy告诉 pip、build 等构建前端使用meson-python作为 PEP 517 构建后端requires中的numpy 2.0只有在编译期间可用f2py模块、NumPy 头文件以及 Meson 的dependency(numpy)支持numpy 2.0起提供meson.build里的 NumPy 依赖探测才能工作。requires-python 3.10与文档要求的前置条件一致dependencies [numpy]声明运行时依赖保证安装后import numpy可用。3.meson.build这是整个构建流程的核心。文档源码树中以meson_mesonpy.build文件名存放以避免与其他示例冲突本仓库路径为 meson_mesonpy.build在你的项目中应命名为meson.build。完整内容如下project(fib_wrapper, c, version : 0.1.0, meson_version: 1.1.0, default_options : [warning_level2], ) add_languages(fortran, native: false) py import(python).find_installation(pure: false) # NumPy 2.0 通过 dependency() 提供 include 目录 np_dep dependency(numpy) incdir_f2py run_command(py, [-c, import numpy.f2py; print(numpy.f2py.get_include())], check : true ).stdout().strip() # f2py include 目录用于 fortranobject.h不在 dependency(numpy) 中 # 因此单独添加 f2py_dep declare_dependency( include_directories : incdir_f2py, ) # 生成 f2py 包装代码 fib_source custom_target(fibmodule.c, input : [fib.f90], output : [fibmodule.c, fib-f2pywrappers.f], command : [py, -m, numpy.f2py, INPUT, -m, fib, --lower] ) py.extension_module(fib, [fib.f90, fib_source, incdir_f2py / fortranobject.c], dependencies : [np_dep, f2py_dep], subdir: fib_wrapper, install : true, ) # 安装 Python 包文件 py.install_sources( fib_wrapper/__init__.py, subdir: fib_wrapper, )这个文件完成了四件事定位 NumPy 头文件dependency(numpy)借助 NumPy 2.x 提供的 pkg-config/CMake 探测能力返回包含目录再用run_command调用numpy.f2py.get_include()拿到 F2PY 的 include 目录并用declare_dependency将其加入头文件搜索路径。这一步对应 numpy/f2py/init.py 中get_include()的实现——它返回numpy/f2py/src目录该目录存放着fortranobject.c与fortranobject.h。文档 F2PY 与构建系统 也强调一个 F2PY 扩展的构建产物包含 Fortran 源码、F2PY 生成的 C 包装文件、fortranobject.{c,h}、NumPy 头文件以及 Python 开发头文件本示例的meson.build恰好逐项覆盖了这些要素。生成 F2PY 包装源码custom_target调用python -m numpy.f2py输入fib.f90输出fibmodule.c以及可能生成的fib-f2pywrappers.f-m fib指定模块名--lower确保符号转为小写。--lower之所以必要是因为裸 meson 工作流中 SCREAMCASE 符号如FIB_会导致链接期undefined symbol错误——这一点在 裸 meson 指南 的 Fibonacci 演练中有详细讨论。编译并链接扩展模块py.extension_module把fib.f90、生成的 C 包装源码、以及fortranobject.c一起编译为 Python 扩展并链接np_dep与f2py_dep两个依赖。安装为正规 Python 包py.install_sources把__init__.py安装进包目录。subdir: fib_wrapper参数是必需的它保证编译出的fib共享库被安装到fib_wrapper/包目录内、与__init__.py相邻。如果省略该参数扩展会被安装到顶层import fib_wrapper将无法找到fib扩展。安装后的布局为site-packages/ └── fib_wrapper/ ├── __init__.py # from .fib import fib └── fib.cpython-*.so # 编译出的扩展模块4.__init__.py一个极简的__init__.py负责重导出包装后的函数from .fib import fib这样用户只需from fib_wrapper import fib即可使用无需关心底层扩展模块的存在。构建与安装可编辑安装开发模式pip install --no-build-isolation --editable .--no-build-isolation会复用当前环境而不是创建隔离的构建环境便于快速迭代。前提是当前环境中已经安装好meson-python、meson、ninja和numpy。构建 wheel# 如果尚未安装 pypa/buildpip install build python -m build --wheel生成的.whl文件位于dist/目录可以上传到 PyPI或通过pip install dist/fib_wrapper-0.1.0-*.whl在其他环境安装。验证安装 from fib_wrapper import fib fib(10) array([ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34], dtypeint32)由于fib.f90中a声明为integer(c_int), intent(out)F2PY 自动生成输入n、返回数组的 Python 签名返回的是int32的 NumPy 数组——这正是显式intent声明的效果。定制 Fortran 编译器meson-python把编译器选择委托给meson。默认情况下meson会选取 PATH 上找到的第一个 Fortran 编译器。如需更多控制构建前设置FC环境变量FCifx python -m build --wheel如果还需要同时控制 C 编译器例如 Intel 工具链下FCifx搭配CCicx或者环境变量在 Windows 上不可靠更稳妥的方式是使用 Meson native file; native.ini [binaries] fortran ifx c icx然后通过-Csetup-args把它传给构建前端python -m build --wheel -Csetup-args--native-filenative.ini这与 从 distutils 迁移到 meson 中列出的环境变量语义一致FC/CC/CFLAGS/FFLAGS/LDFLAGS/CXX等在 Windows 上 native file 是首选方案。添加系统库依赖BLAS、LAPACK 等在meson.build中使用dependency()链接系统库lapack_dep dependency(lapack) py.extension_module(mymod, [sources, generated, incdir_f2py / fortranobject.c], dependencies : [np_dep, f2py_dep, lapack_dep], install : true, )meson会依次通过pkg-config、CMake 或自身的探测逻辑解析这些依赖。注意这里把incdir_f2py / fortranobject.c作为源码之一传入这是 F2PY 扩展必须的与 numpy/f2py/init.py 文档字符串中Step 2 需要mymodmodule.c、mymod-f2pywrappers.f、fortranobject.c的描述完全对应。如果走裸 meson 或python -m numpy.f2py -c的快速路径--dep lapack也会映射到dependency(lapack)参见 distutils-to-meson二者殊途同归。与 scikit-build-core 工作流的区别同样是打包 F2PY 扩展另一条路线是用scikit-build-core内部驱动 CMake对应文档为 Using via scikit-build。与它相比meson-python提供Meson 原生支持 Fortran 编译器无需经过 CMake 这一层Fortran 是 Meson 的一等语言通过 PEP 517 与pip/build直接集成pip install、python -m build --wheel开箱即用与 NumPy 和 SciPy 自身使用的构建系统完全一致这意味着你能直接参考 SciPy 的 meson 配置来解决真实场景中遇到的 F2PY 集成问题。meson-python取代setuptools/numpy.distutils成为 NumPy 与 SciPy 生态中构建分发编译扩展的标准方式这一点在 F2PY 与构建系统索引 中有明确说明。小结与进一步阅读至此你已掌握一条完整的 F2PY 扩展分发链路从 Fortran 源码出发用custom_target生成包装代码用py.extension_module编译安装再用meson-python产出 wheel 并接入 PyPI 分发。这套流程完全可复制到真实的科学计算包中并且与 NumPy、SciPy 官方构建方式保持一致未来迁移、排错都有大量现成参考。如果想继续深入建议阅读裸 meson 构建 F2PY 扩展不借助meson-python直接使用meson setup/meson compile的底层流程能帮助你理解meson.build每一行的作用从 distutils 迁移到 mesonnumpy.distutils时代的命令、环境变量与依赖参数如何映射到 meson 体系使用 scikit-build 构建基于 CMake 的替代方案及其适用场景F2PY 入门三种包装方式fib示例的快速路径、签名文件路径与 F2PY 指令路径的完整对比仓库中 code 目录 下与本主题配套的示例文件fib_mesonpy.f90、pyproj_mesonpy.toml、meson_mesonpy.build。【免费下载链接】numpyThe fundamental package for scientific computing with Python.项目地址: https://gitcode.com/gh_mirrors/nu/numpy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考