Правильный setup.py для смешивания Python и C++
Я пытаюсь смешать оба языка и следую хорошему примеру, представленному здесь Pybind. Я действительно проверил этот пост, чтобы улучшить его, чтобы я мог вернуться к функциям Python, когда скомпилированная функция не существует. Проблема у меня сейчас в том, что мой configure.py
не строит правильный пакет. Позвольте мне развить: структура моего кода примерно такая:
$ tree .
.
├── AUTHORS.md
├── CMakeLists.txt
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── conda.recipe
│ ├── bld.bat
│ └── ...
├── docs
│ ├── Makefile
│ └── ...
├── cmake_example
│ ├── __init__.py
│ ├── __main__.py
│ ├── geometry
│ │ ├── __init__.py
│ │ ├── triangle.py
│ │ └── ...
│ ├── quadrature
│ │ ├── __init__.py
│ │ ├── legendre
│ │ └── ...
│ └── utils
│ ├── __init__.py
│ ├── classes.py
│ └── ...
├── pybind11
│ ├── CMakeLists.txt
│ └── ...
├── setup.py
├── src
│ └── main.cpp
└── tests
└── test.py
Где я положил многоточие, чтобы упростить структуру каталогов, но вы можете видеть, что есть несколько модулей. Теперь мой setup.py
файл выглядит так
import os
import re
import sys
import platform
import subprocess
import glob
from setuptools import setup, Extension, find_packages
from setuptools.command.build_ext import build_ext
from distutils.version import LooseVersion
class CMakeExtension(Extension):
def __init__(self, name, sourcedir=''):
Extension.__init__(self, name, sources=[])
self.sourcedir = os.path.abspath(sourcedir)
class CMakeBuild(build_ext):
def run(self):
try:
out = subprocess.check_output(['cmake', '--version'])
except OSError:
raise RuntimeError("CMake must be installed to build the following extensions: " +
", ".join(e.name for e in self.extensions))
if platform.system() == "Windows":
cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1))
if cmake_version < '3.1.0':
raise RuntimeError("CMake >= 3.1.0 is required on Windows")
for ext in self.extensions:
self.build_extension(ext)
def build_extension(self, ext):
extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir,
'-DPYTHON_EXECUTABLE=' + sys.executable]
cfg = 'Debug' if self.debug else 'Release'
build_args = ['--config', cfg]
if platform.system() == "Windows":
cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)]
if sys.maxsize > 2**32:
cmake_args += ['-A', 'x64']
build_args += ['--', '/m']
else:
cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg]
build_args += ['--', '-j2']
env = os.environ.copy()
env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''),
self.distribution.get_version())
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env)
subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp)
kwargs = dict(
name="cmake_example",
ext_modules=[CMakeExtension('cmake_example._mymath')],
cmdclass=dict(build_ext=CMakeBuild),
zip_safe=False,
packages='cmake_example',
)
# likely there are more exceptions
try:
setup(**kwargs)
except subprocess.CalledProcessError:
print("ERROR: Cannot compile C accelerator module, use pure python version")
del kwargs['ext_modules']
setup(**kwargs)
который я взял из этого поста. Когда я пытаюсь построить колесо, используя python setup.py bdist_wheel
, а затем я устанавливаю с помощью pip install .
Я не могу использовать свой код, потому что он жалуется, что пакеты не найдены:
>>> import cmake_example
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/aaragon/Local/cmake_example/cmake_example/__init__.py", line 11, in <module>
from .geometry import Triangle
ModuleNotFoundError: No module named 'cmake_example.geometry'
Если я вручную добавлю в setup.py
список с packages=['cmake_example', cmake_example.geometry]
тогда это работает, но я не думаю, что это правильный способ сделать это, потому что было бы очень сложно не отставать от добавления новых модулей. Я видел где-то, что я мог бы заменить эту строку и использовать setuptools's findpackages
, но эта функция не предшествует cmake_example
к модулю, поэтому он все еще ломается. Как правильно делать то, что я пытаюсь сделать?
1 ответ
Если я вручную добавлю в setup.py список с package =['cmake_example', cmake_example.geometry], то это сработает, но я не думаю, что это правильный способ сделать это, потому что было бы очень сложно поспевать добавление новых модулей.
Либо вы делаете это вручную, либо, когда становится трудно идти в ногу с добавлением новых модулей, есть setuptools.find_packages
, Используйте как:
from setuptools import setup, find_packages
setup(
name="HelloWorld",
version="0.1",
packages=find_packages(),
)