与 distutils 共享库依赖项
Posted
技术标签:
【中文标题】与 distutils 共享库依赖项【英文标题】:Shared library dependencies with distutils 【发布时间】:2012-04-05 10:49:12 【问题描述】:我是 distutils 的新手,但我遇到了一个真正让我陷入困境的问题。我正在编译一个需要扩展的包,所以我做了扩展:
a_module = Extension(
"amodule",
["initmodule.cpp"],
library_dirs=libdirs,
extra_objects = [
"unix/x86_64/lib/liba.so"
"unix/x86_64/lib/lib.so",
"unix/x86_64/lib/libc.so"],
)
然后我运行设置方法:
setup(name="apackage", version="7.2",
package_dir = '':instdir+'/a/b/python',
packages=['apackage','package.tests'],
ext_modules=[hoc_module]
)
包分发正确,我可以“python setup.py install”很好,但是当我尝试导入我的包时出现错误
ImportError: liba.so.0: cannot open shared object file: No such file or directory
我意识到当我将 liba.so.0 的位置添加到我的 LD_LIBRARY_PATH 时,程序运行良好。不幸的是,我没有编写这些模块,也没有很好地理解编译。这几天我一直在想办法,但无济于事。
更新:我尝试将 liba.a、libb.a 等文件传递给 extra_objects 但这不起作用,产生以下错误:liba.a: could not read symbols: Bad value collect2: ld 返回 1 个退出状态。我要做的是打包一个python模块,该模块需要编译一个库,该库本身依赖于我需要以某种方式包含在包中的其他库。我怀疑我的问题与这个非常相似:http://mail.python.org/pipermail/distutils-sig/2009-February/010960.html但是那个没有解决,我想也许因为它已经有两年了,所以找到了解决方案?
更新 2:现在我已经解决了这个问题:
data_files=[('/usr/local/lib', glob.glob('unix/x86_64/lib/*'))]
也就是说,我正在将我需要的库复制到/usr/local/lib 中。然而,我对这个解决方案并不十分满意,尤其是因为它要求我的用户具有 root 权限,而且这可能仍然无法在 Redhat 发行版中运行。因此,如果有人可以提出比此修复更好的建议,请告诉我。
【问题讨论】:
【参考方案1】:您可以让链接器存储路径在输出二进制文件中搜索,这样就不需要 LD_LIBRARY_PATH。一些例子:
# Will link fine but at run-time LD_LIBRARY_PATH would be required
gcc -o blah blah.o -lpcap -L/opt/csw/lib
# Without LD_LIBRARY_PATH=/opt/csw/lib it will fail to link, but
# it wouldn't be needed at run-time
gcc -o blah blah.o -lpcap -Wl,-R/opt/csw/lib
# LD_LIBRARY_PATH not needed at link or run-time
gcc -o blah blah.o -lpcap -Wl,-L,R/opt/csw/lib
# This makes it possible to use relative paths; run `readelf -d binary_name`
# and you'll see '$ORIGIN/../lib/' in RPATH. This plus `-zorigin` make it look
# relative to the binary for libraries at run-time
gcc -o blah blah.o -lsomelib -L/whatever/path/floats/your/boat -Wl,-R'$ORIGIN/../lib/' -Wl,-zorigin
.. 哪里:
-L
给出的路径在链接时使用
-R
给出的路径在运行时使用
【讨论】:
很棒的答案,通过将您的答案与 sebsauvage.net/python/mingw.html 结合起来,我能够以完全所需的方式构建所需的模块。非常感谢。 没问题,很高兴能帮上忙 仅供参考:您可以将runtime_library_dirs="$ORIGIN/../lib/"
添加到您的Extension
定义中,而不是添加-R'$ORIGIN/../lib/'
选项(在实践中它会做同样的事情)。
但是请记住,runtime_library_dirs
是特定于 python 的。
我应该在使用$ORIGIN
时警告/注意一些事情:ldd
只会在应用程序使用 suid/setuid 运行时在运行时使用完全限定路径。并非每个操作系统都以相同的方式处理它。有些人可能会完全跳过带有$ORIGIN
的路径,而我见过的其他人只会逐字使用字符串(即,使用truss
/strace
你会看到尝试打开$ORIGIN/../lib/libsomelib.so
)。【参考方案2】:
Extension
类的 extra_objects
参数与其说是要链接到您的扩展的库列表,不如说是将传递给链接器的目标文件列表(并且文件名不应包含扩展名,因为 distutils 会添加这些。)它并没有做你想要的。
如果你想链接特定的共享库,正如这些文件的名称所暗示的那样,你必须做两件事:告诉 distutils 告诉编译器链接到那些共享库,并告诉动态链接器(通常ld.so
) 在哪里可以找到这些共享库。您可以告诉 distutils 告诉编译器通过使用Extension
的libraries
参数来链接库,这应该是一个库名称列表(没有lib
前缀和.so
后缀。)在你的例子中这似乎是['a', 'b', 'c']
(虽然看起来'b'
是从'lib.so
' 上掉下来的,而'c'
实际上会与系统libc 发生冲突。)
告诉链接器在哪里可以找到这些共享库可以通过设置LD_LIBRARY_PATH
环境变量来完成,就像您所做的那样,或者通过更改系统范围的配置设置(使用ldconfig
或通过编辑/etc/ld.so.conf
),或者通过硬编码扩展模块中的搜索路径;您可以通过将runtime_library_dirs
参数传递给Extension
来完成后者。不过,硬编码路径确实有其自身的问题——您必须将这些库保存在同一个位置,并且可供扩展模块的所有用户访问。
(或者,您可以使用静态链接而不是动态链接,例如仅以静态形式提供库,liba.a
存档(在这种情况下,distutils 将自动静态链接到它们。)这基本上意味着整个库是包含在扩展模块中,它有各种缺点和优点。)
【讨论】:
非常感谢您详细而出色的回复。我是否正确理解 runtime_library_dirs 应该设置为相对路径,否则我看不到它是如何被硬编码的?另外,我应该将静态链接库(即 liba.a 档案)传递给库或 extra_objects 关键字。不幸的是,文档在这两个问题上对我没有多大帮助。 可以将相对目录作为 runtime_library_dirs 传递,但这不是一个好主意(因为扩展模块在构建过程中会四处移动,并且必须对所有人都使用相同的路径。)至于静态链接,您可以尝试将.a
档案作为extra_objects
参数传递,尽管这不是extra_objects
的用途,我不确定它是否会起作用。也许你应该详细说明你真正想做的事情。
感谢您的回复,我在原来的问题中添加了更多细节。
好的 - 我发现解决我的问题的一种方法是将库放在 /usr/lib 我意识到这不是很理想但是有什么主要原因我不应该这样做?
不,将共享库放置在动态链接器已经考虑的位置是常见的做法。不这样做的唯一原因是其他东西,比如操作系统的包管理器,控制/usr/lib
,在这种情况下,当你以后安装其他一些操作系统包时,你的文件可能会妨碍你。 /usr/local/lib
是一个更好的选择(并且为此目的而存在。)您还可以使用其他位置并告诉动态链接器考虑它(通过/etc/ld.so.conf
或ldconfig -m
命令等。)以上是关于与 distutils 共享库依赖项的主要内容,如果未能解决你的问题,请参考以下文章
将具有外部依赖项的共享库集成到 MATLAB |即犰狳、LAPACK、BLAS