Rust库交叉编译以及在Android与iOS中使用

news2024/9/21 12:46:58

请添加图片描述

本篇是关于交叉编译Rust库,生成Android和iOS的二进制文件(so与a文件),以及简单的集成使用。

1.环境

系统:macOS 13.0 M1 Pro,Windows 10

Python: 3.9.6
Rust: 1.66.1
NDK: 21.4.7075529

这里就不具体说明以上环境的安装配置了,有需要可以去对应官网查找或看文末参考链接。高版本ndk操作有所不同,我后面会说到。其他版本没有具体要求,大体一致即可。

总的来说,macOS和Windows在操作上没有太大的区别,主要是两者环境安装配置的不同,。本篇以macOS为例说明。

2.配置

Android

使用NDK 提供的 make_standalone_toolchain.py 脚本创建工具链。我们需要对我们想要编译的每个架构都这样做。

export ANDROID_HOME=实际Android sdk位置
export NDK_HOME=$ANDROID_HOME/ndk/21.4.7075529

python "$NDK_HOME\build\tools\make_standalone_toolchain.py" --api 30 --arch arm64 --install-dir NDK/arm64
python "$NDK_HOME\build\tools\make_standalone_toolchain.py" --api 30 --arch arm --install-dir NDK/arm
python "$NDK_HOME\build\tools\make_standalone_toolchain.py" --api 30 --arch x86 --install-dir NDK/x86

命令中的 --api 参数对应Android项目的targetSdk版本,同时需要注意当前NDK支持的最高版本,比如版本21最高30,版本25最高33。

创建一个新文件cargo-config.toml。该文件将告诉cargo在交叉编译期间在哪里寻找clang链接器。将以下内容添加到文件中:

# macos
[target.aarch64-linux-android]
ar = "/Users/weilu/NDK/arm64/bin/aarch64-linux-android-ar"
linker = "/Users/weilu/NDK/arm64/bin/aarch64-linux-android-clang"

# windows
[target.armv7-linux-androideabi]
ar = "D:\\NDK\\arm\\bin\\arm-linux-androideabi-ar.exe"
linker = "D:\\NDK\\arm\\bin\\arm-linux-androideabi-clang.cmd"

#[target.i686-linux-android]
#ar = "xxx/bin/i686-linux-android-ar"
#linker = "xxx/bin/i686-linux-android-clang"

上面是我macos和windows上文件目录路径,你们需要替换为自己设备上的目录路径。如果你不需要x86,可以不用添加target.i686-linux-android配置。

然后执行命令cp cargo-config.toml ~/.cargo/config将此配置文件复制到我们的.cargo目录中。所以这里你也可以直接创建config文件手动复制的cargo的安装根目录中。


在执行一开始的命令时你会看到如下警告:

WARNING:__main__:make_standalone_toolchain.py is no longer necessary. The
$NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin directory contains target-specific scripts that perform
the same task.

其实就是说$NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin目录下已经内置了,所以你也可以直接使用。举个例子:

[target.aarch64-linux-android]
ar = "/Users/weilu/android-sdk-macosx/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android-ar"
linker = "/Users/weilu/android-sdk-macosx/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android30-clang"

注意aarch64-linux-android30-clang文件中的数字是你需要的api版本。

最后是安装交叉编译组件:

rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android

按需安装,如果不用x86可以去除i686-linux-android。使用rustup target list可以查看安装结果。


补充

一开始提到高版本ndk操作有所不同,这里我用ndk(版本25.1.8937393)说明一下:

首先是没有了后缀linux-android--ar文件,需要替换为llvm-ar

[target.aarch64-linux-android]
ar = "/Users/weilu/android-sdk-macosx/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ar"
linker = "/Users/weilu/android-sdk-macosx/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android30-clang"

然后打包时会报错:

note: ld: error: unable to find library -lgcc
          clang-14: error: linker command failed with exit code 1 (use -v to see invocation)

原因是高版本将libgcc文件改名为libunwind。所以我们添加软链接,或者复制libunwind重命名为libgcc

ln -s /Users/weilu/android-sdk-macosx/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/14.0.6/lib/linux/aarch64/libunwind.a /Users/weilu/android-sdk-macosx/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/14.0.6/lib/linux/aarch64/libgcc.a

ln -s /Users/weilu/android-sdk-macosx/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/14.0.6/lib/linux/arm/libunwind.a /Users/weilu/android-sdk-macosx/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/14.0.6/lib/linux/arm/libgcc.a

iOS

iOS配置相对简单,安装Xcode,然后执行xcode-select --install安装命令行工具。

安装交叉编译组件:

