将macOS动态库移动到包中
项目描述
Delocate
macOS实用工具,用于
查找从Python扩展导入的动态库
将所需动态库复制到包内的目录中
更新macOS install_names 和 rpath,以使代码从库的副本中加载
提供脚本
delocate-listdeps – 显示树依赖的库
delocate-path – 将树依赖的库复制到树中并重新链接
delocate-wheel – 重写具有复制和重新链接库依赖项的wheel到wheel树。
delocate-merge – 将具有不同架构的两个wheel合并为一个具有双架构二进制的wheel。
Auditwheel 是一个类似的Linux工具。Auditwheel最初是Delocate的部分分支。
问题
假设你在某个地方构建了一个轮子,但是它链接到机器上的其他动态库,因此你无法分发它,因为其他人可能没有这些相同的库。在这里,我们分析Scipy轮子的依赖关系。
$ delocate-listdeps scipy-0.14.0b1-cp34-cp34m-macosx_10_6_intel.whl /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgcc_s.1.dylib /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgfortran.3.dylib /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libquadmath.0.dylib
默认情况下,这不包括/usr/lib和/System中的库。也可以用以下方式查看这些库:
$ delocate-listdeps --all scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate /usr/lib/libSystem.B.dylib /usr/lib/libstdc++.6.dylib /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgcc_s.1.dylib /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgfortran.3.dylib /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libquadmath.0.dylib
输出告诉我,Scipy已经从我的Homebrew安装的gfortran(以及系统库)中获取了动态库。
您可以使用--depending标志列出每个库所依赖的文件。
$ delocate-listdeps --depending scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgcc_s.1.dylib: scipy/interpolate/dfitpack.so scipy/special/specfun.so scipy/interpolate/_fitpack.so ...
解决方案
我们可以这样修复
$ delocate-wheel -w fixed_wheels -v scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl Fixing: scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl Copied to package .dylibs directory: /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgcc_s.1.dylib /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgfortran.3.dylib /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libquadmath.0.dylib
-w标志告诉delocate-wheel将输出到新的轮子目录而不是覆盖旧的轮子。标志-v(详细)会告诉你delocate-wheel正在做什么。在这种情况下,它已经在轮子zip文件中创建了一个新目录,命名为scipy/.dylibs。它已经将所有位于macOS系统树之外的库依赖项复制到这个目录中,并且修复了轮子中的python .so 扩展以使用这些副本而不是在/usr/local/Cellar/gfortran/4.8.2/gfortran/lib中查找。
再次检查链接以确认
$ delocate-listdeps --all fixed_wheels/scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate /usr/lib/libSystem.B.dylib /usr/lib/libstdc++.6.0.9.dylib @loader_path/../../../../.dylibs/libgcc_s.1.dylib @loader_path/../../../../.dylibs/libgfortran.3.dylib @loader_path/../../../../.dylibs/libquadmath.0.dylib @loader_path/../../../.dylibs/libgcc_s.1.dylib @loader_path/../../../.dylibs/libgfortran.3.dylib @loader_path/../../../.dylibs/libquadmath.0.dylib @loader_path/../../.dylibs/libgcc_s.1.dylib @loader_path/../../.dylibs/libgfortran.3.dylib @loader_path/../../.dylibs/libquadmath.0.dylib @loader_path/../.dylibs/libgcc_s.1.dylib @loader_path/../.dylibs/libgfortran.3.dylib @loader_path/../.dylibs/libquadmath.0.dylib @loader_path/libgcc_s.1.dylib @loader_path/libquadmath.0.dylib
因此,系统dylibs相同,但其他库已移动到轮子树中。
这使得轮子在另一台机器上更有可能工作,该机器没有安装与Gfortran相同的版本 - 在这个例子中。
检查所需的架构
当前的Python.org Python和macOS系统Python(/usr/bin/python)都是双架构的二进制文件。例如
$ lipo -info /usr/bin/python Architectures in the fat file: /usr/bin/python are: x86_64 arm64e
注意:您可以编译用于基本ARM(arm64)的二进制文件,或者用于一些扩展的ARM功能(arm64e) - 请参阅这篇SO帖子。这两种类型的二进制文件都适用于Mac M1和M2机器,因此我们将使用arm64来指代arm64或arm64e。
上述Big Sur macOS Python具有融合到同一个文件中的x86_64和arm64(M1)版本。早期的macOS版本具有双架构,分别是32位(i386)和64位(x86_64)。
为了与系统和Python.org Python完全兼容,为Python.org Python或系统Python构建的轮子应该具有相应的架构 - 例如Python扩展和它们的库的x86_64和arm64版本。很容易错误地将Python扩展链接到单一架构库,并因此获得单一架构扩展和/或库。事实上,我的Scipy轮子就是这样一个例子,因为我无意中链接到了仅限于x86_64的Homebrew库。要检查此,您可以使用--require-archs标志
$ delocate-wheel --require-archs=intel scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl Traceback (most recent call last): File "/Users/mb312/.virtualenvs/delocate/bin/delocate-wheel", line 77, in <module> main() File "/Users/mb312/.virtualenvs/delocate/bin/delocate-wheel", line 69, in main check_verbose=opts.verbose) File "/Users/mb312/.virtualenvs/delocate/lib/python2.7/site-packages/delocate/delocating.py", line 477, in delocate_wheel "Some missing architectures in wheel") delocate.delocating.DelocationError: Some missing architectures in wheel
请注意,此命令使用的是支持Python 2的早期版本的Delocate;我们现在仅支持Python 3。
上面--require-archs的intel参数需要32位和64位架构的扩展和库。您可以通过添加-v(详细)标志来查看哪些扩展有错误
$ delocate-wheel -w fixed_wheels --require-archs=intel -v scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl Fixing: scipy-0.14.0-cp34-cp34m-macosx_10_6_intel.whl Required arch i386 missing from /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libgfortran.3.dylib Required arch i386 missing from /usr/local/Cellar/gfortran/4.8.2/gfortran/lib/libquadmath.0.dylib Required arch i386 missing from scipy/fftpack/_fftpack.so Required arch i386 missing from scipy/fftpack/convolve.so Required arch i386 missing from scipy/integrate/_dop.so ...
我需要重新构建这个轮子以链接到双架构库。
制作双架构二进制文件
现代Mac轮子可以是arm64(M1/M2 ARM)、x86_64(64位Intel)或两者都(universal2)。
构建一个双架构的整个 Python 轮子可能会很困难,可能是因为你需要在两种情况下链接不同的库,或者你需要不同的编译器标志,或者因为你在某个持续集成平台(如 - 在撰写本文时 - Cirrus CI)上为 arm64 构建,而在另一个平台上为 x86_64 构建。
解决这个问题的一个方案是先进行整个 arm64 轮子构建,然后进行整个 x86_64 轮子构建,并将这两个轮子 融合 成一个通用轮子。
这就是 delocate-merge 命令所做的事情。
假设你已经构建了一个 ARM 和 Intel 轮子,分别命名为
scipy-1.9.3-cp311-cp311-macosx_12_0_arm64.whl
scipy-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl
然后你可以使用以下命令在 tmp 子目录中创建一个新的融合(universal2)轮子
delocate-merge scipy-1.9.3-cp311-cp311-macosx_12_0_arm64.whl scipy-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl -w tmp
在这种情况下,输出轮子将是
tmp/scipy-1.9.3-cp311-cp311-macosx_12_0_universal2.whl
在新轮子中,你会找到,使用 lipo -archs - 所有轮子中同名的二进制文件现在都是通用的(x86_64 和 arm64)。
:warning: 注意: 在之前的版本(<0.12.0)中,使用 delocate-fuse 命令来创建双架构的二进制文件。默认情况下,该命令会覆盖传入的第一个轮子。这导致用户需要重命名轮子以正确描述其支持的平台。由于这个和其他原因,使用这种方式创建的轮子通常是不正确的。从版本 0.12.0 开始,已删除 delocate-fuse 命令,并替换为 delocate-merge。delocate-merge 命令将创建一个新轮子,其名称基于合并的轮子自动生成。不需要对合并轮子的名称进行任何其他更改。如果需要旧行为(不推荐),则将版本固定为 delocate==0.11.0。
故障排除
DelocationError: “库不存在”
在运行 delocate-wheel 或其姐妹命令 delocate-path 时,你可能会遇到这样的错误
delocate.delocating.DelocationError: library "<long temporary path>/wheel/libme.dylib" does not exist
当你的某个库提供了一个具有相对路径的库依赖时,会发生这种情况
myext.so: libme.dylib (compatibility version 10.0.0, current version 10.0.0) /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 60.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
例如,假设我轮子中的某个文件对于 otool -L myext.so 的输出是这样的
myext.so: /path/to/libme.dylib (compatibility version 10.0.0, current version 10.0.0) /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 60.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
第一行表示 myext.so 预期在路径 ./libme.dylib 中找到 libme.dylib - 这是运行可执行文件的当前工作目录。输出应该是类似的东西
libme.dylib (compatibility version 10.0.0, current version 10.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
要设置库的路径,链接器正在使用链接库时选择的 install name id。在这种情况下,otool -L libme.dylib 将给出类似的东西
代码
参见 https://github.com/matthew-brett/delocate
基于BSD双条款许可协议发布 - 请参阅源代码分发中的文件 LICENSE。
GitHub Actions 自动测试代码。
最新发布的版本在 https://pypi.python.org/pypi/delocate。
支持
请在 Delocate问题跟踪器 上提出问题。
项目详情
下载文件
下载适用于您的平台的文件。如果您不确定选择哪个,请了解更多关于 安装软件包 的信息。
源代码分发
构建分发
delocate-0.12.0.tar.gz的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | e051660836a87b61c99b9b180344be363c31e1f2d9fdc94caebc854b6611335c |
|
MD5 | d035fbbbfe47c8f192e773ee686da8fe |
|
BLAKE2b-256 | d3c19ce172375070c59f3a40973fa4daf98d80a874cddc9ddaed494656dd9259 |
delocate-0.12.0-py3-none-any.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 0da599c7561dcca2835149eaa4733e85408d71f249c6c4c43c3cb16cedb43a09 |
|
MD5 | 6a220a2ae85dd7c205b7aec7c5b84169 |
|
BLAKE2b-256 | bf4aaa78a0333ce108aa722a6b8f449dfae55b12ed180e98721a90e571896dbd |