Search code examples
pythonfortranshared-librariesdistutils

Automatically build shared fortran library in setup.py and link it to f2py


I am looking for the best way to generate a setup.py file which build my package based on Fortran routines.

Package/Module structure

.
├── CHANGES.txt
├── LICENSE.txt
├── MANIFEST.in
├── README.md
├── PKG
│   ├── __init__.py
│   ├── lib
│   │   ├── __init__.py
│   │   ├── xx.f90
│   │   ├── yy.f90
│   │   ├── gg.py
│   ├── dirA
│   │   ├── __init__.py
│   │   ├── ee.py
│   │   ├── bb.py
├── setup.py

Manual compilation of Fortran's files

In order to generate the Python wrapper and compile the Fortran files, I can do it using the following workflow (in the folder lib):

gfortran -fPIC -Wall -fcheck=all --shared xx.f90 -o xx.o [1]

and

f2py -c -m yy yy.f90 xx.o

The previous line insures the linking between the shared library xx.o and the library `yy.f90.

Notice that f2py -c -m xx xx.f90 does not work because of the use of some syntaxes which are not usable with f2py (use of allocatable arguments in subroutines...).

Current setup.py (not working)

I use the Extension module of numpy.distutils:

import os
from numpy.distutils.core import setup, Extension
#
pkgs['PKG'] = 'PKG'
pkgs['PKG.lib'] = 'PKG/lib'
pkgs['PKG.dirA'] = 'PKG/dirA'
#
exts = list()
exts.append(
    Extension(name='PKG.lib.yy',
              sources=['PKG/lib/yy.f90','PKG/lib/xx.o'],
              extra_compile_args=['-g','--backtrace'],
              extra_link_args=[],
              f2py_options=['--debug-capi']))
    ###
    ### 
    setup(name = 'PKG',
          ext_modules = exts,
          packages = list(pkgs.keys()),
          package_dir = pkgs,
          include_package_data=True,
          zip_safe=False
          )

Question

Is there any way to integrate the building of the shared library in the setup.py? One solution could be to run manually the line [1] but if possible I want to avoid to use my own syntax in order to use the same gfortran compiler and options used by f2py.


Solution

  • Finally the solution is to use library/libraries arguments in extension and setup:

    import os
    from numpy.distutils.core import setup, Extension
    #
    pkgs['PKG'] = 'PKG'
    pkgs['PKG.lib'] = 'PKG/lib'
    pkgs['PKG.dirA'] = 'PKG/dirA'
    #
    exts = list()
    exts.append(
        Extension(name='PKG.lib.yy',
                  sources=['PKG/lib/yy.f90'],
                  extra_compile_args=['-g','--backtrace'],
                  extra_link_args=[],
                  libraries=['xx'],
                  f2py_options=['--debug-capi']))
        ###
        ### 
        setup(name = 'PKG',
              ext_modules = exts,
              packages = list(pkgs.keys()),
              package_dir = pkgs,
              libraries=['xx',sources=['PKG/lib/xx.f90']],
              include_package_data=True,
              zip_safe=False
              )
    

    Another solution using Configuration

    from numpy.distutils.core import setup
    from numpy.distutils.misc_util import Configuration
    
    config = Configuration('PKG')
    config.packages=['PKG','PKG.lib']
    config.add_extension('lib.yy',
        sources=['PKG/lib/MeshField2D.f90'],
        libraries=['xx'])
    
    config.add_library('xx',
        sources=['PKG/lib/xx.f90'])   
    
    setup(**config.todict())