如果你感觉自己被困住了,焦虑并充满消极情绪,生命出现了停滞,那么治疗方法很简单:做点什么。
大家好,我是柒八九 。一个专注于前端开发技术/Rust
及AI
应用知识分享 的Coder
前言
之前我们不是写了一篇Rust 赋能前端-开发一款属于你的前端脚手架,从系统架构角度带大家看如何从0到1
构建一个功能完备的前端脚手架。因为,内容包含很多,有些同学说有点消化不了,所以前段时间又写了几篇关于写脚手架可能会用到的技术。
有动手能力强的小伙伴,就开始动手写自己的脚手架了。在他们写完功能后,他们就想要把脚手架编译成二进制文件
,并且通过直接访问或者设置.bashrc
等全局访问。更有甚者,他们还想让自己的朋友使用。在实际操作过程中,就会发生一个问题。
A
同学用Mac
构建了一个工具
,但是她想让B
同学在Windows
环境上使用。此时就会发生问题,我们都知道Windows
和Mac
由于系统架构的不同,在它们环境下编译的二进制文件是不互通的。
之前,我们处理的方式就是采用交叉编译也就是大家说的跨平台编译。但是呢,由于受文章内容的限制,我们就一带而过,没有过多的去解释。
而有的小伙伴,想了解这方面的知识。所以,今天我们就来聊聊--Rust跨平台编译
好了,天不早了,干点正事哇。
我们能所学到的知识点
- 跨平台编译及其在Rust中的好处
- Rust 目标三元组
- Rust原生跨平台编译
- 项目初始化
- 从Mac到Windows环境的跨平台编译
- 如何编写特定于平台的代码
- 其他跨平台解决方案
1. 跨平台编译及其在Rust中的好处
跨平台编译
是指能够在一个平台上编译源代码,生成可以在其他平台上运行的可执行文件
或库文件
。它的主要好处是可以显著提高代码的可移植性 和复用性。
在 Rust
中,跨平台编译有以下主要优势:
-
无需依赖虚拟机 不同于
Java
和.NET
等需要虚拟机的语言,Rust
编译器直接将代码编译为机器码 ,因此可以直接在目标平台上运行,无需额外的运行时环境
,提高了性能。 -
静态链接
Rust
默认静态链接所有依赖库,生成的可执行文件是独立的,无需依赖共享库即可运行,便于部署和分发。 -
LLVM 支持
Rust
使用LLVM
作为编译器后端
,LLVM
提供了强大的跨平台支持,能为多种CPU
架构生成高质量的机器码。 -
标准库的跨平台支持
Rust
的标准库就设计为跨平台的,它利用了一些跨平台的抽象层,如跨平台系统调用接口,从而使标准库能够在不同操作系统上运行。 -
编译时单元测试
Rust
的单元测试在编译时就运行,可以确保在发布时,程序在不同平台上的行为是一致的。
需要说明的是,虽然 Rust
为跨平台编译提供了很好的支持,但由于不同平台的差异,仍然可能需要一些平台特定的代码。不过相比其他语言,Rust
的跨平台编译支持无疑更加方便和高效。
2. Rust 目标三元组
要进行跨平台编译
,我们需要知道我们要构建的平台的目标三元组 (target triple
)。Rust
使用与LLVM相同的格式。格式为<arch><sub>-<vendor>-<sys>-<env>
。
例如,
x86_64-unknown-linux-gnu
代表一个64位Linux
机器x86_64-pc-windows-gnu
代表一个64位的Windows
机器
我们可以运行rustc --print target-list
将打印出Rust
支持的所有目标。这是一段又臭又长的数据信息。
确定我们关心的平台的目标三元组
的两种最佳方法是:
- 在该平台上运行
rustc -vV
,并查找以host:
开头的行------该行的其余部分将是目标三元组
- 或者在rust platform-support页面中查找
下面一些比较常见的目标三元组
目标三元组名 | 描述 |
---|---|
x86_64-unknown-linux-gnu | 64位Linux(内核3.2+,glibc 2.17+) |
x86_64-pc-windows-gnu | 64位MinGW(Windows 7+) |
x86_64-pc-windows-msvc | 64位MSVC(Windows 7+) |
x86_64-apple-darwin | 64位macOS(10.7+,Lion+) |
aarch64-unknown-linux-gnu | ARM64 Linux(内核4.1,glibc 2.17+) |
aarch64-apple-darwin | ARM64 macOS(11.0+,Big Sur+) |
aarch64-apple-ios | ARM64 iOS |
aarch64-apple-ios-sim | ARM64上的Apple iOS模拟器 |
armv7-linux-androideabi | ARMv7a Android |
3. Rust原生跨平台编译
之前,我们在处理f_cli
的跨平台编译的时候,我们直接是用cargo build --target xx
,这是Rust
内置的方式。
但是呢,这块有一个问题。
要将源代码编译成适配特定平台,我们需要指定一个目标(target
)。这告诉编译器
我们的代码应该编译为哪个平台。因此,我们需要安装相应的 GCC。然后,将目标
添加到 Rust
工具链中。
工具链
是一组工具,帮助语言生成功能性的目标代码
。它们可以提供编译器
和链接器
程序,或者额外的库中扩展功能。
下一步是添加链接器。这可以在 Cargo
配置中设置。
Rust
编译器按顺序处理程序中的每个源代码文件 ,并检查我们的代码以确保其遵循Rust
语言的规则,并将我们的源代码转换为称为目标文件的机器语言文件 。编译器
创建一个或多个目标文件之后,另一个名为链接器
的程序将编译器生成的所有目标文件合并为一个单独的可执行程序 。除了能够链接目标文件外,链接器还能够链接库文件
。库文件是预编译代码的集合,已经被"打包"以供在其他程序中重用。
例如,如果我们想要在Mac
环境下将程序编译成可以在Windows
环境下运行的。就需要执行以下步骤
-
安装目标
mingw-w64
:shellbrew install mingw-w64
-
向
rustup
添加目标:shellrustup target add x86_64-pc-windows-gnu
-
创建
.cargo/config
- 将以下指令添加到
.cargo/config
中
shell[target.x86_64-pc-windows-gnu] linker = "x86_64-w64-mingw32-gcc"
- 将以下指令添加到
-
最后运行:
shellcargo build --target=x86_64-pc-windows-gnu --verbose
这只是其中一个平台,如果我们的程序想要在多个平台上发布,那就需要做更多的设置。这是一项功能繁杂的工程。
上面的解决方式是可以的,但是今天我们再解释一种更优雅的跨平台编译方式。--cross,该crate
曾由Rust嵌入式工作组
维护。
下面,我们就简单来启动一个小项目来讲解一下如何使用cross
进行Rust
的跨平台编译。
4. 项目初始化
又到了我们再熟悉不过的场景了。我们用cargo new
构建一个项目
shell
cargo new cross_compile
然后,我们将main.rs
中内容替换成如下代码:
rust
use current_platform::CURRENT_PLATFORM;
fn main() {
println!("我用的电脑系统是{}!", CURRENT_PLATFORM);
}
我们使用current_platform
crate来探查我们的代码运行的系统信息。
我们可以使用cargo run
来执行对应的代码。因为我的系统是mac
,所以CURRENT_PLATFORM
对应的值为x86_64-apple-darwin
。
我们可以通过rustc -vV
进行查验。
如图所示,通过current_platform
返回的值和rustc
的值是匹配的。大家可以在自己的电脑上运行上面的代码。
5. 从Mac到Windows环境的跨平台编译
通过上文我们已经得知Windows
的目标三元组是x86_64-pc-windows-gnu
那么我们就来开始我们的操作 - 在Mac
中将代码编译到Windows
环境中。
我们使用cross crate
进行操作。
第一步是运行cargo install cross
。这将把Cross
安装到$HOME/.cargo/bin
。
Cross
通过使用一个带有适当工具链的镜像的容器引擎来工作。
由于我们是macOS
,所以我们选择使用Docker
来进行处理。对于Linux
,它建议使用Podman,这是一个流行的Docker
替代品。
使用cross
进行交叉编译和cargo
类似。也是需要指定需要编译的target
shell
cross run --target x86_64-pc-windows-gnu
第一次运行时会花费一些时间,因为需要下载并启动适当的容器。
一旦完成,我们就会看到对应的代码输出。(正如上面图中的最后一行)。我们看到cross_compile.exe
正在Windows
环境上运行!
从上面的输出中可以看到,编译后的.exe
文件位于target/x86_64-pc-windows-gnu/debug
。我们可以将其复制到Windows
机器上运行,会显示预期的输出。
执行完上述工作后,我们就可以在Docker
中查看对应的镜像信息。
Cross
甚至支持在其他平台上运行测试!让我们在main.rs
文件中添加一个测试:
rust
mod tests {
use current_platform::{COMPILED_ON, CURRENT_PLATFORM};
#[test]
fn test_compiled_on_equals_current_platform() {
assert_eq!(COMPILED_ON, CURRENT_PLATFORM);
}
}
请注意,这是一个我们期望在Mac
上运行时通过的测试,但当我们跨编译到Windows
并在那里运行时将会失败。
我们在Mac
上运行cargo test
,会得到这样的输出:
要在Windows
上运行测试,语法与运行可执行文件非常相似:
sql
cross test --target x86_64-pc-windows-gnu
大约一分钟后,我们会得到输出:
很遗憾,测试失败了!
测试不是在所有平台上都受支持。此外,由于线程问题,测试是顺序运行的,这可能比在本机运行测试要慢得多。
6. 如何编写特定于平台的代码
通常,我们可能希望编写仅在一个平台上运行的代码。Rust
通过cfg属性使这变得简单。
让我们修改我们的程序,添加一个仅在Windows
上打印的消息。事实上,我们甚至不会在非Windows平台
上编译此代码:
rust
use current_platform::CURRENT_PLATFORM;
#[cfg(target_os="windows")]
fn windows_only() {
println!("该方法只在windows环境被触发");
}
fn main() {
println!("我用的电脑系统是{}!", CURRENT_PLATFORM);
#[cfg(target_os="windows")]
{
windows_only();
}
}
在这里,我们将cfg
属性应用于windows_only()
函数,以便它不会在非Windows
平台上编译。但这意味着我们只能在Windows
上调用它,因此我们将相同的cfg
属性应用于调用该函数的代码块。
实际上,我们还可以将属性应用于其他位置,如enum
、struct
和匹配表达式!
在Mac
上运行cargo run
会得到以下输出:
如我们所见,上面的输出没有Windows
特定的消息。但使用cross run --target x86_64-pc-windows-gnu
会得到以下输出:
由于编码的原因,有些汉字没显示全,但是这不是主要的核心点,我们就不做处理了。
Rust
还提供了一种根据平台信息按需应用属性的简单方法
7. 其他跨平台解决方案
上面我们介绍了两种跨平台编译的的方式
- 内置方式
cargo run --target xxx
cross run --target xx
可以说,上面的方式属于是N vs N
的。也就是可以在多个平台进行互相编译。
其实还有很多解决的方案。只不过有些解决方案是1 vs N
或者是N vs 1
的。 下面我们就简单的列举几个。
- cargo-xwin:将
Cargo
项目交叉编译为Windows msvc
目标 - cargo-zigbuild:使用 zig 作为链接器编译 Cargo 项目。
后记
分享是一种态度。
全文完,既然看到这里了,如果觉得不错,随手点个赞和"在看"吧。