rustup target add aarch64-apple-ios armv7-apple-ios armv7s-apple-ios x86_64-apple-ios i386-apple-ios

一样,按需安装。比如我只需要64位,所以实际只安装了aarch64-apple-ios

最后安装lipo,它是一个命令就可编译出iOS目前支持的5个CPU架构静态库,且自动合并成一个universal静态库。

cargo install cargo-lipo

3.例子

首先创建一个项目:

cargo new --lib rust_demo

使用vs code打开项目,推荐安装rust-analyzerBetter TOMLCodeLLDB等插件。项目结构如下图:

请添加图片描述

打开Cargo.toml文件,配置如下:

[package]
name = "rust_demo"
version = "0.1.0"
edition = "2021"

# 仅Android
[target.'cfg(target_os="android")'.dependencies]
jni = { version = "0.20.0", default-features = false }

[lib]
name = "rust_demo"
# iOS and Android.
crate-type = ["staticlib", "cdylib"]

[dependencies]
  • 添加jni依赖,因为这个只是Android使用,所以可以添加此限制条件。
  • staticlib静态库(.a)和cdylib动态库(.so)

lib.rs输入以下代码:

use std::os::raw::{c_char};
use std::ffi::{CString, CStr};

#[no_mangle]
pub extern fn rust_greeting(to: *const c_char) -> *mut c_char {
    let c_str = unsafe { CStr::from_ptr(to) };
    let recipient = match c_str.to_str() {
        Err(_) => "there",
        Ok(string) => string,
    };

    CString::new("Hello ".to_owned() + recipient).unwrap().into_raw()
}
  • 注解#[no_mangle],告诉Rust编译器不要 mangle 这个函数的名称,确保我们的函数名导出时不变。
  • extern告诉Rust编译器,此函数将从Rust外部调用。

这个方法很简单,就是传入什么字符串,前面拼接一个"Hello "。

到这里,iOS就可以直接打包使用了。Android还需要添加一个JNI方法:

#[cfg(target_os="android")]
#[allow(non_snake_case)]
pub mod android {
    extern crate jni;

    use super::*;
    use self::jni::JNIEnv;
    use self::jni::objects::{JClass, JString};
    use self::jni::sys::{jstring};

    #[no_mangle]
    pub unsafe extern fn Java_com_weilu_demo_RustGreetings_greeting(env: JNIEnv, _: JClass, java_pattern: JString) -> jstring {
        // Our Java companion code might pass-in "world" as a string, hence the name.
        let world = rust_greeting(env.get_string(java_pattern).expect("invalid pattern string").as_ptr());
        // Retake pointer so that we can use it below and allow memory to be freed when it goes out of scope.
        let world_ptr = CString::from_raw(world);
        let output = env.new_string(world_ptr.to_str().unwrap()).expect("Couldn't create java string!");

        output.into_raw()
    }
}
  • #[allow(non_snake_case)]:告诉编译器忽略java这类驼峰命名警告。
  • Java_com_weilu_demo_RustGreetings_greeting指的是在Android项目中调用的包名类名方法名,这个同C++一样。

4.打包

Android

打包命令:

cargo build --target aarch64-linux-android --release
cargo build --target armv7-linux-androideabi --release
cargo build --target i686-linux-android --release

请添加图片描述

so文件位置在项目下的target/xxx-linux-android/release/目录。

iOS

打包命令:

cargo lipo --release
# 或
cargo lipo --targets aarch64-apple-ios --release # 指定平台

请添加图片描述

静态库位置在项目下的target/universal/release/目录。


打包后librust_demo.so文件4.4M,librust_demo.a文件16.7M。这个大小说实话有点大了,所以我们在Cargo.toml添加如下优化配置:

[profile.release]
lto = true
opt-level = 'z'
strip = true
codegen-units = 1

重新编译librust_demo.so文件247K,librust_demo.a文件6.5M。这个大小还是可以接受的。

5.使用

Android

Android端代码如下,注意放到对应的包名下。

package com.weilu.demo;

public class RustGreetings {

	static {
        System.loadLibrary("rust_demo");
    }

    private static native String greeting(final String pattern);

    public String sayHello(String to) {
        return greeting(to);
    }
}

librust_demo.so(64位)放到src/main/jniLibs/arm64-v8a目录下。

调用代码验证:

RustGreetings g = new RustGreetings();
Log.d("rust_demo", g.sayHello("world"));

iOS

Frameworks,Libraries,and Embedded Content下添加librust_demo.alibresolv.tbd文件。
请添加图片描述
创建greetings.h文件,代码如下:

