python setuptools 编译 Fortran 代码并创建一个入口点

python setuptools compile fortran code and make an entry points

这是我的目录结构,

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

现在我想用命令行工具从中制作一个包 test。 现在我有两个选择,1.numpy distutils 和 2.setuptools。

distutils 的问题是它不支持入口点,现在也不推荐使用。但它确实完美地编译了 Fortran 代码。 现在关于 setuptools 我正在尝试使用此代码,

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',
    ],
  }
)

如果我尝试使用它,它会抛出这个错误

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

所以,我猜 setuptools 不支持 fortran 文件?那么,如何编译 Fortran 代码、创建包并为其创建入口点?

首先在 src/ 文件夹下创建一个名为 test 的文件夹,例如

src
  -- <package>
    -- test
        --- ***

然后添加一个MANIFEST.in并添加

recursive-include src/<package_name>/test *

将这两行包含在您的 setup.py

from setuptools import setup, find_packages
package_dir={'': 'src'},
packages=find_packages('src'),

对于您的控制台脚本,执行

entry_points={
            'console_scripts': [
                'hello=<package>.test:main',
                ],
        },

这实际上是一个非常简单的技巧。只需在从 numpy.distutils.core 导入 setup 之前导入 setuptools 就可以了。对此的解释是 numpy.distutils 不仅仅是普通的 distutils 有一些特定于包的调整。特别是,numpy.distutils 检查 setuptools 是否可用,如果可用,则尽可能在后台使用它。如果你有兴趣,look at the module's source code,注意have_setuptools标志的用法。

像往常一样,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

构建并安装包:

$ 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.]