Настройка пакета Python: setup.py с настройкой для обработки обернутого Фортрана


У меня есть пакет 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 - это f90wrapped файл fortran, и деликт.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 2

1 ответ:

Немного погуглив, я предлагаю следующее:

  1. Используйте distutils NumPy
  2. используйте ключевое слово add_library (см.здесь ) для ваших простых файлов Fortran. Это позволит построить файлы Fortran как библиотеку, но не пытаться взаимодействовать с ними с помощью f2py.
  3. предварительно соберите обертки f90 с помощью f90wrap, включите их в свой архив пакетов и укажите эти файлы в качестве источника в расширении.

Я не тестировал все решение, так как это занимает немного времени, но это то, что SciPy делает для некоторых своих модулей, см. здесь.

Документация NumPy имеет пункт над add_library

EDIT 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