使用Mingw(nt)或者GCC(posix)直接编译Fortran代码为Python扩展库
Python 有很多的扩展,一般常用的是用 C/C++
, Python
编写,本文将介绍使用 Fortran
为Python编写扩展。
介绍
Fortran介绍
FORTRAN是英文“FORmulaTRANslator”的缩写,译为“公式翻译器”,它是世界上最早出现的计算机高级程序设计语言,广泛应用于科学和工程计算领域。FORTRAN语言以其特有的功能在数值、科学和工程计算领域发挥着重要作用。 - 摘抄自百度
Fortran 是一门古老的编程语言,1956年发明,比C语言更加原始。但是由于简单高效,在科学计算领域还有非常广泛的应用。
Mingw
MinGW,是Minimalist GNU for Windows的缩写。它是一个可自由使用和自由发布的Windows特定头文件和使用GNU工具集导入库的集合,允许你在GNU/Linux和Windows平台生成本地的Windows程序而不需要第三方C运行时(C Runtime)库。
混编的原理
由于Fortran非常古老且简单,所以Fortran编译产生的对象文件可以直接跟C语言的对象文件进行链接。通过这种方式,只需要按照python的C扩展方法,把fortran代码链接进去,即可完成pyd文件的生成。
案例
说明
编写一个最简单的Fortran加法函数,然后写一个C扩展,在C扩展中调用Fortran的函数,然后再由C扩展返回到Python。
代码
Fortran代码
一个简单的加法函数
1 | module demomodule |
C扩展代码
这是一个最简单的C扩展例子
1 |
|
- 第2行,定义Fortran中的函数。此函数名称为Fortran的编译对象文件中的函数名称,可以使用mingw编译fortran代码,然后使用nm命令查看函数名称。
1 | gfortran.exe -c add_f.f90 -o add_f.o |
- 第5~10行,接收传入的python参数,传入到Fortan函数中并返回,然后再返回到Python中
- 其余行,简单的python的c扩展规则
setup.py
1 | from setuptools import setup, Extension |
library_dirs 中填写的是ucrt库的路径,此库为 Windows 10 SDK的内容,此库为Python编写扩展的依赖,跟fortran没关系,感觉没啥用
extra_compile_args 中想加上优化,但是好像没成功
编译运行
环境准备
需要Mingw的环境,可以把mingw的路径加入到环境变量中,或者命令行执行如下命令,临时添加
1 | set path=%PATH%;C:\msys64\mingw64\bin |
编译代码
运行以下命令,编译生成库
1 | python setup.py build --compiler=mingw32 |
然后出现以下错误。
此错误是因为Python不支持Fortran代码的编译。
修改如下:
使用Python3.6版本的,修改 Lib/distutils/cygwinccompiler.py
文件第 261
行。
1 | for src_name in source_filenames: |
改为
1 | for src_name in source_filenames: |
重新运行以下命令,编译生成库
1 | python setup.py build --compiler=mingw32 |
完成,如下图所示
安装库
运行以下命令,安装库
1 | python setup.py install |
安装完成,如下图所示
测试运行
使用如下代码
1 | import add |
运行结果,如下所示
环境说明
以上代码的运行环境为
- Python: 3.6.8
- Mingw-gcc: gcc version 7.3.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)
- Fortran 为自由格式
总结
缺少DLL或者链接库异常
解决: 把Mingw的路径加入到环境变量中。
其实只需要把那里面的dll文件拷贝到运行库目录中即可
Python3.8.10运行
修改代码的位置与Python3.6不同。
修改代码 Lib/site-packages/setuptools/_distutils/ccompiler.py
第966行
1 | def out_extensions(self): |
改为
1 | def out_extensions(self): |
然后就可以编译了。
修改代码的原理是让gcc编译器识别fortan文件后缀,不同的python版本,不同的setuptools版本,修改的地方都不一样
可以按照以下的思路去修改:
- 找到
error: unknown file type '.f90' (from 'add_f.f90')
这条错误打印的位置. - 尝试修改.
本节的环境:
- python: 3.8.10
- setuptools: 67.2.0
Linux
修改代码的位置与Python3.6不同。
修改代码 distutils/ccompiler.py
第854行
1 | if ext not in self.src_extensions: |
改为
1 | if ext not in self.src_extensions+['.f90','.f']: |
修改内容如图所示:
然后就可以编译了。