通过 cmake 从 C++ 扩展构建 Python 个子模块
Building Python submodules from C++ extensions via cmake
我正在尝试通过 cmake 将 c++ 扩展作为子模块合并到现有的 python 库中。构建 C++ 扩展工作正常并将其作为 python 模块导入,但不能作为头文件库的子模块。
我的目录结构如下:
frontend/
foo.py
bar.py
backend/
backend.cpp
扩展通过 pybind 绑定到 python 模块:
PYBIND11_MODULE(backend, m)
{
m.doc() = "backend c++ implementation"; // optional module docstring
m.def("method", &method, "The method I want to call from python.");
}
在CMakeLists.txt中,相关行是:
pybind11_add_module(backend "frontend/backend/backend.cpp")
我已经按照 here and here 的说明编写了 setup.py 脚本。我想最重要的几行是这样的:
from setuptools import setup, Extension, find_packages
from setuptools.command.build_ext import build_ext
from setuptools.command.test import test as TestCommand
class CMakeExtension(Extension):
def __init__(self, name, sourcedir=".", sources=[]):
Extension.__init__(self, name, sources=[])
class CMakeBuild(build_ext):
def run(self):
build_directory = os.path.abspath(self.build_temp)
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
cmake_list_dir = os.path.abspath(os.path.dirname(__file__))
print("-" * 10, "Running CMake prepare", "-" * 40)
subprocess.check_call(
["cmake", cmake_list_dir], cwd=self.build_temp,
)
print("-" * 10, "Building extensions", "-" * 40)
cmake_cmd = ["cmake", "--build", "."] + self.build_args
subprocess.check_call(cmake_cmd, cwd=self.build_temp)
# Move from build temp to final position
for ext in self.extensions:
self.move_output(ext)
def move_output(self, ext):
build_temp = Path(self.build_temp).resolve()
dest_path = Path(self.get_ext_fullpath(ext.name)).resolve()
source_path = build_temp / self.get_ext_filename(ext.name)
dest_directory = dest_path.parents[0]
dest_directory.mkdir(parents=True, exist_ok=True)
self.copy_file(source_path, dest_path)
extensions = [CMakeExtension("backend")]
setup(
name="frontend",
packages=["frontend"],
ext_modules=extensions,
cmdclass=dict(build_ext=CMakeBuild),
)
但这不会使 backend
成为 frontend
的子模块,而是成为一个独立的模块。所以这有效:
from backend import method
但是为了避免与其他库的命名问题,我想要的是:
from frontend.backend import method
不幸的是,将 pybinding 或扩展调用中的命名更改为 extensions = [CMakeExtension("frontend.backend")]
并没有解决我的问题,安装程序没有找到 backend.<platform>.so
共享库,因为它寻找 frontend/backend.<platform>.so
,这是不存在的。我该如何解决这个问题?
我想我已经通过以下几行解决了问题:
setup.py 文件中的更改:
ext_modules = [
Extension(
"frontend.backend", sources=["frontend/backend/backend.cpp"]
)
]
CMakeLists.txt 文件中的更改:
pybind11_add_module(backend "frontend/backend/backend.cpp")
set_target_properties( backend
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/frontend"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/frontend"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/frontend"
)
共享库对象backend.platform.so 必须位于前端目录中。 pybind 模块名称和源文件 .cpp 都不应包含任何“.”。在名称中,因为 build_ext
中的 get_ext_fullpath()
方法将按点拆分。只有前端目录包含一个 init.py 文件。
我正在尝试通过 cmake 将 c++ 扩展作为子模块合并到现有的 python 库中。构建 C++ 扩展工作正常并将其作为 python 模块导入,但不能作为头文件库的子模块。
我的目录结构如下:
frontend/
foo.py
bar.py
backend/
backend.cpp
扩展通过 pybind 绑定到 python 模块:
PYBIND11_MODULE(backend, m)
{
m.doc() = "backend c++ implementation"; // optional module docstring
m.def("method", &method, "The method I want to call from python.");
}
在CMakeLists.txt中,相关行是:
pybind11_add_module(backend "frontend/backend/backend.cpp")
我已经按照 here and here 的说明编写了 setup.py 脚本。我想最重要的几行是这样的:
from setuptools import setup, Extension, find_packages
from setuptools.command.build_ext import build_ext
from setuptools.command.test import test as TestCommand
class CMakeExtension(Extension):
def __init__(self, name, sourcedir=".", sources=[]):
Extension.__init__(self, name, sources=[])
class CMakeBuild(build_ext):
def run(self):
build_directory = os.path.abspath(self.build_temp)
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
cmake_list_dir = os.path.abspath(os.path.dirname(__file__))
print("-" * 10, "Running CMake prepare", "-" * 40)
subprocess.check_call(
["cmake", cmake_list_dir], cwd=self.build_temp,
)
print("-" * 10, "Building extensions", "-" * 40)
cmake_cmd = ["cmake", "--build", "."] + self.build_args
subprocess.check_call(cmake_cmd, cwd=self.build_temp)
# Move from build temp to final position
for ext in self.extensions:
self.move_output(ext)
def move_output(self, ext):
build_temp = Path(self.build_temp).resolve()
dest_path = Path(self.get_ext_fullpath(ext.name)).resolve()
source_path = build_temp / self.get_ext_filename(ext.name)
dest_directory = dest_path.parents[0]
dest_directory.mkdir(parents=True, exist_ok=True)
self.copy_file(source_path, dest_path)
extensions = [CMakeExtension("backend")]
setup(
name="frontend",
packages=["frontend"],
ext_modules=extensions,
cmdclass=dict(build_ext=CMakeBuild),
)
但这不会使 backend
成为 frontend
的子模块,而是成为一个独立的模块。所以这有效:
from backend import method
但是为了避免与其他库的命名问题,我想要的是:
from frontend.backend import method
不幸的是,将 pybinding 或扩展调用中的命名更改为 extensions = [CMakeExtension("frontend.backend")]
并没有解决我的问题,安装程序没有找到 backend.<platform>.so
共享库,因为它寻找 frontend/backend.<platform>.so
,这是不存在的。我该如何解决这个问题?
我想我已经通过以下几行解决了问题:
setup.py 文件中的更改:
ext_modules = [
Extension(
"frontend.backend", sources=["frontend/backend/backend.cpp"]
)
]
CMakeLists.txt 文件中的更改:
pybind11_add_module(backend "frontend/backend/backend.cpp")
set_target_properties( backend
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/frontend"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/frontend"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/frontend"
)
共享库对象backend.platform.so 必须位于前端目录中。 pybind 模块名称和源文件 .cpp 都不应包含任何“.”。在名称中,因为 build_ext
中的 get_ext_fullpath()
方法将按点拆分。只有前端目录包含一个 init.py 文件。