使用英特尔Fortran编译器的f2py

6

我想使用f2py将我的Python程序与Fortran模块进行接口连接。

我使用的是Win7平台。

我使用最新的Anaconda 64(1.7)作为Python+NumPy堆栈。

我的Fortran编译器是最新的Intel Fortran编译器64(版本14.0.0.103 Build 20130728)。

当执行f2py -c -m PyModule FortranModule.f90 --fcompiler=intelvem时,我遇到了许多问题。

其中一个问题是似乎f2py/distutils传递给编译器的标志序列与ifort所期望的不匹配。

当调用ifort时,我会收到一系列关于未知选项的警告消息。

ifort: command line warning #10006: ignoring unknown option '/LC:\Anaconda\libs'
ifort: command line warning #10006: ignoring unknown option'/LC:\Anaconda\PCbuild\amd64'
ifort: command line warning #10006: ignoring unknown option '/lpython27'

我怀疑这与我的链接器最后出现的错误有关。

error LNK2019: unresolved external symbol __imp_PyImport_ImportModule referenced in function _import_array
error LNK2019... and so forth (there are about 30-40 lines like that, with different python modules missing)

并且这个内容以普通文本形式结束。
fatal error LNK1120: 42 unresolved externals

我猜这是因为选项序列中缺少/link标志。由于这个原因,/l /L选项没有传递给链接器,编译器认为这些是针对他的。

f2py生成的ifort命令看起来像这样:

ifort.exe -dll -dll Pymodule.o fortranobject.o FortranModule.o module-f2pywrappers2.o -LC:\Anaconda\libs -LC:\Anaconda\PCbuild\amd64 -lPython27

我不知道为什么"-dll"会被重复两次(我必须将该标志从原始的“-shared”更改)。现在,我已经试图查看f2py和distutils代码,但是还没有弄清楚如何在命令输出中添加额外的/link。我甚至没有找到生成此输出的位置。如果有人遇到过这个问题或者有一些建议,我会非常感激。谢谢您的时间。

1
我也遇到了类似的问题,但不幸的是我找不到解决方法。最终我编写了一个Makefile将所有东西拼凑在一起。这是Makefile的链接:http://pastebin.com/ChSxLzSb。 - bdforbes
谢谢您提供这个文件!我今天才有机会查看它。由于需要处理很多路径和获取各种文件,它最终让我费了很大的劲才能使其正常工作。但是,最后我已经实现了我想要的目标。我认为很难得出最终答案,因为它似乎高度依赖于所安装的平台和工具链。 - Tesla
你最终采取了什么方法?你成功地将distutils破解了吗? - bdforbes
1
不,黑客攻击你的makefile要容易得多...我可能会在未来几周再试一下distutils。如果我找到有用的东西,我一定会告诉你。 - Tesla
有点晚了,但是... 库路径应该使用 /LIBPATH 而不是 /L。 - cup
@cup请将此作为答案发布,以便关闭此问题。谢谢。 - rocky
2个回答

3

我曾经遇到过自己的代码类似的问题。如果我正确理解了评论,你已经使用了对我有用的方法,所以这只是为那些在使用f2py和依赖项时遇到困难的人提供澄清和总结:

f2py似乎在解析外部源文件的依赖关系方面存在问题。但是,如果将外部依赖项作为已编译的目标文件传递给f2py,则链接正常工作,Python库也可以无问题地构建。

因此,最简单的解决方案似乎是:

  1. 使用您喜欢的编译器和编译器设置将所有依赖项编译为目标文件(*.o)
  2. 将所有目标文件与您的主子程序/函数/模块的源文件一起传递给f2py
  3. 像预期的那样使用生成的Python库

一个简单的Python脚本可能如下所示(pycompile.py):

#!python.exe
# -*- coding: UTF-8 -*-
import os
import platform
'''Uses f2py to compile needed library'''
# build command-strings
# command for compling *.o and *.mod files
fortran_exe = "gfortran "
# fortran compiler settings
fortran_flags = "<some_gfortran_flags> "
# add path to source code
fortran_source = ("./relative/path/to/source_1.f90 "
                  "C:/absolut/path/to/source_2.f90 "                 
                  "...")
# assemble fortran command
fortran_cmd = fortran_exe + fortran_flags + fortran_source

# command for compiling main source file using f2py
f2py_exe = "f2py -c "
# special compiler-options for Linux/ Windows
if (platform.system() == 'Linux'):
    f2py_flags = "--compiler=unix --fcompiler=gnu95 "
