有关 Rust 交叉编译的一些思路 (仅供参考)

这篇具有很好参考价值的文章主要介绍了有关 Rust 交叉编译的一些思路 (仅供参考)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

近来, 使用 Rust 语言开发的应用程序, 渐渐融入了开发者以及普通用户的日常生活. 它们不仅出现在我们常用的工作平台上, 不少嵌入式设备或者云服务器上也多见它们的身影.

Rust 是一种需要编译的语言, 且一些 crate 仍需要 C/C++ 的构建环境. 大多数时候, 在 Rust 工具链 (toolchain) 支持的设备上进行原生构建不会遇到什么问题. 而当目标设备的架构, 与构建时使用的设备架构相同时, 直接将构建好的产物发送至目标设备上即可使用.

不止是很多嵌入式设备使用 ARM 架构. 近来, 不少云服务器商也开始提供 ARM64 架构的通用计算平台服务. 如果不是出于构建目的而特意选择较高配置的服务计划, 只是出于部署服务的目的, 则往往不会为这些设备分配太多的性能资源, 进而不足以在其上进行项目的构建. 而作为个人开发者/用户, 往往不一定有相同平台且性能足够的设备进行构建工作, 要为这些设备可以直接执行的二进制文件 (binaries), 就需要进行交叉编译/构建.

编译/构建概念简述

要理解交叉编译/构建, 可以先从可执行程序 (executable) / 二进制文件 (binaries) 的构建开始.

可执行程序里主要包含的是机器可以理解的指令, 由于它们一般先由操作系统加载并执行, 因此还要存储在操作系统所能读取的格式中. 因此, 要 构建 出可以使用的二进制文件, 需要根据源代码, 生成 对应平台 的指令, 并在最后将这些指令以 操作系统所要求 的可执行文件格式进行封装.

当然, 实际中的项目的代码往往繁多, 最终生成的二进制文件可能也不止一个, 意味着代码之间存在复用的情况. 因此, 为了减少重复的编译工作, 以及为了在更改了某些部分的代码后不一定需要重新编译其他的部分, 源代码往往会被分开在不同的文件 (亦即不同的单元) 中进行编译, 最后综合起来形成整体; 项目通常也会使用到来自外部的依赖, 也就是所说的 “软件包” 或者 “(代码) 库”, 这些部分的代码, 并不全都以源代码的形式提供, 或以源代码的形式为项目所使用, 出于性能的考虑, 可能会提前编译完成, 以库 (library) 文件的形式为他人复用.

比如, 通常在编程时, 不管使用何种语言, 通常都会使用到语言本身所提供的一些功能, 也就是所说的 “标准库”. 解释性语言或者脚本语言, 运行程序一般不需要事先对源代码进行编译, 代码即是程序本身, 但是执行时需要解释器和运行时, 以及可能存在的外部依赖. 而如 C 或 Rust 这样默认需要编译构建形成可执行文件才能运行的语言, 程序运行期需要的标准库中的代码, 往往会在构建时一同合并 (或间接合并) 进入二进制文件. 为了减少分发工具链时的复杂性, 以及在开发该语言的程序时, 省去重新编译这些代码的时间, 标准库中的一些代码, 往往也会被预先编译成库文件, 供开发时使用.

要满足以上所说的两种代码复用的使用场景, 就需要 “链接” 的操作, 以将这些编译后的代码综合起来. 根据这些代码最后是否会进入二进制文件中, 分为动态链接和静态链接: 静态链接是直接将代码存入该二进制文件, 而动态链接的代码, 在该二进制文件执行时才会被寻找并加载.

类似地, 若要进行交叉编译, 则需要能够根据源代码生成目标平台代码以及二进制文件的工具 (以及这些工具所需要的环境), 也就是需要支持目标平台的编译器和链接器. 而像 C 或 Rust 这种较为底层的语言, 往往还需要系统层面的库, 如 C 标准库. 如果还需要使用外部库, 则还需要这些外部库针对对应平台的预构建二进制文件, 或在构建过程中直接从源代码构建依赖的这些外部库.

项目一般还会带有各种针对产物的测试, 这些测试一般需要运行构建出的程序. 而交叉编译的情况下, 要运行其他平台的代码, 则还需要某种虚拟化解决方案.

Rust 中的交叉编译/构建

Rust 的编译器 rustc 基于 LLVM 项目, 因此进行跨平台的代码生成并无问题;至于标准库, Rust 也有很广泛的预构建支持 (参见 Platform Support - The rustc book), 只需要 rustup target add 即可. 因此, 如果项目以及项目的依赖中只有 Rust 代码时, 交叉编译是比较简单的, 不过由于 Cargo 还是会使用系统默认的链接器, 因此可以指定链接器为 rust-lld, 该工具在添加 target 的时候便会一并下载, 所以可以直接使用. 以 x86_64 Linux 上为 ARM64 Linux 进行构建为例 (假设使用 GNU LIBC 而非 MUSL LIBC, 即 aarch64-unknown-linux-gnu 目标):

