在工作空间中编译时,dylib无法加载libstd

Mae*_*eln 5 python macos dylib rust rust-cargo

我有一个具有以下结构的项目:

Cargo.toml
my_script.py
my_lib:
    - Cargo.toml
    - src
my_bin:
    - Cargo.toml
    - src
Run Code Online (Sandbox Code Playgroud)

哪里:

  • my_lib 是一个Rust库 crate-type = ["dylib"]
  • my_bin 是一个Rust二进制应用程序,使用 my_lib
  • my_script.py 是Python 3脚本,也使用 my_lib

根目录Cargo.toml包含一个基本的工作区声明:

[workspace]
members = [
    "my_lib",
    "my_bin"
]
Run Code Online (Sandbox Code Playgroud)

如果我执行cargo build和,一切都会正常进行cargo run -p my_bin。问题来自Python脚本。

在此脚本中,我my_lib使用以下代码加载lib文件:

[workspace]
members = [
    "my_lib",
    "my_bin"
]
Run Code Online (Sandbox Code Playgroud)

如果我使用库目录./my_lib/target/...)中的库文件,则脚本在加载库和执行其功能时没有问题。

但是,如果我使用工作空间目录./target/...)中的库文件,则在尝试加载库时出现以下错误:

from ctypes import cdll
from sys import platform

if platform == 'darwin':
    prefix = 'lib'
    ext = 'dylib'
elif platform == 'win32':
    prefix = ''
    ext = 'dll'
else:
    prefix = 'lib'
    ext = 'so'

# Working path:
# lib_path = './my_lib/target/debug/{}my_lib.{}'.format(prefix, ext)

# Buggy "Library not loaded: @rpath/libstd-d00eaa6834e55536.dylib" path:
lib_path = './target/debug/{}my_lib.{}'.format(prefix, ext)

lib = cdll.LoadLibrary(lib_path)
my_func = lib.my_func
my_func()
Run Code Online (Sandbox Code Playgroud)

以相同的方式,尝试my_bin直接从工作空间目标目录执行会产生相同的错误(即使cargo run -p my_bin工作完美)。

使用软件“ Dependency Walker”,我发现该my_lib库找不到Rust libstd库(具有前面的错误消息说明)。

手动将包含Rust工具链库的路径导出到环境中PATH可解决此问题。然而,这远非理想且不便携。我也不明白为什么仅在使用工作区目标时才出现此问题。

那么,为什么libstd每个项目目标都可以时工作区目标找不到锈?有没有一种方法可以解决此问题,而无需找到工具链路径并修改环境变量?

zrz*_*zka 6

动态链接有时并不容易。错误信息Library not loaded: @rpath/libstd-d00eaa6834e55536.dylib非常清楚。您在使用DYLD_LIBRARY_PATH(macOS) 时遇到问题。

TL; 博士

DYLD_LIBRARY_PATH不包含 Rust 库路径。将以下内容放入您的~/.bash_profile

source "$HOME/.cargo/env"
export RUST_SRC_PATH="$(rustc --print sysroot)/lib/rustlib/src/rust/src"
export DYLD_LIBRARY_PATH="$(rustc --print sysroot)/lib:$DYLD_LIBRARY_PATH"
Run Code Online (Sandbox Code Playgroud)

解释

除了一件事,我遵循了您的项目结构 - 我删除了_( my_bin-> mybin, ...)。

cargo run --bin mybin 对比 target/debug/mybin

首先,检查什么otool -L target/debug/mybin说:

target/debug/mybin:
    /Users/robertvojta/Work/bar/target/debug/deps/libmylib.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libstd-d4fbe66ddea5f3ce.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)
    /usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)
Run Code Online (Sandbox Code Playgroud)

请注意@rpath. 如果您不知道它是什么,我建议您阅读 Mike Ash 的帖子:

同时运行man dlopen并阅读该SEARCHING部分。在这里复制粘贴很长,所以,就第一句话:

dlopen() 在由一组环境变量和进程的当前工作目录指定的目录中搜索兼容的 Mach-O 文件。

