Python 包设置:setup.py 自定义处理包装的 Fortran
Python package setup: setup.py with customisation to handle wrapped 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文件移动到尚未创建的安装文件夹。我使用的代码大纲的一个例子是在这个例子 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 安装过程中,是否有办法只 运行ning 这些自定义命令?即避免 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 是 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
被构建并存储在那里。
我想不通的是如何使链接正确,以便一步完成。
谁能看到我错过了什么?
经过一番谷歌搜索,我提出以下建议:
- 使用 NumPy 的 distutils
- 对纯 Fortran 文件使用
add_library
关键字(见 here)。这会将 Fortran 文件构建为库,但不会尝试使用 f2py 与它们交互。
- 使用 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
我有一个 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文件移动到尚未创建的安装文件夹。我使用的代码大纲的一个例子是在这个例子 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 安装过程中,是否有办法只 运行ning 这些自定义命令?即避免 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 是 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
被构建并存储在那里。
我想不通的是如何使链接正确,以便一步完成。
谁能看到我错过了什么?
经过一番谷歌搜索,我提出以下建议:
- 使用 NumPy 的 distutils
- 对纯 Fortran 文件使用
add_library
关键字(见 here)。这会将 Fortran 文件构建为库,但不会尝试使用 f2py 与它们交互。 - 使用 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