在分发python包时处理dylib

时间:2017-11-19 18:00:24

标签: cython portability distutils dylib

在使用python setup.py bdist_wheel或等效命令构建Cython模块后,我需要运行某些命令。这可能吗?

具体情况如下: 我正在为osx开发一个Cython程序。它链接到我的主目录中的某些.dylib文件,要获得要构建的程序,您必须运行

export DYLD_LIBRARY_PATH=/path/to/parent/dir

(这很麻烦)或在setup.py中包含一条指令来运行

install_name_tool -change @executable_path/my.dylib /path/to/my.dylib

这基本上是它告诉二进制文件在哪里寻找dylib。这两种选择都不是理想选择,但后者似乎更具可移植性。现在我只是将这些说明添加到setup.py的底部,但这仍然不可移植:假设我按照以下方式打包并上传我的包:

python setup.py sdist  # I think this collects source files into a .tar
python setup.py bdist_wheel  # I think this builds a binary. This is where I assume the post-compilation stuff should happen.
twine upload dist/*   # This puts stuff up on PyPi.

现在,我已经阅读了文档和一些教程,我承认仍然不能100%确定这些命令的作用。但我知道,如果随后在另一个项目中,我运行

pip install mypackage

dylib问题让人头疼:

ImportError: dlopen(/path/to/my/module.cpython-36m-darwin.so, 2): 
Library not loaded: @executable_path/my.dylib
  Referenced from: /path/to/my/module.cpython-36m-darwin.so
  Reason: image not found

似乎解决方案是向setup.py添加某种自动后编译方向,以指示需要执行install_name_tool操作。假设这是可能的,那怎么做呢?理想情况下,我想使用现成的Cython.Build.cythonizesetuptools.Extension,但也许我需要做一些更加个性化的事情。谢谢你的建议!

解决方案

Danny正确地指出,运行install_name_tool不是一个可移植的解决方案,而是应该以某种方式将dylib包含在包中。 delocate是工作的工具,但我遇到的第一个挑战是delocate不适用于@executable_path

❯ delocate-wheel -w fixed_wheels -v dist/*.whl
Fixing: dist/my-package-1.0.9-cp36-cp36m-macosx_10_12_x86_64.whl
/Users/ethan/virtualenvs/my-package/lib/python3.6/site-packages/delocate/delocating.py:71: UserWarning: Not processing required path @executable_path/my.dylib because it begins with @
  'begins with @'.format(required))

使用otool,我能够验证id my.dylib使用@executable_path

❯ otool -L my.dylib
my.dylib:
    @executable_path/my.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
    /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
    /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1258.1.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)

为了摆脱@executable_path,我运行了以下内容:

❯ install_name_tool -id my.dylib my.dylib

现在查看otool -L的输出:

❯ otool -L my.dylib
my.dylib:
    my.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
    /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
    /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1258.1.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)

一般来说,这对于dylib来说不是一个好id,但delocate-wheel就足够了。我重建了我的.whl

python setup.py bdist_wheel

现在当我跑delocate_wheel时:

❯ delocate-wheel -w fixed_wheels -v dist/*.whl
Fixing: dist/my.whl
Traceback (most recent call last):
...    
delocate.delocating.DelocationError: library "/Users/ethan/my-package/my.dylib" does not exist

这清楚地告诉我们delocate期望找到my.dylib的位置。所以我将my.dylib复制到/Users/ethan/my-package/my.dylib并重新命令:

❯ delocate-wheel -w fixed_wheels -v dist/*.whl
Fixing: dist/my.whl
Copied to package .dylibs directory:
  /Users/ethan/my-package/my.dylib

成功!这个.dylibs目录是什么?我跑tar -xvf my.whl打开方向盘并检查其内容:

❯ tree -a fixed_wheels/my-package
fixed_wheels/my-package
├── .dylibs
│   └── my.dylib
├── __init__.py
└── package.cpython-36m-darwin.so

如您所见,my.dylib已被复制到.dylibs/目录中,该目录将打包在my.whl中。将my.whl上传到pypi后,我就可以下载并运行代码了。

1 个答案:

答案 0 :(得分:0)

查看delocate和Linux等效版auditwheel

delocate工具不是必须在运行时更改链接加载器路径,而是将第三方库嵌入到轮中并相应地调整加载时间路径。

结果是一个可分发的二进制轮,正确包含所有依赖库。

设置运行时加载路径不可移植,不能在python版本或机器架构中使用。

例如,对于上述内容,在python setup.py bdist_wheel运行:

创建轮子之后

delocate-wheel -w fixed_wheels <wheel file>

固定的轮盘文件将放在fixed_wheels目录下。使用-v运行以查看它嵌入的库。