elif (platform.system() == 'Windows'):
    f2py_flags = "--compiler=mingw32 --fcompiler=gnu95 "
# add path to source code/ dependencies
f2py_source = ("-m for_to_py_lib "
               "./path/to/main_source.f90 "
               "source_1.o "
               "source_2.o "
               "... "
               )
# assemble f2py command
f2py_cmd = f2py_exe + f2py_flags + f2py_source

# compile .o and .mod files
print "compiling object- and module-files..."
print
print fortran_cmd
os.system(fortran_cmd)
# compile main_source.f90 with f2py
print "================================================================"
print "start f2py..."
print
print f2py_cmd
os.system(f2py_cmd)

针对大型项目的更灵活的解决方案可以通过Makefile提供,正如评论中@bdforbes所讨论的那样(供参考),或者结合上述脚本使用自定义的CMake用户命令:

###############################################################################
# General project properties
################################################################################
# Set Project Name
project (for_to_py_lib)
# Set Version Number
set (for_to_py_lib_VERSION_MAJOR 1)
set (for_to_py_lib_VERSION_MINOR 0)
# save folder locations for later use/ scripting (see pycompile.py)
# relative to SOURCE folder
set(source_root ${CMAKE_CURRENT_LIST_DIR}/SOURCE) # save top level source dir for later use
set(lib_root ${CMAKE_CURRENT_LIST_DIR}/LIBRARIES) # save top level lib dir for later use
# relative to BUILD folder
set(build_root ${CMAKE_CURRENT_BINARY_DIR}) # save top level build dir for later use

###
### Fortran to Python library
###
find_package(PythonInterp)
if (PYTHONINTERP_FOUND)
    # copy python compile skript file to build folder and substitute CMake variables
    configure_file(${source_root}/pycompile.py ${build_root}/pycompile.py @ONLY)
    # define for_to_py library ending
    if (UNIX)
        set(CMAKE_PYTHON_LIBRARY_SUFFIX .so)
    elseif (WIN32)
        set(CMAKE_PYTHON_LIBRARY_SUFFIX .pyd)
    endif()
    # add custom target to ALL, building the for_to_py python library (using f2py)
    add_custom_target(for_to_py ALL
                      DEPENDS ${build_root}/for_to_py${CMAKE_PYTHON_LIBRARY_SUFFIX})
    # build command for python library (execute python script pycompile.py containing the actual build commands)
    add_custom_command(OUTPUT ${build_root}/for_to_py${CMAKE_PYTHON_LIBRARY_SUFFIX}
                       COMMAND ${PYTHON_EXECUTABLE} ${build_root}/pycompile.py
                       WORKING_DIRECTORY ${build_root}
                       DEPENDS ${build_root}/pycompile.py
                               ${source_root}/path/to/source_1.f90
                               ${source_root}/path/to/source_2.f90
                               ${source_root}/INOUT/s4binout.f90
                       COMMENT "Generating fortran to python library")
    # post build command for python library (copying of generated files)
    add_custom_command(TARGET for_to_py
                       POST_BUILD
                       COMMAND ${CMAKE_COMMAND} -E copy_if_different 
                               ${build_root}/s4_to_py${CMAKE_PYTHON_LIBRARY_SUFFIX}
                               ${lib_root}/for_to_py${CMAKE_PYTHON_LIBRARY_SUFFIX}
                       COMMENT "\
***************************************************************************************************\n\
 copy of python library for_to_py${CMAKE_PYTHON_LIBRARY_SUFFIX} placed in ${lib_root}/for_to_py${CMAKE_PYTHON_LIBRARY_SUFFIX} \n\
***************************************************************************************************"
                       )
endif (PYTHONINTERP_FOUND)

使用修改后的pycompile:
#!python.exe
# -*- coding: UTF-8 -*-
...
fortran_source = ("@source_root@/source_1.f90 "
                  "@source_root@/source_2.f90 "                 
                  "...")
...
# add path to source code/ dependencies
f2py_source = ("-m for_to_py_lib "
               "@build_root@/for_to_py.f90 "
               "source_1.o "
               "source_2.o "
               "... "
               )
...

# compile .o and .mod files
...

0

指定库路径应使用 /LIBPATH 而不是 /L


谢谢。抱歉回复晚了,我没有注意到二月的评论……这并没有真正回答问题。实际上,对于ifort来说,应该是/libdir。 真正的问题是“如何更改dist-utils生成编译器参数列表的方式?”目前,bdforbes使用makefile的解决方案可行,但如果f2py可以处理整个过程,那就太好了。 - Tesla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接