#include <stdint.h>
// 方法名与rust一致
const char* rust_greeting(const char* to);

Swift 项目需要桥接,创建Greetings-Bridging-Header.h,代码如下:

#ifndef Greetings_Bridging_Header_h
#define Greetings_Bridging_Header_h

#import "greetings.h"

#endif /* Greetings_Bridging_Header_h */

打开Build Settings 选项卡, 将 Objective-C Bridging Header设置为 Greetings-Bridging-Header.h路径。

请添加图片描述
然后在 Build SettingsLibrary Search Paths 添加librust_demo.a路径。

请添加图片描述

添加RustGreetings.swift文件,写入如下代码:

class RustGreetings {
    func sayHello(to: String) -> String {
        let result = rust_greeting(to)
        let swift_result = String(cString: result!)
        return swift_result
    }
}

调用代码验证:

let rustGreetings = RustGreetings()
print("\(rustGreetings.sayHello(to: "world"))")

至此,本篇结束。下一篇会基于本篇内容开发一款小工具。

参考

  • Building and Deploying a Rust library on iOS
  • Building and Deploying a Rust library on Android
  • 创建第一个Rust JNI项目
  • Support Android ndk versions r23-beta3 and up
  • Rust 编译后二进制大小和常用优化方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182480.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

收藏贴!新手到底应该购买Salesforce专业版还是企业版?

Salesforce专业版&#xff08;Professional Edition&#xff09;是一个适用于小型企业的工具&#xff0c;它具有完整Salesforce套件的许多功能&#xff0c;但也有一些明显的限制。本篇文章将具体阐明Salesforce专业版是什么&#xff0c;它的优势以及其与企业版&#xff08;Ente…

SQL Server 2008如何创建定期自动备份任务

我们知道&#xff0c;利用SQL Server 2008数据库可以实现数据库的定期自动备份。方法是用SQL SERVER 2008自带的维护计划创建一个计划对数据库进行备份&#xff0c;下面我们将SQL SERVER 2008定期自动备份的方法分享给大家。 首先需要启动SQL Server Agent服务&#xff0c;这个…

Python实现vlog生成器

Python实现vlog生成器 vlog&#xff0c;全称为Video blog&#xff0c;意为影音博客&#xff0c;也有翻译为微录。 本文将尝试用Python基于Moviepy从一个文本文件中自动生成一个视频格式的vlog&#xff0c;实现的功能如下&#xff1a; 将文件的第一行标题生成视频的片头将文件…

C++——红黑树

目录 红黑树介绍 红黑树实现 节点的插入 完整代码 红黑树介绍 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确…

5 使用pytorch实现线性回归

文章目录前提的python的知识补充基本流程准备数据构造计算图loss以及oprimizer循环训练课程代码课程来源&#xff1a; 链接课程内容参考&#xff1a; 链接以及&#xff08;强烈推荐&#xff09;Birandaの前提的python的知识补充 pytorch 之 call, init,forward pytorch系列nn.…

Python进行因子分析

1 因子分析 1.1 定义 因子分析法(Factor Analysis)是一种利用降维的思想&#xff0c;从研究原始变量相关矩阵内部的依赖关系出发&#xff0c;把一些具有错综复杂关系的变量归结为少数几个综合因子的一种多变量统计分析方法。其优势在于不仅可以在减少大量指标分析的工作量的同…

尚硅谷hello scala-配置idea2022.1.2版本创建scala2.11.8版本maven文件

0_前置说明 软件版本 idea2022.1.2 scala2.11.8 java1.8.0_144 尚硅谷资源下载 关注b站尚硅谷 idea资源 百度网盘&#xff1a;https://pan.baidu.com/s/1Gbavx34OfF29LZqJ8dc85g?pwdyyds 提取码: yyds B站直达&#xff1a;​https://www.bilibili.com/video/BV1CK411d7a…

LabVIEW NI Linux Real-Time深层解析

LabVIEW NI Linux Real-Time深层解析NI LabVIEW Real-Time模块支持NI Linux Real-Time操作系统&#xff0c;在选定的NI硬件上提供。本文介绍了具体的新特性和高级功能&#xff0c;可让您为应用充分利用NI Linux Real-Time。Linux Shell支持NI Linux Real-Time操作系统提供了全面…

《Linux0.11源码趣读》学习笔记day6

到上次记录&#xff0c;整个操作系统的全部代码就已经从硬盘加载到内存中了&#xff0c;然后这些代码又通过jmpi跳转到0x90200处&#xff0c;即硬盘第二个扇区开始处的内容 这些内容就是第二个操作系统源代码文件setup.s 不过现在先来看一下操作系统的编译过程 操作系统的编译…

