本文介绍了用于混合 Python 和 C++ 的正确 setup.py的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试混合两种语言,我正在遵循 pybind here.我实际上检查了 这个发布 以改进它,以便在编译函数不存在时我可以回退到 Python 函数.我现在遇到的问题是我的 configure.py 没有构建正确的包.让我开发:我的代码结构是这样的:

$ 树..├── AUTHORS.md├── CMakeLists.txt├── 执照├── MANIFEST.in├── 生成文件├── README.md├── conda.recipe│ ├── bld.bat│ └── ...├── 文档│ ├── Makefile│ └── ...├── cmake_example│ ├── __init__.py│ ├── __main__.py│ ├──几何│ │ ├── __init__.py│ │ ├── triangle.py│ │ └── ...│ ├──正交│ │ ├── __init__.py│ │ ├──传奇│ │ └── ...│ └── 实用程序│ ├── __init__.py│ ├── classes.py│ └── ...├── pybind11│ ├── CMakeLists.txt│ └── ...├── setup.py├── src│ └── main.cpp└── 测试└── test.py

我放省略号的地方是为了简化目录结构,但是你可以看到有几个模块.现在我的 setup.py 文件看起来像这样

导入操作系统进口重新导入系统进口平台导入子流程导入全局从 setuptools 导入设置、扩展、find_packages从 setuptools.command.build_ext 导入 build_ext从 distutils.version 导入 LooseVersion类 CMakeExtension(扩展):def __init__(self, name, sourcedir=''):扩展名.__init__(self, name, sources=[])self.sourcedir = os.path.abspath(sourcedir)类 CMakeBuild(build_ext):定义运行(自我):尝试:out = subprocess.check_output(['cmake', '--version'])除了 OSError:raise RuntimeError("必须安装 CMake 才能构建以下扩展:" +", ".join(e.name for e in self.extensions))如果 platform.system() == "Windows":cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1))如果 cmake_version <'3.1.0':raise RuntimeError("CMake >= 3.1.0 is required on Windows")对于 self.extensions 中的 ext: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]如果 platform.system() == "Windows":cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)]如果 sys.maxsize >2**32:cmake_args += ['-A', 'x64']build_args += ['--', '/m']别的: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())如果不是 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(名称="cmake_example",ext_modules=[CMakeExtension('cmake_example._mymath')],cmdclass=dict(build_ext=CMakeBuild),zip_safe=假,包='cmake_example',)# 可能有更多的例外尝试:设置(**kwargs)除了 subprocess.CalledProcessError:打印(错误:无法编译C加速器模块,请使用纯python版本")del kwargs['ext_modules']设置(**kwargs)

我取自 这篇文章.当我尝试使用 python setup.py bdist_wheel 构建轮子,然后使用 pip install . 安装时,我无法使用我的代码,因为它抱怨包未找到:

>>>导入 cmake_example回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中文件/Users/aaragon/Local/cmake_example/cmake_example/__init__.py",第 11 行,在 <module>从 .geometry 导入三角形ModuleNotFoundError:没有名为cmake_example.geometry"的模块

如果我使用 packages=['cmake_example', cmake_example.geometry] 手动添加 setup.py 列表,那么它可以工作,但我不认为这是正确的方法,因为要跟上添加新模块的步伐会非常困难.我在某处看到我可以替换该行并使用 setuptools 的 findpackages,但是此函数并未将 cmake_example 预先添加到模块中,因此它仍然会中断.做我想做的事情的正确方法是什么?

解决方案

无论是手动完成,还是在难以跟上添加新模块的步伐时,setuptools.find_packages.像这样使用:

from setuptools import setup, find_packages设置(name="HelloWorld",版本=0.1",包=find_packages(),)

I'm trying to mix both languages and I'm following the nice example provided by pybind here. I actually checked this post to improve on it so I can fall back to Python functions whenever a compiled function doesn't exist. The problem I have now is that my configure.py is not building the correct package. Let me develop: the structure of my code is something like this:

$ 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

Where I put ellipsis to simplify the directory structure, but you can see there are a few modules. Now my setup.py file looks like this

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)

which I took from this post. When I try to build the wheel using python setup.py bdist_wheel, and then I install using pip install ., I can't use my code because it complains that packages are not found:

>>> 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'

If I manually add in setup.py the list with packages=['cmake_example', cmake_example.geometry] then it works, but I don't think this is the right way to do it because it would be super hard to keep up with adding new modules. I saw somewhere I could replace that line and use setuptools's findpackages, but this function doesn't prepend the cmake_example to the module, so it still breaks. What's the correct way to do what I'm trying to do?

解决方案

Either you do it manually or when it's getting hard to keep up with adding new modules there's setuptools.find_packages. Use like:

from setuptools import setup, find_packages
setup(
    name="HelloWorld",
    version="0.1",
    packages=find_packages(),
)

这篇关于用于混合 Python 和 C++ 的正确 setup.py的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-01 00:46