我该如何在Rust中指定链接器路径?

22

我正在尝试将一个Rust程序与libsoundio链接起来。我在使用Windows,并有GCC二进制文件可供下载。如果我将它放在与我的项目相同的文件夹中,我可以像这样进行链接:

#[link(name = ":libsoundio-1.1.0/i686/libsoundio.a")]
#[link(name = "ole32")]
extern {
    fn soundio_version_string() -> *const c_char;
}

但我真的想要指定#[link(name = "libsoundio")]甚至是#[link(name = "soundio")],然后在其他地方提供链接器路径。

我在哪里可以指定该路径?

我尝试了以下rustc-link-search建议:

#[link(name = "libsoundio")]
#[link(name = "ole32")]
extern {
    fn soundio_version_string() -> *const c_char;
}

并在.cargo/config文件中:

[target.i686-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/i686"]
rustc-link-lib = ["libsoundio.a"]

[target.x86_64-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/x86_64"]
rustc-link-lib = ["libsoundio.a"]

但是它仍然只向gcc传递"-l" "libsoundio",并且出现相同的ld:cannot find -llibsoundio错误。我是否错过了一些非常明显的东西?文档似乎表明这应该可以工作。

3个回答

21

构建脚本文档中所述:

构建脚本打印到标准输出的所有行[...以]cargo:开头,将直接由Cargo解释[...]rustc-link-search表示指定值应作为-L标志传递给编译器。

在您的Cargo.toml文件中:

[package]
name = "link-example"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]
build = "build.rs"

还有你的 build.rs:

fn main() {
    println!(r"cargo:rustc-link-search=C:\Rust\linka\libsoundio-1.1.0\i686");
}

请注意,您的构建脚本可以使用 Rust 的所有功能,并且可以根据目标平台(例如 32 位和 64 位)输出不同的值。
最后,您的代码:
extern crate libc;

use libc::c_char;
use std::ffi::CStr;

#[link(name = "soundio")]
extern {
    fn soundio_version_string() -> *const c_char;
}

fn main() {
    let v = unsafe { CStr::from_ptr(soundio_version_string()) };
    println!("{:?}", v);
}

证明就在布丁中:
$ cargo run
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target\debug\linka.exe`
"1.0.3"

理想情况下,您将创建一个名为soundio-sys的包,使用*-sys 包的惯例。该包仅需有一个构建脚本,链接到适当的库并公开 C 方法。它将使用Cargo links来唯一标识本地库,并防止多次链接到它。其他库可以包含这个新包,而不必担心这些链接细节。

18

另一种可能的方法是设置RUSTFLAGS,例如:

RUSTFLAGS='-L my/lib/location' cargo build # or cargo run

我不知道这是否是最有条理和推荐的方法,但它适用于我的简单项目。


3
非常感谢!这个回答被低估了! - yerlilbilgin
能否将RUSTFLAGS移入配置文件中?我按照这份文档操作,但没有成功。https://doc.rust-lang.org/cargo/reference/config.html @hbobenicio 这是我尝试的方式:https://stackoverflow.com/questions/77250705/why-the-rustflags-parameter-in-cargo-toml-did-not-work-in-macos - undefined

6
我找到了一个可以正常工作的东西:你可以在你的 Cargo.toml 文件中指定链接。
[package]
links = "libsoundio"
build = "build.rs"

这指定项目链接到libsoundio。现在,您可以在.cargo/config文件中指定搜索路径和库名称:

[target.i686-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/i686"]
rustc-link-lib = [":libsoundio.a"]

[target.x86_64-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/x86_64"]
rustc-link-lib = [":libsoundio.a"]

(: 前缀告诉 GCC 使用实际的文件名,而不是添加所有愚蠢的 lib 前缀和扩展名魔法。)

您还需要创建一个空的 build.rs 文件:

fn main() {}

这个文件不会被运行,因为.cargo/config中的值会覆盖其输出,但由于某种原因,Cargo仍然需要它 - 无论何时使用links =,都必须有build =,即使它没有用到。

最后在main.rs中:

#[link(name = "libsoundio")]
#[link(name = "ole32")]
extern {
    fn soundio_version_string() -> *const c_char;
}

谢谢!我该如何为我的目标获取名称(我正在使用Mac和Linux)?我应该将.cargo/config提交到版本控制中,还是每个开发人员都应该有不同的配置文件? - Envek
目标由 rustup 设置。运行 rustup show。(或者您可以使用类似于 cargo --target blah 的内容覆盖默认值。).cargo/config 应该在版本控制中。然而,大多数人似乎使用非空的 build.rs 来完成这个任务,而不是 .cargo/config - Timmmm
build.rs是必需的,因为在[package]部分中已经指定要使用它。 - Raffaello

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