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

2023-11-22

最终更新

这个问题是关于如何写一个setup.py这将编译一个 cython 模块,该模块可以像 C 一样直接访问 FORTRAN 代码。这是一个相当漫长而艰巨的解决方案旅程,但完整的混乱情况包含在下面作为上下文。

原问题

我有一个扩展,它是一个 Cython 文件,它设置一些堆内存并将其传递给 fortran 代码,还有一个 fortran 文件,它是一个古老的旧模块,如果可以的话,我希望避免重新实现。

The .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 文件的位置(没有目录前缀),但在修复该问题后,它以完全相同的方式中断。

我尝试过的事情:

I found 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我自己并摆脱链接它?或者这是 Cython 中的错误吗,这将迫使我重新实现 distutils 或使用预处理器进行修改?

UPDATE

所以,检查了numpy.distutils包,我更了解这个问题了。看来你必须

  1. 使用 cython 将 .pyx 文件转换为 cpython .c 文件,
  2. 然后使用一个Extension/setup()支持 fortran 的组合,例如numpy's.

尝试过这个之后,我的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是不允许的...)

现在事情变得有问题并且依赖于平台。我有两个可用的测试系统 - 一个是 Mac OS X 10.6 (Snow Leopard),使用 Macports Python 2.7,另一个是 Mac OS X 10.7 (Lion),使用系统 python 2.7。

在 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, and .f90 -> .o并将它们链接起来。如果我知道如何包含所有相关的标头,我可以自己编写此编译器行。

请告诉我,我不需要为此编写自己的 makefile...或者有一种方法可以将 fortran 转换为(输出兼容的)C,这样我就可以避免 python 看到 .f90 扩展名(它修复了整个问题)问题。)请注意f2c不适合这个,因为它只适用于 F77,这是一种更现代的方言(因此.f90文件扩展名)。

UPDATE 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手动...


UPDATE:我在 github 上创建了一个项目,它手动完成了编译行的生成。它被称为复杂的构建.

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


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

  • imports
  • cythonize() any .pyx文件,所以你只有 fortran 和 C 文件。
  • define a build() function which compiles your code:
    • 也许是一些易于更改的常量,例如编译器名称和体系结构
    • 列出 fortran 和 C 文件
    • 生成将构建模块的 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我想,有它自己的类build_ext命令 - 高级学生的练习;)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Cython 和 fortran - 如何在没有 f2py 的情况下一起编译 的相关文章

