从 cmake 创建一个 python 包 (deb/rpm)
Creating a python package (deb/rpm) from cmake
我正在尝试从 cmake
创建一个 python 包(deb 和 rpm),最好使用 cpack
。我读过
- https://cmake.org/cmake/help/latest/cpack_gen/rpm.html 并且,
- https://cmake.org/cmake/help/latest/cpack_gen/deb.html
我的共享库的安装工作正常(使用组件安装)。但是我无法理解安装 python 绑定(胶水)代码的文档。使用标准的 cmake 安装机制,我尝试了:
install(
FILES __init__.py library.py
DESTINATION ${ACME_PYTHON_PACKAGE_DIR}/project_name
COMPONENT python)
然后使用蛮力方法结束:
# debian based package (relative path)
set(ACME_PYTHON_PACKAGE_DIR lib/python3/dist-packages)
和
# rpm based package (full path required)
set(ACME_PYTHON_PACKAGE_DIR /var/lang/lib/python3.8/site-packages)
以上来源于:
debian % python -c 'import site; print(site.getsitepackages())'
['/usr/local/lib/python3.9/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.9/dist-packages']
同时:
rpm % python -c 'import site; print(site.getsitepackages())'
['/var/lang/lib/python3.8/site-packages']
很明显,蛮力方法不可移植,并且注定会在 python 的下一个版本中失败。我能想到的唯一可能的解决方案是生成一个临时 setup.py
python 脚本(使用 setuptools
),它将执行安装。通常 cmake
会调用 following process:
% python setup.py install --root ${ACME_PYTHON_INSTALL_ROOT}
我的问题是:
- 我是否正确理解了 python 包的 cmake/cpack 文档?如果是这样,这意味着我需要生成一个中间
setup.py
脚本。
- 我一直在搜索 cmake/cpack 代码库 (
git grep setuptools
),但没有找到处理 setup.py
生成并将结果文件传回 [=19= 的辅助函数].是否有我可以重复使用的现有 cmake
模块?
我确实阅读了一些替代解决方案,例如:
- How to build debian package with CPack to execute setup.py?
这看起来过于复杂,并且面向仅基于 Debian 的系统。在我的情况下,我需要处理 RPM。
我将post我目前使用的临时解决方案,直到有人提供更强大的东西。
所以我最终偶然发现:
- https://alioth-lists.debian.net/pipermail/libkdtree-devel/2012-October/000366.html 并且,
- Using CMake with setup.py
重新使用上面的方法来执行 install
步骤而不是 build
步骤可以按如下方式完成:
find_package(Python COMPONENTS Interpreter)
set(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in")
set(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py")
set(SETUP_DEPS "${CMAKE_CURRENT_SOURCE_DIR}/project_name/__init__.py")
set(SETUP_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build-python")
configure_file(${SETUP_PY_IN} ${SETUP_PY})
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/setup_timestamp
COMMAND ${Python_EXECUTABLE} ARGS ${SETUP_PY} install --root ${SETUP_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/setup_timestamp
DEPENDS ${SETUP_DEPS})
add_custom_target(target ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup_timestamp)
然后丑陋的部分是:
install(
# trailing slash is important:
DIRECTORY ${SETUP_OUTPUT}/
DESTINATION "/" # FIXME may cause issues with other cpack generators
COMPONENT python)
事实证明 install()
的文档对 absolute paths:
非常清楚
DESTINATION
[...]
As absolute paths are not supported by cpack installer generators,
it is preferable to use relative paths throughout.
供参考,这是我的 setup.py.in
:
from setuptools import setup
if __name__ == '__main__':
setup(name='project_name_python',
version='${PROJECT_VERSION}',
package_dir={'': '${CMAKE_CURRENT_SOURCE_DIR}'},
packages=['project_name'])
您可以使用 -B
标志删除 __pycache__
文件夹:
COMMAND ${Python_EXECUTABLE} ARGS -B ${SETUP_PY} install --root ${SETUP_OUTPUT}
您可以更喜欢并添加 debian 选项,例如:
if(CPACK_BINARY_DEB)
set(EXTRA_ARG "--install-layout" "deb")
endif()
用作:
COMMAND ${Python_EXECUTABLE} ARGS -B ${SETUP_PY} install --root ${SETUP_OUTPUT} ${EXTRA_ARG}
如 中所述,丑陋的部分是在 cmake install()
命令中处理绝对路径。我能够重构代码以避免在 install()
中使用绝对路径。我干脆把安装改成了:
install(
# trailing slash is important:
DIRECTORY ${SETUP_OUTPUT}/
# "." syntax is a reliable mechanism, see:
# https://gitlab.kitware.com/cmake/cmake/-/issues/22616
DESTINATION "."
COMPONENT python)
然后只需要:
set(CMAKE_INSTALL_PREFIX "/")
set(CPACK_PACKAGING_INSTALL_PREFIX "/")
include(CPack)
此时所有安装路径现在都需要明确包含 /usr
,因为我们已经清除了 CMAKE_INSTALL_PREFIX
的值。
上面已经针对 deb 和 rpm 包进行了测试。 CPACK_BINARY_TGZ
使用上述解决方案 运行 正确:
我正在尝试从 cmake
创建一个 python 包(deb 和 rpm),最好使用 cpack
。我读过
- https://cmake.org/cmake/help/latest/cpack_gen/rpm.html 并且,
- https://cmake.org/cmake/help/latest/cpack_gen/deb.html
我的共享库的安装工作正常(使用组件安装)。但是我无法理解安装 python 绑定(胶水)代码的文档。使用标准的 cmake 安装机制,我尝试了:
install(
FILES __init__.py library.py
DESTINATION ${ACME_PYTHON_PACKAGE_DIR}/project_name
COMPONENT python)
然后使用蛮力方法结束:
# debian based package (relative path)
set(ACME_PYTHON_PACKAGE_DIR lib/python3/dist-packages)
和
# rpm based package (full path required)
set(ACME_PYTHON_PACKAGE_DIR /var/lang/lib/python3.8/site-packages)
以上来源于:
debian % python -c 'import site; print(site.getsitepackages())'
['/usr/local/lib/python3.9/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.9/dist-packages']
同时:
rpm % python -c 'import site; print(site.getsitepackages())'
['/var/lang/lib/python3.8/site-packages']
很明显,蛮力方法不可移植,并且注定会在 python 的下一个版本中失败。我能想到的唯一可能的解决方案是生成一个临时 setup.py
python 脚本(使用 setuptools
),它将执行安装。通常 cmake
会调用 following process:
% python setup.py install --root ${ACME_PYTHON_INSTALL_ROOT}
我的问题是:
- 我是否正确理解了 python 包的 cmake/cpack 文档?如果是这样,这意味着我需要生成一个中间
setup.py
脚本。 - 我一直在搜索 cmake/cpack 代码库 (
git grep setuptools
),但没有找到处理setup.py
生成并将结果文件传回 [=19= 的辅助函数].是否有我可以重复使用的现有cmake
模块?
我确实阅读了一些替代解决方案,例如:
- How to build debian package with CPack to execute setup.py?
这看起来过于复杂,并且面向仅基于 Debian 的系统。在我的情况下,我需要处理 RPM。
我将post我目前使用的临时解决方案,直到有人提供更强大的东西。
所以我最终偶然发现:
- https://alioth-lists.debian.net/pipermail/libkdtree-devel/2012-October/000366.html 并且,
- Using CMake with setup.py
重新使用上面的方法来执行 install
步骤而不是 build
步骤可以按如下方式完成:
find_package(Python COMPONENTS Interpreter)
set(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in")
set(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py")
set(SETUP_DEPS "${CMAKE_CURRENT_SOURCE_DIR}/project_name/__init__.py")
set(SETUP_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build-python")
configure_file(${SETUP_PY_IN} ${SETUP_PY})
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/setup_timestamp
COMMAND ${Python_EXECUTABLE} ARGS ${SETUP_PY} install --root ${SETUP_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/setup_timestamp
DEPENDS ${SETUP_DEPS})
add_custom_target(target ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup_timestamp)
然后丑陋的部分是:
install(
# trailing slash is important:
DIRECTORY ${SETUP_OUTPUT}/
DESTINATION "/" # FIXME may cause issues with other cpack generators
COMPONENT python)
事实证明 install()
的文档对 absolute paths:
DESTINATION [...] As absolute paths are not supported by cpack installer generators, it is preferable to use relative paths throughout.
供参考,这是我的 setup.py.in
:
from setuptools import setup
if __name__ == '__main__':
setup(name='project_name_python',
version='${PROJECT_VERSION}',
package_dir={'': '${CMAKE_CURRENT_SOURCE_DIR}'},
packages=['project_name'])
您可以使用 -B
标志删除 __pycache__
文件夹:
COMMAND ${Python_EXECUTABLE} ARGS -B ${SETUP_PY} install --root ${SETUP_OUTPUT}
您可以更喜欢并添加 debian 选项,例如:
if(CPACK_BINARY_DEB)
set(EXTRA_ARG "--install-layout" "deb")
endif()
用作:
COMMAND ${Python_EXECUTABLE} ARGS -B ${SETUP_PY} install --root ${SETUP_OUTPUT} ${EXTRA_ARG}
如 install()
命令中处理绝对路径。我能够重构代码以避免在 install()
中使用绝对路径。我干脆把安装改成了:
install(
# trailing slash is important:
DIRECTORY ${SETUP_OUTPUT}/
# "." syntax is a reliable mechanism, see:
# https://gitlab.kitware.com/cmake/cmake/-/issues/22616
DESTINATION "."
COMPONENT python)
然后只需要:
set(CMAKE_INSTALL_PREFIX "/")
set(CPACK_PACKAGING_INSTALL_PREFIX "/")
include(CPack)
此时所有安装路径现在都需要明确包含 /usr
,因为我们已经清除了 CMAKE_INSTALL_PREFIX
的值。
上面已经针对 deb 和 rpm 包进行了测试。 CPACK_BINARY_TGZ
使用上述解决方案 运行 正确: