如何在构建时强制 python 轮子特定于平台?
How to force a python wheel to be platform specific when building it?
我正在开发一个 python2 包,其中 setup.py
包含一些自定义安装命令。这些命令实际上构建了一些 Rust 代码并输出了一些 .dylib
文件,这些文件被移动到 python 包中。
重要的一点是 Rust 代码在 python 包之外。
setuptools
应该自动检测 python 包是纯粹的 python 还是平台特定的(例如,如果它包含一些 C 扩展)。
在我的例子中,当我 运行 python setup.py bdist_wheel
时,生成的轮子被标记为纯 python 轮子:<package_name>-<version>-py2-none-any.whl
。
这是有问题的,因为我需要在不同的平台上 运行 这段代码,因此我需要为每个平台生成一个轮子。
有没有办法在构建轮子时强制构建特定于平台?
下面是我平时看的代码uwsgi
基本方法是:
setup.py
# ...
try:
from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
class bdist_wheel(_bdist_wheel):
def finalize_options(self):
_bdist_wheel.finalize_options(self)
self.root_is_pure = False
except ImportError:
bdist_wheel = None
setup(
# ...
cmdclass={'bdist_wheel': bdist_wheel},
)
root_is_pure
位告诉轮子机器构建一个非 purelib (pyX-none-any
) 轮子。您还可以通过说有 binary 特定于平台的组件但没有 cpython abi 特定组件来获得 fancier。
模块 setuptools
、distutils
和 wheel
通过检查 ext_modules
.[=27= 来决定 python 分布是否纯]
如果您自己构建外部模块,您仍然可以将其列在 ext_modules
中,以便构建工具知道它存在。诀窍是提供一个空的源列表,这样 setuptools
和 distutils
就不会尝试构建它。例如,
setup(
...,
ext_modules=[
setuptools.Extension(
name='your.external.module',
sources=[]
)
]
)
这个解决方案对我来说比修补 bdist_wheel
命令更有效。原因是 bdist_wheel
在内部调用 install
命令,该命令再次检查 ext_modules
是否存在,以决定是 purelib
还是 platlib
安装。如果您不列出外部模块,您最终会将 lib 安装在 wheel 内的 purelib
子文件夹中。这在使用 auditwheel repair
时会导致问题,它会抱怨扩展安装在 purelib
文件夹中。
您还可以在构建轮子时通过指定 --plat-name
:
specify/spoof 特定平台名称
python setup.py bdist_wheel --plat-name=manylinux1_x86_64
root_is_pure 技巧和空 ext_modules 技巧对我都不起作用,但经过大量搜索后,我终于在 'pip setup.py bdist_wheel' no longer builds forced non-pure wheels
中找到了可行的解决方案
基本上,您覆盖分布 class 中的 'has_ext_modules' 函数,并将 distclass 设置为指向覆盖 class。到那时,setup.py 会认为你有一个二进制发行版,并会用 python 的特定版本、ABI 和当前架构创建一个轮子。正如 https://whosebug.com/users/5316090/py-j 所建议的:
from setuptools import setup
from setuptools.dist import Distribution
DISTNAME = "packagename"
DESCRIPTION = ""
MAINTAINER = ""
MAINTAINER_EMAIL = ""
URL = ""
LICENSE = ""
DOWNLOAD_URL = ""
VERSION = '1.2'
PYTHON_VERSION = (2, 7)
# Tested with wheel v0.29.0
class BinaryDistribution(Distribution):
"""Distribution which always forces a binary package with platform name"""
def has_ext_modules(foo):
return True
setup(name=DISTNAME,
description=DESCRIPTION,
maintainer=MAINTAINER,
maintainer_email=MAINTAINER_EMAIL,
url=URL,
license=LICENSE,
download_url=DOWNLOAD_URL,
version=VERSION,
packages=["packagename"],
# Include pre-compiled extension
package_data={"packagename": ["_precompiled_extension.pyd"]},
distclass=BinaryDistribution)
我觉得 很好,但对我来说不适合。
我的情况是我必须强制为 x86_64 创建轮子,但任何 python3,所以使 root 不纯实际上导致我的轮子是 py36-cp36 :(
更好的方法,IMO,一般来说就是使用 sys.argv
:
from setuptools import setup
import sys
sys.argv.extend(['--plat-name', 'x86_64'])
setup(name='example-wheel')
我正在开发一个 python2 包,其中 setup.py
包含一些自定义安装命令。这些命令实际上构建了一些 Rust 代码并输出了一些 .dylib
文件,这些文件被移动到 python 包中。
重要的一点是 Rust 代码在 python 包之外。
setuptools
应该自动检测 python 包是纯粹的 python 还是平台特定的(例如,如果它包含一些 C 扩展)。
在我的例子中,当我 运行 python setup.py bdist_wheel
时,生成的轮子被标记为纯 python 轮子:<package_name>-<version>-py2-none-any.whl
。
这是有问题的,因为我需要在不同的平台上 运行 这段代码,因此我需要为每个平台生成一个轮子。
有没有办法在构建轮子时强制构建特定于平台?
下面是我平时看的代码uwsgi
基本方法是:
setup.py
# ...
try:
from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
class bdist_wheel(_bdist_wheel):
def finalize_options(self):
_bdist_wheel.finalize_options(self)
self.root_is_pure = False
except ImportError:
bdist_wheel = None
setup(
# ...
cmdclass={'bdist_wheel': bdist_wheel},
)
root_is_pure
位告诉轮子机器构建一个非 purelib (pyX-none-any
) 轮子。您还可以通过说有 binary 特定于平台的组件但没有 cpython abi 特定组件来获得 fancier。
模块 setuptools
、distutils
和 wheel
通过检查 ext_modules
.[=27= 来决定 python 分布是否纯]
如果您自己构建外部模块,您仍然可以将其列在 ext_modules
中,以便构建工具知道它存在。诀窍是提供一个空的源列表,这样 setuptools
和 distutils
就不会尝试构建它。例如,
setup(
...,
ext_modules=[
setuptools.Extension(
name='your.external.module',
sources=[]
)
]
)
这个解决方案对我来说比修补 bdist_wheel
命令更有效。原因是 bdist_wheel
在内部调用 install
命令,该命令再次检查 ext_modules
是否存在,以决定是 purelib
还是 platlib
安装。如果您不列出外部模块,您最终会将 lib 安装在 wheel 内的 purelib
子文件夹中。这在使用 auditwheel repair
时会导致问题,它会抱怨扩展安装在 purelib
文件夹中。
您还可以在构建轮子时通过指定 --plat-name
:
python setup.py bdist_wheel --plat-name=manylinux1_x86_64
root_is_pure 技巧和空 ext_modules 技巧对我都不起作用,但经过大量搜索后,我终于在 'pip setup.py bdist_wheel' no longer builds forced non-pure wheels
中找到了可行的解决方案基本上,您覆盖分布 class 中的 'has_ext_modules' 函数,并将 distclass 设置为指向覆盖 class。到那时,setup.py 会认为你有一个二进制发行版,并会用 python 的特定版本、ABI 和当前架构创建一个轮子。正如 https://whosebug.com/users/5316090/py-j 所建议的:
from setuptools import setup
from setuptools.dist import Distribution
DISTNAME = "packagename"
DESCRIPTION = ""
MAINTAINER = ""
MAINTAINER_EMAIL = ""
URL = ""
LICENSE = ""
DOWNLOAD_URL = ""
VERSION = '1.2'
PYTHON_VERSION = (2, 7)
# Tested with wheel v0.29.0
class BinaryDistribution(Distribution):
"""Distribution which always forces a binary package with platform name"""
def has_ext_modules(foo):
return True
setup(name=DISTNAME,
description=DESCRIPTION,
maintainer=MAINTAINER,
maintainer_email=MAINTAINER_EMAIL,
url=URL,
license=LICENSE,
download_url=DOWNLOAD_URL,
version=VERSION,
packages=["packagename"],
# Include pre-compiled extension
package_data={"packagename": ["_precompiled_extension.pyd"]},
distclass=BinaryDistribution)
我觉得
我的情况是我必须强制为 x86_64 创建轮子,但任何 python3,所以使 root 不纯实际上导致我的轮子是 py36-cp36 :(
更好的方法,IMO,一般来说就是使用 sys.argv
:
from setuptools import setup
import sys
sys.argv.extend(['--plat-name', 'x86_64'])
setup(name='example-wheel')