后端学习 - Docker

文章目录基本概念三个核心概念&#xff1a;镜像、容器、仓库联合文件系统 UnionFS常用命令Docker File基本概念 一次配置&#xff0c;处处使用运行在同一宿主机上的容器是相互隔离的&#xff0c;各自拥有独立的文件系统容器模型和虚拟机模型的主要区别 相较于虚拟机而言&#…

【Pytorch项目实战】之生成式网络:编码器-解码器、自编码器AE、变分自编码器VAE、生成式对抗网络GAN

文章目录生成式网络 - 生成合成图像算法一&#xff1a;编码器-解码器算法二&#xff1a;自编码器&#xff08;Auto-Encoder&#xff0c;AE&#xff09;算法三&#xff1a;变分自编码器&#xff08;Variational Auto Encoder&#xff0c;VAE&#xff09;算法四&#xff1a;生成式…

九型人格是什么?

九型人格是什么? 九型人格学(Enneagram/Ninehouse)是一个有2000多年历史的古老学问,它按照人们习惯性的思维模式,情绪反应和行为习惯等性格特质,将人的性格分为九种,又被称为九柱图,起源于中亚西亚地区,和中国的八卦图有点像,近代的九型是由六十年代智利的一位心理学…

计算机组成原理 | 第四章:存储器 | 存储器与CPU连接 | 存储器的校验 | Cache容量计算

文章目录&#x1f4da;概述&#x1f407;存储器分类&#x1f407;存储器的层次结构&#x1f955;原理&#x1f955;主存速度慢的原因&#x1f955;存储器三个主要特征的关系&#x1f955;缓存-主存层次和主存-辅存层次⭐️&#x1f4da;主存储器&#x1f407;概述&#x1f955;…

【opencv】Haar分类器及Adaboost算法人脸识别理论讲解

提到opencv,就不得不提其图像识别能力,最近旷世开源的YoloX项目兴起,作为目前Yolo系列中的最强者,本人对其也很感兴趣,但是完全没用机器学习和计算机视觉的基础,知其然,不知其所以然,于是想稍稍入坑一下opencv图像识别,了解一下相关算法,(说不定以后毕设会用到呢)。…

磨金石教育影视干货分享|朋友亲身经历—给新人剪辑师的三个建议

大学的时候有一个同学很喜欢视频剪辑。平时没事就蹲在电脑前&#xff0c;下载一些素材&#xff0c;自学剪辑软件&#xff0c;慢慢的搞一些创意剪辑。那时候自媒体短视频已经很火爆&#xff0c;这位同学剪辑的视频&#xff0c;不管质量如何就往上面发。一开始我们对于新事物的认…

Java---微服务---分布式搜索引擎elasticsearch(2)

分布式搜索引擎elasticsearch&#xff08;2&#xff09;1.DSL查询文档1.1.DSL查询分类1.2.全文检索查询1.2.1.使用场景1.2.2.基本语法1.2.3.示例1.2.4.总结1.3.精准查询1.3.1.term查询1.3.2.range查询1.3.3.总结1.4.地理坐标查询1.4.1.矩形范围查询1.4.2.附近查询1.5.复合查询1…

SpringBoot+Vue项目学生读书笔记共享平台

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏…

一起自学SLAM算法:10.3 机器学习与SLAM

连载文章&#xff0c;长期更新&#xff0c;欢迎关注&#xff1a; 前面已经分析过的8种SLAM算法案例&#xff08;Gmapping、Cartographer、LOAM、ORB-SLAM2、LSD-SLAM、SVO、RTABMAP和VINS&#xff09;都可以称为传统方法&#xff0c;因为这些算法都是在人为精心设计的特定规则下…

电子技术——MOS放大器基础

电子技术——MOS放大器基础 我们已经学过MOS可以当做一个压控流源&#xff0c;使用栅极电压 vGSv_{GS}vGS​ 控制漏极电流 iDi_DiD​ 。尽管两个量的关系不是线性的&#xff0c;稍后我们将会介绍偏置在线性区的工作方法。 构建压控压源放大器 现在&#xff0c;我们有了一个压…

【Java|golang】1664. 生成平衡数组的方案数---奇数前缀和 + 偶数前缀和

给你一个整数数组 nums 。你需要选择 恰好 一个下标&#xff08;下标从 0 开始&#xff09;并删除对应的元素。请注意剩下元素的下标可能会因为删除操作而发生改变。 比方说&#xff0c;如果 nums [6,1,7,4,1] &#xff0c;那么&#xff1a; 选择删除下标 1 &#xff0c;剩下…