您将了解DYLD_LIBRARY_PATH和其他环境变量。

在你的 shell 中,echo $DYLD_LIBRARY_PATH命令的输出是什么?我假设它是空的/不包含 Rust 库路径。

将以下行添加到您的mybin:main.rs...

println!(
    "DYLD_LIBRARY_PATH={}",
    std::env::var("DYLD_LIBRARY_PATH").unwrap_or("N/A".to_string())
);
Run Code Online (Sandbox Code Playgroud)

……然后跑cargo run --bin mybin。您应该会看到如下内容:

DYLD_LIBRARY_PATH=~/.rustup/toolchains/stable-x86_64-apple-darwin/lib
Run Code Online (Sandbox Code Playgroud)

cargo run 为您注入此环境变量。

从哪里可以获得正确的价值?运行rustc --print sysroot并附/lib加到输出。

如果你想mybin直接运行(没有cargo),你可以这样做:

source "$HOME/.cargo/env"
export RUST_SRC_PATH="$(rustc --print sysroot)/lib/rustlib/src/rust/src"
export DYLD_LIBRARY_PATH="$(rustc --print sysroot)/lib:$DYLD_LIBRARY_PATH"
Run Code Online (Sandbox Code Playgroud)

Python脚本

将类似的行添加到您的run.py脚本中:

target/debug/mybin:
    /Users/robertvojta/Work/bar/target/debug/deps/libmylib.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libstd-d4fbe66ddea5f3ce.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)
    /usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)
Run Code Online (Sandbox Code Playgroud)

如果打印N/ADYLD_LIBRARY_PATH则未设置。你可以用类似的方式解决这个问题:

println!(
    "DYLD_LIBRARY_PATH={}",
    std::env::var("DYLD_LIBRARY_PATH").unwrap_or("N/A".to_string())
);
Run Code Online (Sandbox Code Playgroud)

macOS 和系统完整性保护

请注意,您不能为此使用系统 Python ...

DYLD_LIBRARY_PATH=~/.rustup/toolchains/stable-x86_64-apple-darwin/lib
Run Code Online (Sandbox Code Playgroud)

...但您可以使用一个安装通过brew例如...

DYLD_LIBRARY_PATH="$(rustc --print sysroot)/lib:$DYLD_LIBRARY_PATH" target/debug/mybin
Run Code Online (Sandbox Code Playgroud)

原因是SIP。SIP 是在 El Capitan 中引入的,它可能会妨碍您。您可以体验以下内容:

import os
print('DYLD_LIBRARY_PATH: {}'.format(os.environ.get('DYLD_LIBRARY_PATH', 'N/A')))
Run Code Online (Sandbox Code Playgroud)

这是SIP 描述页面。SIP 保护文件夹,例如/usr, /bin, /sbin,但它不保护/usr/local例如。

这是什么意思?SIP 做了很多事情,但其中之一就是破坏DYLD_LIBRARY_PATH价值。Shebang 线像...

  • #!/usr/bin/env python
  • #!/usr/bin/python

......不会为你工作。您必须使用 Python 解释器,该解释器未安装在系统(和受保护)文件夹中。通过 安装一个brew,安装 Anaconda,...

SIP 可以被禁用,但要这样做。

另一种方式如何解决这个问题是要取代@rpathmylib一个完整的路径通过install_name_toolman install_name_tool)。为什么 Mac Os X 中的 Mach-O 库需要 install_name_tool 和 otool 中的更多信息.

例子:

DYLD_LIBRARY_PATH="$(rustc --print sysroot)/lib:$DYLD_LIBRARY_PATH" python run.py
Run Code Online (Sandbox Code Playgroud)

如您所见,现在没有@rpathDYLD_LIBRARY_PATH未设置,但它可以与系统 Python 解释器一起工作(Hallo通过hallo函数 from打印libmylib.dylib)。

请注意一件事 - 例如,与 Linux 相比,macOS 动态库的行为有所不同。

如果您不想弄乱它,您可以更改mylib crate-type["rlib", "cdylib"],但这可能不是您想要的。