将 C++ 扩展 headers 与 Python 包源分发捆绑在一起
Bundling C++ extension headers with a Python package source distribution
我正在为 C++ 库编写一个 Cython 包装器,我想将其作为 Python 包分发。我想出了一个我的包的虚拟版本,看起来像这样(完整来源 here)。
$ tree
.
├── bogus.pyx
├── inc
│ └── bogus.hpp
├── setup.py
└── src
└── bogus.cpp
$
$ cat inc/bogus.hpp
#ifndef BOGUS
#define BOGUS
class bogus
{
protected:
int data;
public:
bogus();
int get_double(int value);
};
#endif
$
$ cat src/bogus.cpp
#include "bogus.hpp"
bogus::bogus() : data(0)
{
}
int bogus::get_double(int value)
{
data = value * 2;
return data;
}
$ cat bogus.pyx
# distutils: language = c++
# distutils: sources = src/bogus.cpp
# cython: c_string_type=str, c_string_encoding=ascii
cdef extern from 'bogus.hpp':
cdef cppclass bogus:
bogus() except +
int get_double(int value)
cdef class Bogus:
cdef bogus b
def get_double(self, int value):
return self.b.get_double(value)
通过以下 setup.py
文件,我可以确认该库使用 python setup.py install
正确安装并且工作正常。
from setuptools import setup, Extension
import glob
headers = list(glob.glob('inc/*.hpp'))
bogus = Extension(
'bogus',
sources=['bogus.pyx', 'src/bogus.cpp'],
include_dirs=['inc/'],
language='c++',
extra_compile_args=['--std=c++11', '-Wno-unused-function'],
extra_link_args=['--std=c++11'],
)
setup(
name='bogus',
description='Troubleshooting Python packaging and distribution',
author='Daniel Standage',
ext_modules=[bogus],
install_requires=['cython'],
version='0.1.0'
)
但是,当我使用 python setup.py sdist build
构建源代码分发时,不包含 C++ header 文件并且无法编译 C++ 扩展。
如何确保 C++ header 文件与源代码分发捆绑在一起?!?!
<咆哮>
对此问题进行故障排除时,我发现了极其复杂且不一致的文档、建议和 hack,none 其中对我有用。在MANIFEST.in
中放一个graft
行?没有。 package_data
或 data_files
选项?没有。 Python 包装这几年好像进步了很多,但是对于我们这些没有生活气息的人来说还是难以理解Python 包装!
简答
将 include inc/*.hpp
放入 MANIFEST.in
文件。
长答案
根据各种博客文章和 SO 线程,我尝试了在 MANIFEST.in
文件中声明文件的建议。在 these instructions 之后,我向 MANIFEST.in
添加了 graft inc/
行以包含整个目录。这没有用。
但是,将此行替换为 include inc/*.hpp
确实有效。可以说这应该是我尝试的第一件事,但由于不熟悉 setuptools 和 distutils 的复杂性和缺陷,我没有理由认为 graft
不会起作用。
我正在为 C++ 库编写一个 Cython 包装器,我想将其作为 Python 包分发。我想出了一个我的包的虚拟版本,看起来像这样(完整来源 here)。
$ tree
.
├── bogus.pyx
├── inc
│ └── bogus.hpp
├── setup.py
└── src
└── bogus.cpp
$
$ cat inc/bogus.hpp
#ifndef BOGUS
#define BOGUS
class bogus
{
protected:
int data;
public:
bogus();
int get_double(int value);
};
#endif
$
$ cat src/bogus.cpp
#include "bogus.hpp"
bogus::bogus() : data(0)
{
}
int bogus::get_double(int value)
{
data = value * 2;
return data;
}
$ cat bogus.pyx
# distutils: language = c++
# distutils: sources = src/bogus.cpp
# cython: c_string_type=str, c_string_encoding=ascii
cdef extern from 'bogus.hpp':
cdef cppclass bogus:
bogus() except +
int get_double(int value)
cdef class Bogus:
cdef bogus b
def get_double(self, int value):
return self.b.get_double(value)
通过以下 setup.py
文件,我可以确认该库使用 python setup.py install
正确安装并且工作正常。
from setuptools import setup, Extension
import glob
headers = list(glob.glob('inc/*.hpp'))
bogus = Extension(
'bogus',
sources=['bogus.pyx', 'src/bogus.cpp'],
include_dirs=['inc/'],
language='c++',
extra_compile_args=['--std=c++11', '-Wno-unused-function'],
extra_link_args=['--std=c++11'],
)
setup(
name='bogus',
description='Troubleshooting Python packaging and distribution',
author='Daniel Standage',
ext_modules=[bogus],
install_requires=['cython'],
version='0.1.0'
)
但是,当我使用 python setup.py sdist build
构建源代码分发时,不包含 C++ header 文件并且无法编译 C++ 扩展。
如何确保 C++ header 文件与源代码分发捆绑在一起?!?!
<咆哮>
对此问题进行故障排除时,我发现了极其复杂且不一致的文档、建议和 hack,none 其中对我有用。在MANIFEST.in
中放一个graft
行?没有。 package_data
或 data_files
选项?没有。 Python 包装这几年好像进步了很多,但是对于我们这些没有生活气息的人来说还是难以理解Python 包装!
简答
将 include inc/*.hpp
放入 MANIFEST.in
文件。
长答案
根据各种博客文章和 SO 线程,我尝试了在 MANIFEST.in
文件中声明文件的建议。在 these instructions 之后,我向 MANIFEST.in
添加了 graft inc/
行以包含整个目录。这没有用。
但是,将此行替换为 include inc/*.hpp
确实有效。可以说这应该是我尝试的第一件事,但由于不熟悉 setuptools 和 distutils 的复杂性和缺陷,我没有理由认为 graft
不会起作用。