Python 2.7 使用 CMake 和 SetupTools 的 C++ 扩展?
Python 2.7 C++ extensions with CMake and SetupTools?
概览
我有一个提供 Python 2.7 包装器的 C++ 项目。我正在使用 CMake 构建该项目,我想要一个 setup.py 文件,以便我可以轻松地将此项目 pip install git+https://mygitrepo/my_project
放入 virtualenv 中。我已经成功地达到了可以使用 pip 来构建我的项目的地步,但是我不知道如何在我的 setup.py 文件或我的 CMakeLists.txt 中指定我的二进制文件应该安装在哪里。
详情
当我使用 CMake(例如 mkdir /path/to/my_project/build && cd /path/to/my_project/build && cmake .. && make
)构建我的项目时,一切都正确构建,我最终得到一个 libmy_project.so
和一个 pymy_project.so
文件,我可以成功导入和使用在 Python.
当我在 virtualenv 中使用 pip 构建项目时(例如 pip install /path/to/my_project -v
),我可以看到 CMake 和我的编译器正在按预期进行编译。然而,它们建立在一个临时目录中并立即被丢弃。 如何告诉我的 setup.py 要保留哪些文件,以及将它们放在哪里?
相关文件
我的 CMakeLists.txt 如下所示:
cmake_minimum_required (VERSION 3.0.0)
project (my_project)
# Find SomePackage
find_package(SomePackage REQUIRED COMPONENTS cool_unit great_unit)
include_directories(${SomePackage_INCLUDE_DIR})
# Find Python
find_package(PythonLibs 2.7 REQUIRED)
include_directories(${PYTHON_INCLUDE_DIR})
# Build libmy_project
add_library(my_project SHARED src/MyProject.cpp)
target_link_libraries(my_project ${SomePackage_LIBRARIES} ${PYTHON_LIBRARIES})
# Build py_my_project
add_library(py_my_project SHARED src/python/pyMyProject.cpp)
set_target_properties(py_my_project PROPERTIES PREFIX "")
target_link_libraries(py_my_project ${SomePackage_LIBRARIES}
${PYTHON_LIBRARIES} my_project)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/__init__.py "__all__ = ['py_my_project']")
我的 setup.py 文件如下所示:
import os
import subprocess
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
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):
for ext in self.extensions:
self.build_extension(ext)
def build_extension(self, ext):
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
subprocess.check_call(['cmake', ext.sourcedir], cwd=self.build_temp)
subprocess.check_call(['cmake', '--build', '.'], cwd=self.build_temp)
setup(
name='my_project',
version='0.0.1',
author='Me',
author_email='rcv@whosebug.com',
description='A really cool and easy to install library',
long_description='',
ext_modules=[CMakeExtension('.')],
cmdclass=dict(build_ext=CMakeBuild),
zip_safe=False,
)
“我如何告诉我的 setup.py 哪些文件要保留,以及将它们放在哪里?”的答案似乎是 self.get_ext_fullpath(ext.name)
如 this solution as pointed out by abarnert and hoefling 中所见。此方法 returns 一个临时根目录,您可以将构建输出放入其中。然后 Pip 似乎会扫描该目录,并在构建完成后将所有内容复制到您的真实环境中。
不幸的是,Linux 动态链接器无法为我的扩展找到我的依赖项(libmy_project.so
),因为 virtualenv 没有设置 LD_LIBRARY_PATH
或任何东西,所以我结束了只是将所有编译单元滚动到我的扩展中。完整的解决方案如下:
setup.py
import os
import subprocess
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
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):
for ext in self.extensions:
self.build_extension(ext)
def build_extension(self, ext):
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
extdir = self.get_ext_fullpath(ext.name)
if not os.path.exists(extdir):
os.makedirs(extdir)
# This is the temp directory where your build output should go
install_prefix = os.path.abspath(os.path.dirname(extdir))
cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={}'.format(install_prefix)]
subprocess.check_call(['cmake', ext.sourcedir, cmake_args], cwd=self.build_temp)
subprocess.check_call(['cmake', '--build', '.'], cwd=self.build_temp)
setup(
name='my_project',
version='0.0.1',
author='Me',
author_email='rcv@whosebug.com',
description='A really cool and easy to install library',
long_description='',
ext_modules=[CMakeExtension('.')],
cmdclass=dict(build_ext=CMakeBuild),
zip_safe=False,
install_requires=[
'cmake',
]
)
CMakeLists.txt
cmake_minimum_required (VERSION 3.0.0)
project (my_project)
# Find SomePackage
find_package(SomePackage REQUIRED COMPONENTS cool_unit great_unit)
include_directories(${SomePackage_INCLUDE_DIR})
# Find Python
find_package(PythonLibs 2.7 REQUIRED)
include_directories(${PYTHON_INCLUDE_DIR})
# Build py_my_project
add_library(py_my_project SHARED src/python/pyMyProject.cpp src/MyProject.cpp)
set_target_properties(py_my_project PROPERTIES PREFIX "")
target_link_libraries(py_my_project ${SomePackage_LIBRARIES}
${PYTHON_LIBRARIES})
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/__init__.py "__all__ = ['py_my_project']")
概览
我有一个提供 Python 2.7 包装器的 C++ 项目。我正在使用 CMake 构建该项目,我想要一个 setup.py 文件,以便我可以轻松地将此项目 pip install git+https://mygitrepo/my_project
放入 virtualenv 中。我已经成功地达到了可以使用 pip 来构建我的项目的地步,但是我不知道如何在我的 setup.py 文件或我的 CMakeLists.txt 中指定我的二进制文件应该安装在哪里。
详情
当我使用 CMake(例如 mkdir /path/to/my_project/build && cd /path/to/my_project/build && cmake .. && make
)构建我的项目时,一切都正确构建,我最终得到一个 libmy_project.so
和一个 pymy_project.so
文件,我可以成功导入和使用在 Python.
当我在 virtualenv 中使用 pip 构建项目时(例如 pip install /path/to/my_project -v
),我可以看到 CMake 和我的编译器正在按预期进行编译。然而,它们建立在一个临时目录中并立即被丢弃。 如何告诉我的 setup.py 要保留哪些文件,以及将它们放在哪里?
相关文件
我的 CMakeLists.txt 如下所示:
cmake_minimum_required (VERSION 3.0.0)
project (my_project)
# Find SomePackage
find_package(SomePackage REQUIRED COMPONENTS cool_unit great_unit)
include_directories(${SomePackage_INCLUDE_DIR})
# Find Python
find_package(PythonLibs 2.7 REQUIRED)
include_directories(${PYTHON_INCLUDE_DIR})
# Build libmy_project
add_library(my_project SHARED src/MyProject.cpp)
target_link_libraries(my_project ${SomePackage_LIBRARIES} ${PYTHON_LIBRARIES})
# Build py_my_project
add_library(py_my_project SHARED src/python/pyMyProject.cpp)
set_target_properties(py_my_project PROPERTIES PREFIX "")
target_link_libraries(py_my_project ${SomePackage_LIBRARIES}
${PYTHON_LIBRARIES} my_project)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/__init__.py "__all__ = ['py_my_project']")
我的 setup.py 文件如下所示:
import os
import subprocess
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
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):
for ext in self.extensions:
self.build_extension(ext)
def build_extension(self, ext):
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
subprocess.check_call(['cmake', ext.sourcedir], cwd=self.build_temp)
subprocess.check_call(['cmake', '--build', '.'], cwd=self.build_temp)
setup(
name='my_project',
version='0.0.1',
author='Me',
author_email='rcv@whosebug.com',
description='A really cool and easy to install library',
long_description='',
ext_modules=[CMakeExtension('.')],
cmdclass=dict(build_ext=CMakeBuild),
zip_safe=False,
)
“我如何告诉我的 setup.py 哪些文件要保留,以及将它们放在哪里?”的答案似乎是 self.get_ext_fullpath(ext.name)
如 this solution as pointed out by abarnert and hoefling 中所见。此方法 returns 一个临时根目录,您可以将构建输出放入其中。然后 Pip 似乎会扫描该目录,并在构建完成后将所有内容复制到您的真实环境中。
不幸的是,Linux 动态链接器无法为我的扩展找到我的依赖项(libmy_project.so
),因为 virtualenv 没有设置 LD_LIBRARY_PATH
或任何东西,所以我结束了只是将所有编译单元滚动到我的扩展中。完整的解决方案如下:
setup.py
import os
import subprocess
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
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):
for ext in self.extensions:
self.build_extension(ext)
def build_extension(self, ext):
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
extdir = self.get_ext_fullpath(ext.name)
if not os.path.exists(extdir):
os.makedirs(extdir)
# This is the temp directory where your build output should go
install_prefix = os.path.abspath(os.path.dirname(extdir))
cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={}'.format(install_prefix)]
subprocess.check_call(['cmake', ext.sourcedir, cmake_args], cwd=self.build_temp)
subprocess.check_call(['cmake', '--build', '.'], cwd=self.build_temp)
setup(
name='my_project',
version='0.0.1',
author='Me',
author_email='rcv@whosebug.com',
description='A really cool and easy to install library',
long_description='',
ext_modules=[CMakeExtension('.')],
cmdclass=dict(build_ext=CMakeBuild),
zip_safe=False,
install_requires=[
'cmake',
]
)
CMakeLists.txt
cmake_minimum_required (VERSION 3.0.0)
project (my_project)
# Find SomePackage
find_package(SomePackage REQUIRED COMPONENTS cool_unit great_unit)
include_directories(${SomePackage_INCLUDE_DIR})
# Find Python
find_package(PythonLibs 2.7 REQUIRED)
include_directories(${PYTHON_INCLUDE_DIR})
# Build py_my_project
add_library(py_my_project SHARED src/python/pyMyProject.cpp src/MyProject.cpp)
set_target_properties(py_my_project PROPERTIES PREFIX "")
target_link_libraries(py_my_project ${SomePackage_LIBRARIES}
${PYTHON_LIBRARIES})
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/__init__.py "__all__ = ['py_my_project']")