链接
注意: 这部分所描述的内容,主要是从编译器角度进行,而非语言。
编译器支持静态和动态地将不同的 crate 链接起来。 本节将探讨链接 crate 的各种方法,有关本地库的更多信息可以参阅《Rust 程序设计语言》中 FFI 章节 。
在一次编译会话中,编译器可以通过使用命令行标志或 crate_type
属性生成多个制品。
如果指定了一个或多个命令行标志,则忽略所有 crate_type
属性,仅构建由命令行所指定的制品。
-
--crate-type=bin
,#![crate_type = "bin"]
- 将生成可运行的可执行文件。 在 crate 中需要有一个main
函数,程序开始执行时,首先运行该函数。 并将链接所有的语言以及本地的依赖项,生成可分发的单个二进制文件。 这是 crate 的默认类型。 -
--crate-type=lib
,#![crate_type = "lib"]
- 将生成 "Rust 库" 。这是一个泛指的选项,因为库有多种形式。 其表示生成 "编译器推荐" 的库格式。目前生成的库,意味着对于 rustc 始终是可用的,但实际所生成库的类型可能会随时间而变化。 其余的输出类型都表示了单独的且不同的库类型,而lib
类型可以看作是其中一个的别名 (但实际的类型由编译器定义) 。 -
--crate-type=dylib
,#![crate_type = "dylib"]
- 将生成动态 Rust 库。这与lib
输出类型不同,此选项强制生成动态库。 所生成的动态库可以用作其他库和/或可执行文件的依赖项。 此输出类型将在 Linux 上创建*.so
文件,在 macOS 上创建*.dylib
文件,在 Windows 上创建*.dll
文件。 -
--crate-type=staticlib
,#![crate_type = "staticlib"]
- 将生成静态系统库。 这与其他库输出不同,因为 Rust 编译器决不会尝试链接staticlib
格式的库。 此输出类型的可以创建一个包含所有本地 crate 代码以及所有上游依赖项的静态库, 将在 Linux、macOS 和 Windows (MinGW) 上创建*.a
文件,在 Windows (MSVC) 上创建*.lib
文件。 此格式推荐在将 Rust 代码链接到现有的非 Rust 应用程序时使用,因为这一格式的库不会对其他 Rust 代码产生动态依赖。 -
--crate-type=cdylib
,#![crate_type = "cdylib"]
- 将生成一个动态系统库。 这用于编译可从另一种语言中加载的动态库。 此输出类型将在 Linux 上创建*.so
文件,在 macOS 上创建*.dylib
文件,在 Windows 上创建*.dll
文件。 -
--crate-type=rlib
,#![crate_type = "rlib"]
- 将生成 "Rust库" 文件。这用作中间制品,可以看作是 "静态Rust库" 。 这些rlib
文件不像staticlib
文件一样被编译器静态链接。 编译器在将来的链接中将解释这些rlib
文件,这基本上意味着rustc
将像在动态库中查找元数据一样查找rlib
文件中的元数据。 这种输出形式用于生成静态链接的可执行文件以及staticlib
输出。 -
--crate-type=proc-macro
,#![crate_type = "proc-macro"]
- 产生的输出未指定,但如果提供了-L
路径,则编译器将识别输出制品为宏,它可以加载到程序中。 使用此 crate 类型编译的 crate 必须仅导出 过程宏 。编译器将自动设置proc_macro
配置选项 。这些crate总是使用编译器自身构建的相同目标进行编译。 例如,如果您从 Linux 执行编译器,并且使用x86_64
CPU,则目标将是x86_64-unknown-linux-gnu
,即使 crate 是正在为不同目标构建的另一个 crate 的依赖项。
请注意,这些输出是可叠加的,这意味着如果指定多个,则编译器将生成每种形式的输出而无需重新编译。
但是,这仅适用于由同一方法指定的输出。如果仅指定了 crate_type
属性,则将构建所有输出,但是如果指定了一个或多个 --crate-type
命令行标志,则仅构建这些输出。
有了这些不同类型的输出,如果 crate A 依赖于 crate B ,那么编译器可以在系统中以各种不同的形式找到 B 。
然而,编译器查找的唯一格式是 rlib
格式和动态库格式。有了这两个选项,编译器必须在这两种格式之间做出选择。
考虑到这一点,编译器在确定将使用哪种依赖关系格式时遵循以下规则:
-
如果正在生成静态库,则所有上游依赖项都必须以
rlib
格式可用。这个要求因为动态库无法转换为静态格式。请注意,无法将本机动态依赖项链接到静态库中,在这种情况下,将打印所有未链接的本机动态依赖项的警告。
-
如果正在生成一个
rlib
文件,则上游依赖项可用的格式没有限制。只需要所有上游依赖项都可用于读取元数据。这样做的原因是,
rlib
文件不包含任何上游依赖项。所有rlib
文件包含libstd.rlib
的副本时效率不高! -
如果正在生成可执行文件且未指定
-C prefer-dynamic
标志,则首先尝试在rlib
格式中查找依赖项。 如果某些依赖项在rlib
格式中不可用,则尝试动态链接 (见下文) 。 -
如果正在生成动态库或正在以动态链接方式链接的可执行文件,则编译器将尝试在 rlib 或 dylib 格式中协调可用的依赖项以创建最终产品。
编译器的主要目标是确保库在任何构件中都不会出现多次。例如,如果动态库 B 和 C 分别静态链接到库 A,那么一个 crate 就无法将 B 和 C 链接在一起,因为会有两个 A 的副本。 编译器允许混合使用 rlib 和 dylib 格式,但必须满足此限制。
目前,编译器没有实现提示链接库应使用哪种格式的方法。在动态链接时,编译器将尝试最大化动态依赖性,同时仍允许某些依赖性通过 rlib 进行链接。
对于大多数情况,如果要动态链接,建议将所有库都作为 dylib 可用。对于其他情况,如果编译器无法确定链接每个库时要使用的格式,编译器将发出警告。
总的来说, --crate-type=bin
或 --crate-type=lib
应该足以满足所有编译需求,其他选项只是提供更细粒度的控制,以便更好地控制 crate 的输出格式。
静态和动态C运行时
通常,标准库会尽可能地支持适合目标的静态链接和动态链接 C 运行时。
例如, x86_64-pc-windows-msvc
和 x86_64-unknown-linux-musl
目标通常都带有两种运行时,用户可以选择其中一个。
编译器中的所有目标都有链接到 C 运行时的默认模式。通常,默认情况下目标是动态链接的,但也有一些例外,默认情况下是静态链接的,例如:
arm-unknown-linux-musleabi
arm-unknown-linux-musleabihf
armv7-unknown-linux-musleabihf
i686-unknown-linux-musl
x86_64-unknown-linux-musl
C 运行时的链接是配置为遵守 crt-static
目标特性的。这些目标特性通常是通过编译器本身的标志从命令行配置的。例如,要启用静态运行时,你将执行:
rustc -C target-feature=+crt-static foo.rs
而要动态链接到 C 运行时,则会执行:
rustc -C target-feature=-crt-static foo.rs
不支持在 C 运行时链接方式之间切换的目标将忽略此标志。建议在编译器成功后检查生成的二进制文件,确保它被链接为你所期望的方式。
对于 Crate 也可以了解 C 运行时的链接方式。例如,在 MSVC 上编写的代码需要根据链接的运行时以不同的方式编译 (例如使用 /MT
或 /MD
) 。
这是通过 cfg
属性 target_feature
选项 导出的:
#![allow(unused)] fn main() { #[cfg(target_feature = "crt-static")] fn foo() { println!("the C runtime should be statically linked"); } #[cfg(not(target_feature = "crt-static"))] fn foo() { println!("the C runtime should be dynamically linked"); } }
还请注意,Cargo 构建脚本可以通过 环境变量 获得这个特性。在构建脚本中,你可以通过以下方式检测链接方式:
use std::env; fn main() { let linkage = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or(String::new()); if linkage.contains("crt-static") { println!("the C runtime will be statically linked"); } else { println!("the C runtime will be dynamically linked"); } }
要在本地使用此功能,通常会使用 RUSTFLAGS
环境变量通过 Cargo 指定编译器的标志。
例如,在 MSVC 上编译静态链接的二进制文件,你可以执行:
RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-pc-windows-msvc