f2py with Intel Fortran compiler
Question: I am trying to use f2py to interface my python programs with my Fortran
modules.

I am on a Win7 platform.

I use latest Anaconda 64 (1.7) as a Python+NumPy stack.

My Fortran compiler is the latest Intel Fortran compiler 64 (version
14.0.0.103 Build 20130728).

I have been experiencing a number of issues when executing `f2py -c -m
PyModule FortranModule.f90 --fcompiler=intelvem`

The last one, which I can't seem to sort out is that it looks like the
sequence of flags f2py/distutils passes to the compiler does not match what
ifort expects.

I get a series of warning messages regarding unknown options when ifort is
invoked.

    
    
    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'
    

I suspect this is related to the errors I get from the linker at the end

    
    
    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)
    

and it concludes with a plain

    
    
    fatal error LNK1120: 42 unresolved externals
    

My guess is that this is because the /link flag is missing in the sequence of
options. Because of this, the /l /L options are not passed to the linker and
the compiler believes these are addressed to him.

The ifort command generated by f2py looks like this:

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

I have no idea why the "-dll" is repeated twice (I had to change that flag
from an original "-shared").

Now, I have tried to look into the f2py and distutils codes but haven't
figured out how to bodge an additional /link in the command output. I haven't
even been able to locate where this output is generated.

If anyone has encountered this problem in the past and/or may have some
suggestions, I would very much appreciate it.

Thank you for your time


Answer: I encountered similar problems with my own code some time ago. If I understand
the comments correctly you already used the approach that worked for me, so
this is just meant as clarification and summary for all those that struggle
with f2py and dependencies:

f2py seems to have problems resolving dependecies on external source files. If
the external dependencies get passed to f2py as already compiled object files
though, the linking works fine and the python library gets build without
problems.

The easiest solution therefore seems to be:

  1. compile all dependencies to object files (*.o) using your prefered compiler and compiler settings
  2. pass all object files to f2py, together with the **source file** of your main subroutine/ function/ module/ ...
  3. use generated python library as expected

A simple python skript could look like this (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)
    

* * *

A more flexible solution for large projects could be provided via Makefile, as
dicussed by [@bdforbes](https://stackoverflow.com/users/336001/bdforbes) in
the comments ([for reference](http://pastebin.com/ChSxLzSb)) or a custom CMake
User Command in combination with the above skript:

    
    
    ###############################################################################
    # 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)
    

with modified 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
    ...
    

