teh*_*rus 11 c python fortran distutils cython
最终更新
这个问题是关于如何编写一个setup.py将编译一个直接访问FORTRAN代码的cython模块,就像C一样.这是一个相当漫长而艰巨的解决方案之旅,但下面包含完整的混乱情况.
原始问题
我有一个扩展,它是一个Cython文件,它设置一些堆内存并将其传递给fortran代码,以及一个fortran文件,这是一个古老的模块,我想避免重新实现,如果可以的话.
该.pyx文件可以很好地编译为C,但是cython编译器会对.f90文件进行扼流,并出现以下错误:
$ python setup.py build_ext --inplace
running build_ext
cythoning delaunay/__init__.pyx to delaunay/__init__.c
building 'delaunay' extension
error: unknown file type '.f90' (from 'delaunay/stripack.f90')
Run Code Online (Sandbox Code Playgroud)
这是我的安装文件的(上半部分):
from distutils.core import setup, Extension
from Cython.Distutils import build_ext
ext_modules = [
Extension("delaunay",
sources=["delaunay/__init__.pyx",
"delaunay/stripack.f90"])
]
setup(
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules,
...
)
Run Code Online (Sandbox Code Playgroud)
注意:我最初错误地指定了fortran文件的位置(没有目录前缀),但是在我修复之后,这种方式完全相同.
我尝试过的事情:
我找到了这个,并尝试传递fortran编译器(即gfortran)的名称,如下所示:
$ python setup.py config --fcompiler=gfortran build_ext --inplace
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
or: setup.py --help [cmd1 cmd2 ...]
or: setup.py --help-commands
or: setup.py cmd --help
error: option --fcompiler not recognized
Run Code Online (Sandbox Code Playgroud)
我也尝试删除--inplace,如果是问题(它不是,与顶部错误消息相同).
那么,我该如何编译这个fortran?我可以把它变成一个.o自己的东西,并通过链接来逃脱吗?或者这是Cython中的一个错误,它会迫使我重新实现distutils或者使用预处理器进行破解?
UPDATE
所以,在检查了numpy.distutils包裹之后,我更了解这个问题.看来你必须这样做
Extension/ setup()组合numpy.尝试过这个,我setup.py现在看起来像这样:
from numpy.distutils.core import setup
from Cython.Build import cythonize
from numpy.distutils.extension import Extension
cy_modules = cythonize('delaunay/sphere.pyx')
e = cy_modules[0]
ext_modules = [
Extension("delaunay.sphere",
sources=e.sources + ['delaunay/stripack.f90'])
]
setup(
ext_modules = ext_modules,
name="delaunay",
...
)
Run Code Online (Sandbox Code Playgroud)
(请注意,我还对模块进行了一些重组,因为看似__init__.pyx不允许...)
现在,事情变得越来越多,并且依赖于平台.我有两种测试系统可供选择 - 一种是使用Macports Python 2.7的Mac OS X 10.6(Snow Leopard),另一种是使用系统python 2.7的Mac OS X 10.7(Lion).
在Snow Leopard上,以下内容适用:
这意味着该模块编译(欢呼!)(虽然--inplace似乎没有numpy,所以我不得不在系统范围内安装测试模块:/)但我仍然遇到import如下崩溃:
>>> import delaunay
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<snip>site-packages/delaunay/__init__.py", line 1, in <module>
from sphere import delaunay_mesh
ImportError: dlopen(<snip>site-packages/delaunay/sphere.so, 2): no suitable image found. Did find:
<snip>site-packages/delaunay/sphere.so: mach-o, but wrong architecture
Run Code Online (Sandbox Code Playgroud)
在Lion上,我遇到了一个编译错误,在一个相当混乱的编译行之后:
gfortran:f77: build/src.macosx-10.7-intel-2.7/delaunay/sphere-f2pywrappers.f
/usr/local/bin/gfortran -Wall -arch i686 -arch x86_64 -Wall -undefined dynamic_lookup -bundle build/temp.macosx-10.7-intel-2.7/delaunay/sphere.o build/temp.macosx-10.7-intel-2.7/build/src.macosx-10.7-intel-2.7/delaunay/spheremodule.o build/temp.macosx-10.7-intel-2.7/build/src.macosx-10.7-intel-2.7/fortranobject.o build/temp.macosx-10.7-intel-2.7/delaunay/stripack.o build/temp.macosx-10.7-intel-2.7/build/src.macosx-10.7-intel-2.7/delaunay/sphere-f2pywrappers.o -lgfortran -o build/lib.macosx-10.7-intel-2.7/delaunay/sphere.so
ld: duplicate symbol _initsphere in build/temp.macosx-10.7-intel-2.7/build/src.macosx-10.7-intel-2.7/delaunay/spheremodule.o ldand :build /temp.macosx-10.7-intelduplicate- 2.7symbol/ delaunay/sphere.o _initsphere in forbuild architecture /i386
temp.macosx-10.7-intel-2.7/build/src.macosx-10.7-intel-2.7/delaunay/spheremodule.o and build/temp.macosx-10.7-intel-2.7/delaunay/sphere.o for architecture x86_64
Run Code Online (Sandbox Code Playgroud)
现在让我们回过头来看看这里的细节.首先,我知道64位Mac OS X中的架构冲突有很多令人头痛的问题; 我必须非常努力地让Macports Python在Snow Leopard机器上工作(只是从系统python 2.6升级).我也知道当你看到gfortran -arch i686 -arch x86_64你向你的编译器发送混合消息时.在那里埋藏着各种特定于平台的问题,我们不需要在这个问题的背景下担心.
但是,让我们看看这一行:
gfortran:f77: build/src.macosx-10.7-intel-2.7/delaunay/sphere-f2pywrappers.f
什么是numpy做什么?!我在这个版本中不需要任何f2py功能!我实际上写了一个cython模块,以避免处理f2py的疯狂(我需要有4个或5个输出变量,以及两个都没有输出的参数 - 在f2py中都没有得到很好的支持.)我只是想要它要编译.c- > .o,.f90- > .o并链接它们.如果我知道如何包含所有相关的标题,我可以自己编写这个编译器行.
请告诉我,我不需要为此编写自己的makefile ...或者有一种方法可以将fortran转换为(输出兼容)C,这样我就可以避免python看到.f90扩展名(修复了整个问题.)注意f2c不适合这个,因为它只适用于F77,这是一个更现代的方言(因此.f90文件扩展名).
更新2 以下bash脚本将很乐意编译并链接代码:
PYTHON_H_LOCATION="/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7/"
cython sphere.pyx
gcc -arch x86_64 -c sphere.c -I$PYTHON_H_LOCATION
gfortran -arch x86_64 -c stripack.f90
gfortran -arch x86_64 -bundle -undefined dynamic_lookup -L/opt/local/lib *.o -o sphere.so
Run Code Online (Sandbox Code Playgroud)
关于如何使这种hack与setup.py兼容的任何建议?我没有安装此模块的任何人必须Python.h手动查找...
更新:我在 github 上创建了一个项目,它手动生成了编译行。它被称为“complex_build”。
更新 2:事实上,“手动生成”是一个非常糟糕的主意,因为它是特定于平台的 - 项目现在从模块读取值distutils.sysconfig,这是用于编译 python 的设置(即正是我们想要的)唯一设置猜测是 fortran 编译器和文件扩展名(用户可配置)。我怀疑它现在正在重新实现相当多的 distutils!
做到这一点的方法是编写您自己的编译器行,并将它们破解到您的setup.py. 我在下面展示了一个适用于我的(非常简单)案例的示例,该案例具有以下结构:
cythonize()任何.pyx文件,因此您只有 fortran 和 C 文件。build()编译代码的函数:
install并且目标尚不存在,则构建它。build,则立即运行构建。我的实现如下所示。它仅针对一个扩展模块而设计,并且每次都会重新编译所有文件,因此可能需要进一步扩展才能具有更通用的用途。另请注意,我已经对各种 unix 进行了硬编码/,因此,如果您将其移植到 Windows,请确保将其调整或替换为os.path.sep.
from distutils.core import setup
from distutils.sysconfig import get_python_inc
from Cython.Build import cythonize
import sys, os, shutil
cythonize('delaunay/sphere.pyx')
target = 'build/lib/delaunay/sphere.so'
def build():
fortran_compiler = 'gfortran'
c_compiler = 'gcc'
architecture = 'x86_64'
python_h_location = get_python_inc()
build_temp = 'build/custom_temp'
global target
try:
shutil.rmtree(build_temp)
except OSError:
pass
os.makedirs(build_temp) # if you get an error here, please ensure the build/ ...
# folder is writable by this user.
c_files = ['delaunay/sphere.c']
fortran_files = ['delaunay/stripack.f90']
c_compile_commands = []
for cf in c_files:
# use the path (sans /s), without the extension, as the object file name:
components = os.path.split(cf)
name = components[0].replace('/', '') + '.'.join(components[1].split('.')[:-1])
c_compile_commands.append(
c_compiler + ' -arch ' + architecture + ' -I' + python_h_location + ' -o ' +
build_temp + '/' + name + '.o -c ' + cf
)
fortran_compile_commands = []
for ff in fortran_files:
# prefix with f in case of name collisions with c files:
components = os.path.split(ff)
name = components[0].replace('/', '') + 'f' + '.'.join(components[1].split('.')[:-1])
fortran_compile_commands.append(
fortran_compiler + ' -arch ' + architecture + ' -o ' + build_temp +
'/' + name + '.o -c ' + ff
)
commands = c_compile_commands + fortran_compile_commands + [
fortran_compiler + ' -arch ' + architecture +
' -bundle -undefined dynamic_lookup ' + build_temp + '/*.o -o ' + target
]
for c in commands:
os.system(c)
if 'install' in sys.argv and not os.path.exists(target):
try:
os.makedirs('build/lib/delaunay')
except OSError:
# we don't care if the containing folder already exists.
pass
build()
setup(
name="delaunay",
version="0.1",
...
packages=["delaunay"]
)
if 'build' in sys.argv:
build()
Run Code Online (Sandbox Code Playgroud)
我想这可以包含在一个新的Extension类中,有它自己的build_ext命令 - 一个针对高级学生的练习;)
| 归档时间: |
|
| 查看次数: |
3669 次 |
| 最近记录: |