Python包设置:带有自定义功能的setup.py,用于处理封装的fortran



我有一个想要分发的python包。我已经设置好了软件包,可以下载tarball,解压缩并安装它,使用:

python setup.py install

这很好用。

我还想将包上传到PyPi,并使用pip进行安装。

然而,该包包含f2py封装的fortran,需要在构建时对其进行编译,并将生成的.so文件移到最终的安装文件夹中。我很困惑如何使用来做到这一点

python3 setup.py sdist

然后是:

pip3 install pkg_name_here.tar.gz

原因是当我运行时

python3 setup.py sdist

自定义命令正在运行,其中一部分是试图将已编译的*so文件移动到尚未创建的安装文件夹中。我使用的代码大纲的一个例子是在这个例子中:

from setuptools.command.install import install
from setuptools.command.develop import develop
from setuptools.command.egg_info import egg_info
'''
BEGIN CUSTOM INSTALL COMMANDS
These classes are used to hook into setup.py's install process. Depending on the context:
$ pip install my-package
Can yield `setup.py install`, `setup.py egg_info`, or `setup.py develop`
'''

def custom_command():
import sys
if sys.platform in ['darwin', 'linux']:
os.system('./custom_command.sh')

class CustomInstallCommand(install):
def run(self):
install.run(self)
custom_command()

class CustomDevelopCommand(develop):
def run(self):
develop.run(self)
custom_command()

class CustomEggInfoCommand(egg_info):
def run(self):
egg_info.run(self)
custom_command()
'''
END CUSTOM INSTALL COMMANDS 
'''
setup(
...
cmdclass={
'install': CustomInstallCommand,
'develop': CustomDevelopCommand,
'egg_info': CustomEggInfoCommand,
},
...
)

在我的实例中,custom_command((编译并包装fortran,并将lib文件复制到安装文件夹中。

我想知道的是,是否有一种方法可以在安装pip时只运行这些自定义命令?即避免在打包过程中运行custom_command((,只在安装过程中运行。

更新

根据皮埃尔·德布伊尔的建议,我已经取得了一些进展,但仍然没有做到这一点。

setup.py文件当前看起来像:

def setup_f90_ext(parent_package='',top_path=''):
from numpy.distutils.misc_util import Configuration
from os.path import join
config = Configuration('',parent_package,top_path)
tort_src = [join('PackageName/','tort.f90')]
config.add_library('tort', sources=tort_src,
extra_f90_compile_args=['-fopenmp -lgomp -O3'],
extra_link_args=['-lgomp'])
sources = [join('PackageName','f90wrap_tort.f90')]
config.add_extension(name='',
sources=sources,
extra_f90_compile_args=['-fopenmp -lgomp -O3'],
libraries=['tort'],
extra_link_args=['-lgomp'],
include_dirs=['build/temp*/'])
return config
if __name__ == '__main__':
from numpy.distutils.core import setup
import subprocess
import os
import sys
version_file = open(os.getcwd()+'/PackageName/'+ 'VERSION')
__version__ = version_file.read().strip()

subprocess.call(cmd, shell=True)
config = {'name':'PackageName',
'version':__version__,
'project_description':'Package description',
'description':'Description',
'long_description': open('README.txt').read(),#read('README.txt'),
}
config2 = dict(config,**setup_f90_ext(parent_package='PackageName',top_path='').todict())
setup(**config2)   

其中f90wrap_tort.f90是f90包装的fortran文件,tort.f90是原始fortran。

如果我运行两次命令,此文件可与python setup.py install一起使用

第一次运行python setup.py install时,我得到以下错误:

gfortran:f90: ./PackageName/f90wrap_tort.f90
f951: Warning: Nonexistent include directory ‘build/temp*/’ [-Wmissing-include-dirs]
./PackageName/f90wrap_tort.f90:4:8:
use tort_mod, only: test_node
1
Fatal Error: Can't open module file ‘tort_mod.mod’ for reading at (1): No such file or directory
compilation terminated.
f951: Warning: Nonexistent include directory ‘build/temp*/’ [-Wmissing-include-dirs]
./PackageName/f90wrap_tort.f90:4:8:
use tort_mod, only: test_node
1
Fatal Error: Can't open module file ‘tort_mod.mod’ for reading at (1): No such file or directory

我之所以在扩展中放入include_dirs=['build/temp*/']参数,是因为在运行python setup.py install之后,我注意到tort_mod第一次被构建并存储在那里。

我想不出的是如何正确地进行链接,以便一步到位。

有人能看到我缺了什么吗?

经过一点谷歌搜索,我建议如下:

  1. 使用NumPy的distutils
  2. 对于普通Fortran文件,请使用add_library关键字(见此处(。这将把Fortran文件构建为一个库,但不尝试用f2py与它们接口
  3. 使用f90wrap预构建f90包装器,将它们包含在包归档中,并在扩展中指定这些文件作为源文件

我没有测试整个解决方案,因为它有点耗时,但这就是SciPy为他们的一些模块所做的,请参阅此处。

NumPy的文档在add_library上有一个项目

编辑1:在使用include_dirs=['build/temp.linux-x86_64-2.7'])配置进行构建后,我在第一次尝试构建时就获得了这个目录结构。

build/lib.linux-x86_64-2.7
├── crystal_torture
│   ├── cluster.py
│   ├── dist.f90
│   ├── f90wrap_tort.f90
│   ├── graph.py
│   ├── __init__.py
│   ├── minimal_cluster.py
│   ├── node.py
│   ├── node.pyc
│   ├── pymatgen_doping.py
│   ├── pymatgen_interface.py
│   ├── tort.f90
│   ├── tort.py
│   └── tort.pyc
└── crystal_torture.so

最新更新