与 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 告诉编译器通过使用Extensionlibraries 参数来链接库,这应该是一个库名称列表(没有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.confldconfig -m 命令等。)

以上是关于与 distutils 共享库依赖项的主要内容,如果未能解决你的问题,请参考以下文章

如何在 petalinux 中检查共享库依赖项

将具有外部依赖项的共享库集成到 MATLAB |即犰狳、LAPACK、BLAS

如何在没有Android Studio的情况下编译JNI共享库,并在[关闭]中编译依赖项

通过 Grab 添加的外部库的可选依赖项不可用

Laravel - 在多个独立的代码库之间共享功能

在 linux 中使用另一个共享库构建共享库