可以在 Cargo.toml 中配置要使用的 Linker (参见 Configuration - The Cargo Book):

[target.aarch64-unknown-linux-gnu]
linker = "rust-lld"

不过笔者倾向于直接在执行 cargo 命令时指定:

TRIPLET=aarch64-unknown-linux-gnu
rustup target add $TRIPLET
cargo build \
  --target $TRIPLET \
  --config target.$TRIPLET.linker=\"rust-lld\" 

在通过命令行指定 config 项的值时, 值需要符合 TOML 的语法, 比如这里, 引号就是需要保留的, 因此在 sh/bash 中使用了 \ 进行转义. 如果读者使用 PowerShell 或 pwsh, 转义方式略有不同, 可以参考 https://www.rlmueller.net/PowerShellEscape.htm.


而如果项目或项目的一些依赖中涉及非 Rust 代码的构建 (通常为 C/C++), 情况就要复杂些. Cross compiling for arm or aarch64 on Debian or Ubuntu 这篇文章就简单介绍了为 ARM 平台的交叉编译, 可供参考.

以 C/C++ 为例, 要为目标系统交叉构建, 纵使 Clang/LLVM 天生为了跨平台, 能够生成各种平台的代码, 但总归还是需要目标系统的标头文件以及若干标准库等. 于是倒不如使用常见 Linux 发行版中, 通过包管理器就能直接安装的 GCC 交叉编译工具链, 虽然需要为不同的交叉编译目标安装不同的工具链, 但这些工具链一般都已经成熟, 一般不需要什么额外的配置. 还是以上文的 aarch64-unknown-linux-gnu 平台为例:

首先安装工具链:

sudo apt install gcc make gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu

GCC 交叉编译工具链中的工具名, 即是在之前的 gcc, ld 前边, 加上目标的标识, 即 aarch64-linux-gnu-gcc, aarch64-linux-gnu-ld.

使用 Rust 的好处是, 尽管是需要 C/C++ 代码的 crate 依赖, 里面一般也都写好了构建脚本, 因此只需装好交叉构建需要的工具链, 再为 Cargo 指定好构建的目标 target, Cargo 便会 (调用一层 wrapper, 进而) 完成 CC, CXX 等编译器的选择, 这些编译器又内置了链接器的使用与配置, 因此便无需再多费心了. 只是同样可能需要手动指定链接时使用的链接器 linker. (参见 GuillaumeGomez/sysinfo - GitHub)

TRIPLET=aarch64-unknown-linux-gnu
rustup target add $TRIPLET
cargo build \
  --target $TRIPLET \
  --config target.$TRIPLET.linker=\"aarch64-linux-gnu-gcc\" 

需要注意, 这里 linker 位置实际上给的是 aarch64-linux-gnu-gcc. 笔者最初测试时, 想当然地填上了 aarch64-linux-gnu-ld, 结果虽然构建成功, 但构建产物完全无法使用.


除此之外, 有基于 musl 的交叉构建目标. 相较于 GNU LIBC, musl 的代码是一次 LIBC 的重新实现, 它的存在一定程度上使 Linux 系统间的交叉编译更简单. 目前只有少数 Linux 发行版使用 musl 作为系统的 LIBC, 因此在大多数平台上, 即使是为相同平台构建, 但是是使用 musl 的目标 (比如在 x86_64-unknown-linux-gnu 平台上构建 x86_64-unknown-linux-musl 目标), 也是一种交叉编译, 不过由于 musl 是一种对 libc dropped-in 般的替换, 因此除了指定 target 外, 并不需要额外做什么配置. 如果要为其他平台交叉编译也很简单, 只需要下载当前平台上, 针对目标平台的 musl 工具链, 其中包含类似上文中所述 GCC 交叉编译工具链中的工具, 将该工具链 (临时) 添加到 PATH 中, 就能和上一节所述一样, 为 Cargo 所使用.


上述所述, 仅为大致的思路, 在进行具体的实践时, 仍有可能会遇到特定的问题.

此外, 也有 cross-rs/cross 这样, 使整个交叉编译过程更简单的项目. 不过因为这些项目多使用 Docker 作为底层驱动, 笔者更倾向于不使用 Docker 的方式. 亦有 zigbuild 这种项目, 使用 ziglang 这门语言的编译工具链, 进行 C/C++ 的编译. 本篇文章中不再过多展开, 有兴趣的读者可以自行了解.

一个提到 zigbuild 的讨论: https://users.rust-lang.org/t/cross-compile-for-aarch64-unknown-linux-gnu-on-windows/79654.文章来源地址https://www.toymoban.com/news/detail-707442.html

