gpt4 book ai didi

python - Cython 和 fortran - 如何在没有 f2py 的情况下一起编译

转载 作者:太空狗 更新时间:2023-10-29 18:08:20 28 4
gpt4 key购买 nike

最终更新

这个问题是关于如何写一个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')

这是我的安装文件(的上半部分):

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,
...
)

注意:我最初错误地指定了 fortran 文件的位置(没有目录前缀),但是在我修复它之后这以完全相同的方式中断。

我尝试过的东西:

我找到了 this ,并尝试像这样传入 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

我也试过删除 --inplace ,以防万一这是问题(不是,与顶部错误消息相同)。

那么,我如何编译这个fortran?我可以把它破解成 .o我自己并摆脱链接它?或 is this a bug in Cython ,这将迫使我重新实现 distutils 或使用预处理器进行破解?

更新

所以,检查了 numpy.distutils包,我更了解这个问题。看来你必须
  • 使用 cython 将 .pyx 文件转换为 cpython .c 文件,
  • 然后使用 Extension/setup()支持 fortran 的组合,如 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",
    ...
    )

    (请注意,我还对模块进行了一些重构,因为似乎不允许使用 __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

    在 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

    现在让我们先退后一步,然后再仔细研究这里的细节。首先,我知道在 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...问题。)请注意 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

    关于如何使这种 hack 与 setup.py 兼容的任何建议?我没有安装这个模块的人必须去找 Python.h手动...

    最佳答案

    更新:我在 github 上创建了一个项目,它手动完成了编译行的生成。它叫做 complicated_build .

    更新 2:事实上,“手动生成”是一个非常糟糕的主意,因为它是特定于平台的——该项目现在从 distutils.sysconfig 读取值。模块,这是用于编译 python 的设置(即正是我们想要的),唯一猜测的设置是 fortran 编译器和文件扩展名(用户可配置)。我怀疑它现在正在重新实现一些 distutils!

    这样做的方法是编写您自己的编译器行,并将它们破解到您的 setup.py 中。 .我在下面展示了一个适用于我的(非常简单的)案例的示例,它具有以下结构:

  • 进口
  • cythonize()任何 .pyx文件,所以你只有 fortran 和 C 文件。
  • 定义一个 build()编译代码的函数:
  • 也许一些易于更改的常量,例如编译器名称和体系结构
  • 列出 fortran 和 C 文件
  • 生成将构建模块的 shell 命令
  • 添加链接器行
  • 运行 shell 命令。
  • 如果命令是 install并且目标还不存在,构建它。
  • 运行 setup(将构建纯 python 部分)
  • 如果命令是 build ,现在运行构建。

  • 我的实现如下所示。它只为一个扩展模块设计,并且每次都会重新编译所有文件,因此可能需要进一步扩展才能更通用。另请注意,我已经硬编码了各种 unix / s,所以如果你把它移植到 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()

    这可以被包装成一个新的 Extension我猜是类(class),有自己的 build_ext命令 - 高级学生的练习;)

    关于python - Cython 和 fortran - 如何在没有 f2py 的情况下一起编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12696520/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com