【问题标题】:Python package setup: setup.py with customisation to handle wrapped fortranPython 包设置:setup.py 自定义处理包装的 fortran
【发布时间】:2019-01-03 21:18:28
【问题描述】:

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

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 文件移动到尚未创建的安装文件夹中。我使用的代码大纲示例在此示例中here

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() 在打包过程中运行,并且只在安装过程中运行。

更新

按照 Pierre de Buyl 的建议,我取得了一些进展,但仍然没有这个工作。

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 是 f90wrapped 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 被构建并存储在那里。

我想不通的是如何使链接正确,以便一步完成。

谁能看到我错过了什么?

【问题讨论】:

  • 你用什么工具来绑定Fortran代码? (f2py,iso_c_binding,其他?)您是否知道 NumPy 为此目的进行了 distutils 修改? docs.scipy.org/doc/numpy/reference/distutils.html
  • 我使用 f2py 和 f2py-f90wrap。不幸的是,据我所知,disutils 扩展无法处理 f90wrap 部分。
  • 路径build/temp* 不是实际路径。如果只有一个这样的临时目录,请添加from glob import glob,然后改用include_dirs=glob('build/temp*/')
  • 试过皮埃尔,也没用。如果上面的代码不够清晰,可以从test-files.pythonhosted.org/packages/4b/09/… 获取完整的包。仍然不知道如何让它工作......
  • 我用include_dirs=['build/temp.linux-x86_64-2.7']) 构建它你现在有什么错误?

标签: python pip fortran distutils setup.py


【解决方案1】:

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

  1. 使用 NumPy 的 distutils
  2. 对普通的 Fortran 文件使用 add_library 关键字(见 here)。这会将 Fortran 文件构建为库,但不会尝试使用 f2py 与它们交互。
  3. 使用 f90wrap 预构建 f90 包装器,将它们包含在您的包存档中,并将这些文件指定为扩展中的源。

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

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

【讨论】:

  • 感谢您找到这个 - 看起来很有希望,但我似乎无法让它正常运行。我已经更新了帖子以包含您的建议。
  • 太棒了 - 你是对的。现在我只需要放一个函数来确定这个文件夹在任何机器上的名称是什么,我应该很高兴。谢谢一百万!
猜你喜欢
  • 1970-01-01
  • 2020-01-08
  • 1970-01-01
  • 2017-09-03
  • 2015-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-02
相关资源
最近更新 更多