Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python setuptools compile fortran code and make an entry points

Here's my directory structure,

├── test
│   ├── test.f90
│   ├── __init__.py
│   └── test.py

Now I want to make a package from this with an command line tool test. Now I have two options, 1. numpy distutils and 2. setuptools.

Problem with distutils is that it doesn't support entry points and it is also not recomended now. But it does compile the fortran code perfectly. Now as for setuptools I'm trying to use this code,

mod = Extension(name = 'foo.adt', sources = ['test/test.f90'])
setup(
  name = 'foo',
  packages = ['foo'],
  package_dir = {'foo':'test'},
  ext_modules = [mod],
  entry_points={
    'console_scripts': [
        'hello = foo.test:main',
    ],
  }
)

If I try to use this, it's throwing this error

error: unknown file type '.f90' (from 'test/test.f90')

So, I guess setuptools doesn't support fortran files? So, how do I compile the fortran code, create the package and create a entry point for that?

like image 436
Eular Avatar asked Mar 26 '19 08:03

Eular


1 Answers

It's actually a pretty simple trick. Just import setuptools before importing setup from numpy.distutils.core and you're good to go. The explanation for this is that numpy.distutils is much more than just the vanilla distutils with some package-specific tweaks. In particular, numpy.distutils checks whether setuptools is available and if so, uses it where possible under the hood. If you're interested, look at the module's source code, paying attention to the usages of have_setuptools flag.

As usual, a Minimal, Complete, and Verifiable example:

so-55352409/
├── spam
│   ├── __init__.py
│   ├── cli.py
│   └── libfib.f90
└── setup.py

setup.py:

import setuptools  # this is the "magic" import
from numpy.distutils.core import setup, Extension


lib = Extension(name='spam.libfib', sources=['spam/libfib.f90'])

setup(
    name = 'spamlib',
    packages = ['spam'],
    ext_modules = [lib],
    entry_points={
        'console_scripts': [
            'hello = spam.cli:main',
        ],
    }
)

spam/cli.py:

from spam.libfib import fib


def main():
    print(fib(10))

spam/libfib.f90:

C FILE: LIBFIB.F90
      SUBROUTINE FIB(A,N)
C
C     CALCULATE FIRST N FIBONACCI NUMBERS
C
      INTEGER N
      REAL*8 A(N)
Cf2py intent(in) n
Cf2py intent(out) a
Cf2py depend(n) a
      DO I=1,N
         IF (I.EQ.1) THEN
            A(I) = 0.0D0
         ELSEIF (I.EQ.2) THEN
            A(I) = 1.0D0
         ELSE 
            A(I) = A(I-1) + A(I-2)
         ENDIF
      ENDDO
      END
C END FILE LIBFIB.F90

Build and install the package:

$ cd so-55352409
$ python setup.py bdist_wheel
...
$ pip install dist/spamlib-0.0.0-cp36-cp36m-linux_x86_64.whl
...
$ hello
[ 0.  1.  1.  2.  3.  5.  8. 13. 21. 34.]
like image 193
hoefling Avatar answered Sep 17 '22 01:09

hoefling