使用Distutils时的共享库依赖性

19

我是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方法:

    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 returned 1 exit status。我的目标是打包一个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 发行版上可能仍然无法工作。因此,如果有人能提出比这个修复更好的建议,请告诉我。

2个回答

17

你可以让链接器在输出的二进制文件中存储要搜索的路径,这样就不需要 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 的说明:

  • -L 指定的路径会在链接时使用
  • -R 指定的路径会在运行时使用

非常棒的答案,通过结合您的回答和这篇文章http://sebsauvage.net/python/mingw.html,我能够按照所需的方式构建所需的模块。非常感谢。 - Mike Vella
没问题,我很高兴能帮忙。 - Brian Vandenberg
4
FYI:不需要加上“-R'$ORIGIN/../lib/'”选项,您可以在Extension定义中添加runtime_library_dirs="$ORIGIN/../lib/" (实际上是相同的效果)来达到相同的目的。 - David Robinson
请注意,runtime_library_dirs 是特定于Python的。 - Brian Vandenberg
当使用$ORIGIN时,我应该警告一些东西:如果应用程序以suid/setuid方式运行,则ldd仅在运行时使用完全限定的路径。不是每个操作系统都处理它的方式相同;有些可能会完全跳过带有$ORIGIN的路径,而其他一些则只是直接使用字符串(例如,使用truss/strace,您将看到尝试打开$ORIGIN/../lib/libsomelib.so)。 - Brian Vandenberg
显示剩余2条评论

11
Extension类的extra_objects参数不是要将库链接到您的扩展中,而是要将对象文件传递给链接器(文件名不应包括扩展名,因为distutils会添加)。它并没有做你想要的事情。
如果您想要链接特定的共享库,则需要执行两个操作:告诉distutils告诉编译器链接这些共享库,并告诉动态链接器(通常是ld.so)在哪里找到这些共享库。您可以通过使用Extensionlibraries参数告诉distutils告诉编译器链接这些库,该参数应该是一个库名称列表(不包括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 归档文件)传递给 libraries 还是 extra_objects 关键字?不幸的是,文档对这两个问题都没有帮助。 - Mike Vella
1
可以将相对目录作为 runtime_library_dirs 传递,但这并不是一个好主意(因为扩展模块在构建过程中会移动位置,并且同一路径必须适用于所有情况)。至于静态连接,您可以尝试将.a 归档文件作为 extra_objects 参数传递,虽然这并不是 extra_objects 的预期用法,而且我也不确定它是否起作用。也许您应该详细说明您想要做什么。 - Thomas Wouters
谢谢您的回复,我已经在我的原始问题中添加了更多细节。 - Mike Vella
好的 - 我已经想出了解决我的问题的方法,那就是将库放置在 /usr/lib 中。我意识到这不是很理想,但是有没有什么主要的原因我不应该这样做呢? - Mike Vella
1
不,将共享库放置在动态链接器已经考虑的位置是常见做法。唯一不这样做的原因是如果其他东西(如操作系统的软件包管理器)控制/usr/lib,那么当您稍后安装其他操作系统软件包时,您的文件可能会妨碍它们。 /usr/local/lib是更好的选择(并且存在于此目的)。您还可以使用其他位置并告诉动态链接器考虑它(通过/etc/ld.so.confldconfig -m命令等)。 - Thomas Wouters
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接