随机推荐

  • 尝试在 Windows 上重定向 stdout 和 stderr - _fileno(stdout) 返回 -2

    这是我的第一个问题 我即将将一些运行良好的 C 代码从 UNIX 移植到 Windows 它通过管道将 stdout 和 stderr 重定向到自定义 GUI 组件 我需要它来显示来自第三方库的反馈 该第三方库仅将消息输出到 GUI 上的标
  • 在C python中,访问字节码评估堆栈

    给定一个 C Python 帧指针 如何查看任意计算堆栈条目 一些特定的堆栈条目可以通过locals 我说的是其他堆栈条目 我不久前问了一个更广泛的问题 获取 C python exec 参数字符串或访问计算堆栈 但在这里我想重点关注能够在
  • 使用多语句 lambda 进行返回类型推导

    我一直在编写代码 最近发现 g 不会警告我某一类问题 根据 C 11 5 1 2 4 如果您的 lambda 不是单个返回语句 则返回类型必须声明为尾随返回类型或为 void 尽管 g 允许编译无效代码 如果它足够有意义 但有没有办法关闭此
  • 使用 SSIS 加载多个表并保持外键关系

    我正在尝试使用 SSIS 将单个文件 包含数百万条记录 中的数据加载到 SQL Server 上的多个表中 同时维护文件中定义的关系 为了更好地举例说明 假设我正在尝试加载一个文件 其中包含员工姓名 他们过去占用的办公室以及由选项卡分隔的职
  • Python Numpy 中的数组和 __rmul__ 运算符

    在一个项目中 我创建了一个类 我需要这个新类和一个真实矩阵之间的操作 所以我重载了 rmul 像这样的功能 class foo object aarg 0 def init self self aarg 1 def rmul self A
  • 如何在图像上绘制带有轮廓的文本?

    我一直在搜索和寻找在图像上绘制带有轮廓的文本 这是我的代码 private static void tulisnamafile string imagepath string textnya Image image Image FromSt
  • WinRT StorageFile 写入下载的文件

    我正在努力解决一个简单的问题 我想使用以下代码从网络下载图像 WebRequest requestPic WebRequest Create http something com id jpg WebResponse responsePic
  • preg_match:仅数字字母和逗号

    如何编写仅匹配数字字母和逗号的正则表达式 我想出了下面这个 但它不起作用 它也接受其他标点符号 check for matches number alphabets and commas only if preg match a zA Z0
  • 由于环境变量(HP笔记本电脑),delphi XE2无法在我的计算机上编译任何项目

    我有一台 HP 笔记本电脑 并且在 HP 笔记本电脑上安装了 delphi xe2 过去 5 个月前我使用 delphi 没有任何问题 但现在我收到此错误消息 DCC Error E1026 File not found FMX Filte
  • 订阅中的 Angular 订阅

    我有以下由多个订阅组成的代码 我需要实现的目标是这样的 订阅activatedRoute以获取用户和产品数据 返回商品数据后 使用商品数据订阅getSeller服务 使用返回的卖家数据订阅 getRating 服务 我的问题 有没有更好的方
  • Lua 5.2 中的沙箱

    我正在学习Roberto Ierusalimschy的 Programing in Lua 我发现书中的Sandboxing示例使用了该函数setfenv 改变给定函数的环境 但是在 lua 5 2 中这个函数不再可用 我尝试将文件 配置文
  • WPF DataGrid:如何将列设置为 TextWrap?

    我不确定为什么我的代码没有正确执行 TextWrapping 它不会包装 描述 列的文本 这正是我想要的 它只是将其切断 甚至没有使用 来让我知道还有更多数据 我尝试使用在网上找到的代码来完成这项工作 但没有成功 理想情况下 我希望能够仅将
  • 通过XPath提取属性节点的值

    如何通过 XPath 提取属性节点的值 示例 XML 文件是
  • C++20 标准对于将主题用作模板非类型参数有何规定?

    The 模板非类型参数 文章 模板参数和模板参数 的段落指出 唯一的例外是引用的非类型模板参数 或指针类型以及引用或指针的非静态数据成员 输入类类型及其子对象的非类型模板参数 C 20 起 不能引用 成为以下地址 临时对象 包括在引用初始化
  • 通过 Google Chrome 扩展访问本地文件?

    我需要从本地文件将名称列表加载到我的 google chrome 扩展中 如何才能做到这一点 如果文件本身附带扩展名怎么办 如果此文件随您的扩展一起提供 那么您可以使用以下命令加载它XMLHttpRequest内部背景页面 使用相对路径 带
  • 嵌入式(ASP.NET)网络服务器[关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 目前不接受答案 我正在寻找适用于 NET 的轻量级可嵌入 Web 服务器 我需要它来伪造 SOAP Web 服务以进行自动化测试 因此如果它支持 ASP NET W
  • jQuery Ajax 到 asp.net asmx Web 服务抛出请求格式无效:application/json

    我让 jquery 使用整数调用 asp net web 服务 在我们移植到 net 4 0 的旧应用程序上 我无法让此调用正常工作 我可以调用一个没有参数的方法 但将数据发送到 Web 方法会返回以下错误 System InvalidOp
  • 如何在 Android 设备中检测来电?

    我正在尝试制作一个应用程序 例如当电话打来电话时我想检测号码 以下是我尝试过的方法 但它没有检测到来电 我想运行我的MainActivity在后台 我该怎么做 我已给予许可manifest file
  • 我应该如何构建一个简单的 ASP.NET MVC 应用程序?

    我一直在阅读一些有关 ASP NET MVC SOLID 等的内容 并且正在尝试为中小型 ASP NET MVC 应用程序找出一个简单的 秘诀 将这些概念整合在一起 我最关心的问题是控制器过于复杂 就像网络表单中的代码隐藏文件 其中包含所有
  • Cython 和 fortran - 如何在没有 f2py 的情况下一起编译

    最终更新 这个问题是关于如何写一个setup py这将编译一个 cython 模块 该模块可以像 C 一样直接访问 FORTRAN 代码 这是一个相当漫长而艰巨的解决方案旅程 但完整的混乱情况包含在下面作为上下文 原问题 我有一个扩展 它是