到了这里,关于有关 Rust 交叉编译的一些思路 (仅供参考)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 我的医学预测模型评价步骤(仅供参考)

    个人意见,仅供参考 一切变化都是源于决策曲线分析,据说决策曲线分析已经获得了预测模型界的认可,也已经被写进了预测模型的报告指南–TRIPOD 中。一篇在pubmed上发表的关于如何使用决策曲线分析的指导论文,给出了使用决策曲线分析的几点推荐:1. 确定临床使用场景

    2024年02月02日
    浏览(63)
  • 分布式计算----期末复习题(仅供参考)

    一.单选题,每个2分 1.Hadoop 之父 是下面的哪一位?(B) A. James Gosling        B.Doug Cutting    C.Matei Zaharia   D.Linus Benedict Torvalds 2.Hadoop中,用于 处理或者分析海量数据 的组件是哪一个?(  B   ) A.HDFS     B.MapReduce     C.Yarn   D.以上选项都不是 3.HDFS中 存储和管理元数据

    2024年02月10日
    浏览(50)
  • uniapp获取手机号(前端部分,仅供参考~)

    html部分 js部分 api部分

    2024年02月09日
    浏览(54)
  • 轴承故障诊断系统的需求说明,仅供参考使用

    项目名称:轴承故障诊断系统 项目目标 开发一个自动化系统,用于测试和诊断工业轴承的潜在故障。系统将通过分析从轴承收集的振动数据来检测异常模式,以预测故障并提供维护建议。 硬件需求 传感器 :高精度振动传感器,型号:Honeywell 78628/1NC。 数据采集卡 :NI PXI-

    2024年01月23日
    浏览(41)
  • halcon不能连接海康相机解决方法(仅供参考)

    halcon不能连接相机有很多原因,这里作者给出其中一种的解决方法。 首先需要自行先下载海康软件,   1.首先点开Development, 2.根据图片的路径,点开以下文件夹  3.根据自己电脑安装的halcon版本打开对应文件夹     4.我的电脑是win64位的,根据自己的电脑打开对应的文件夹。

    2024年02月12日
    浏览(43)
  • 【Software Testing】【期末习题库】【2023年春】【仅供参考】

    类型 总分占比 平时成绩 40% 考试/考查 60% 题型 题量×分值 备注 单选 20×1’ 多选 10×3’ 全对=3’,错1个=0’,少选=-1’ 填空 10×2’ 判断 5×2’ 大题 2×10’ 平时习题(3次): ①软件测试概述 ②黑盒测试 ③白盒测试和性能测试 期中考试(1次) 大题1:平时课上练习过的习题

    2024年02月10日
    浏览(47)
  • 运维系列(仅供参考):官方文档翻译 -- 开始 Elasticsearch 入门

    想快速入门,来看 官方文档吧!!! 个人翻译版 Github地址,英语水平有限,有错误或遗漏的欢迎留言指正。 准备好如何使用 Elasticsearch 来测试驱动使用 REST APIs 来存储、查询、分析数据了吗? 遵循如下开始教程: 获取一个 Elasticsearch 实例并启动运行 添加索引实例文档 使用

    2024年04月27日
    浏览(62)
  • Python爬取MidJourney历史图片【仅供参考学习使用】

    使用MidJourney时, 在https://www.midjourney.com/app/这里有接口https://www.midjourney.com/api/app/recent-jobs/?amount=35dedupe=truejobStatus=completedjobType=upscaleorderBy=newpage=3prompt=undefinedrefreshApi=0searchType=advancedservice=nulltoDate=2023-06-16+09%3A50%3A17.379092type=alluserId=b12e169c-f609-4fd6-b917-11c2deaa8cffuser_id_ranked_score=n

    2024年02月13日
    浏览(55)
  • 删除文件后磁盘空间未释放,只能重启进程?(仅供参考)

    很多运维同学都遇到过“磁盘告警”,遇到这种情况就需要去清理磁盘。 这时候,很多同学通过各种途径、手段、命令找到了占用磁盘比较大的文件,然后大手一挥,  以为这样任务就完成了,谁知道,一查询磁盘使用量还是居高不下,完全没有释放。 这是因为在Linux中,如

    2024年02月11日
    浏览(37)
  • gamit10.71解算教程(入门级别,仅供参考)

    数据准备 1.1 数据下载 本次实例拟使用aira bjnm chan cmum hkws mizu twtf ulab等IGS站数据进行解算,计算日本的两个站mizu, aira的位置,解算时间为2019年129天。 在主目录下创建工程文件目录 mkdir test 链接tables文件夹下中的文件 sh_setup -yr 2019 -apr igs14_comb.apr (注:igs14_comb.apr文件中保存

    2024年01月25日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包