iOS 编译和部署 Rust Library

这是一篇译文,原文连接为 Building and Deploying a Rust library on iOShtml

首先,咱们须要安装 Xcode,而后设置 Xcode 编译工具。若是你已经安装了 Xcode 编译工具而且已经将其更新到最新了,你能够跳过这一步。ios

xcode-select --install
复制代码

接下来,咱们须要确保安装了 Rust 环境来编译 iOS 架构产物。这一步咱们须要安装 rustup。一样的,若是你已经安装了,就能够跳过这一步。Rustup 安装工具将安装 Rust 官方渠道的 release 包而且方便你切换不一样的 release 版本。这样有益于你未来的 Rust 开发,而不止因而这篇文章所讨论的。git

curl https://sh.rustup.rs -sSf | sh
复制代码

而后添加 iOS 架构到 rustup,这样咱们就能编译跨平台产物了。github

rustup target add aarch64-apple-ios armv7-apple-ios armv7s-apple-ios x86_64-apple-ios i386-apple-ios
复制代码

当你安装 Rust 也会同时安装 cargo(相似 cocoapods、pip、gems 的 Rust 的包管理工具)。如今咱们使用 cargo 来安装 cargo-lipo。这是一个 cargo 自动建立 iOS library 的跨平台子命令。没有这个 crate,跨平台编译 Rust 成 iOS 产物将难于上青天。shell

cargo install cargo-lipo
复制代码

如今咱们已经完成环境配置准备开始,咱们先来建立项目路径。swift

mkdir greetings
cd greetings
cargo new cargo
mkdir ios
复制代码

cargo new cargo 初始化了一个以 cargo 命名的带有默认文件和路径的全新的 Rust 项目,cargo 路径下有个文件叫作 Cargo.toml,这个文件是包管理描述文件(相似 cocoapods 的 podfile),而且这里有一个子路径叫作 src,这个路径下有一个 lib.rs 文件(若是没有就 touch lib.rs 新建一个),咱们写的 Rust 代码都放在这个文件夹下。数组

咱们的 Rust 项目将很是简单,就是一个 Hello World 项目。它包含一个函数叫作 rust_greeting,这个函数接受一个 string 做为入参并返回一个 string。所以,假如入参是 “world”,返回值是 “Hello world”。xcode

打开 cargo/scr/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]
pub extern fn rust_greeting_free(s: *mut c_char) {
    unsafe {
        if s.is_null() { return }
        CString::from_raw(s)
    };
}
复制代码

咱们一块儿来看看上面都写了啥。app

咱们不可能用 Rust 来调用这个 library,因此咱们用 C 桥接。编译默认会在编译时破坏函数名,这里使用 #[no_mangle] 来告诉编译器不要破坏函数名,确保咱们的函数名称被导入到 C 文件。

extern 告诉 Rust 编译器这个方法将要在 Rust 之外的地方调用,所以要确保其按照 C 的调用规则编译。

rust_greeting 方法接受一个 C 类型的字符串数组指针,咱们须要把这个 C 字符串转成 Rust 的 str 类型。首先,咱们用指针建立一个 CStr 对象。而后咱们把这对象转成 Rust 的 str 类,而后检查转换是否成功,假若有错误发生,那咱们以 there 代替入参,不然咱们使用入参。而后在入前面拼接一个 Hello,而后返回,返回的 string 咱们须要转成 CString 而后返回给 C 代码。

使用 CString 而且返回原始值能保证字符在方法返回之后仍然没有被释放。若是字符串被释放了,指针将会被指向空内存或者彻底是其余位置。可是为了确保函数返回之后字符串仍然存在,咱们须要开辟一块内存,并再也不对这块内存作任何操做。这是形成内存泄漏的缘由,因此提供第二个函数 rust_greeting_free,函数入参是一个 CString,并负责管理他的内存。咱们须要记得调用 rust_greeting_free 来确保咱们不会在 iOS 平台碰到问题。

咱们还须要建立 C 桥接文件。 在 cargo / src 中建立一个名为 greetings.h 的新文件。在这个文件中,咱们来定义一下 C 接口。咱们须要确保 iOS 调用的每一个 Rust 函数都在这里有定义。

#include <stdint.h>

const char* rust_greeting(const char* to);
void rust_greeting_free(char *);
复制代码

让咱们构建咱们的代码,以确保它能正常工做。 为了作到这一点,咱们必须完成 Cargo.toml 文件。 这将告诉 cargo 为咱们的代码建立一个静态库和 C 动态库。

[package]
name = "greetings"
version = "0.1.1"
authors = ["fluffyemily <fluffyemily@mozilla.com>"]
description = "Example static library project built for iOS"
publish = false
 [lib]
name = "greetings"
crate-type = ["staticlib", "cdylib"]
复制代码

咱们须要使用 cargo-lipo 构建咱们的 iOS 库。构建产物位置在 cargo/target/。通用 iOS 库的位置在 cargo/target/universal/release/libgreetings.a

cd cargo
cargo lipo --release
复制代码

这就是咱们第一个 Rust 库,如今咱们把它集成到 iOS 项目中去。

打开 Xcode 并建立一个新项目。 转到 File\New\Project... 并选择 iOS Application Single View Application 模板。 这个模板是 iOS 的默认应用程序。 点击下一步。

咱们以 Greetings 命名咱们的项目,确保建立的是一个 swift 项目。点击 Next 选择项目路径。咱们这里使用咱们以前建立的 ios 路径,若是你选择了其余路径,你将不得不修改咱们稍后设置的一些路径。 点击建立。

从项目导航器中选择 Greetings 项目,而后确保选择 Greetings target。 打开常规选项卡, 向下滚动到 Linked Frameworks and Libraries 部分。

导入你的 libgreetings.a 库,方法是从 Finder 中拖动它,或者点击列表底部的 + ,点击 Add other... 并导航到 cargo/target/universal/release/, 选择 libgreetings.a,而后点击 Open。

连接 libresolv.tbd。 点击 Linked Frameworks 列表底部的 + 并在搜索框中键入 libresolv。 选择 libresolv.tbd,而后“添加”。

须要一个 bridging header 来访问咱们建立的 C 文件。 首先,让咱们将 greetings.h 导入到 Xcode 项目中,这样咱们就能够连接到它。 转到 File\Add files to“Greetings.h” ... 导航到 Greetings.h 并选择 Add。

要建立 bridging header,请转到 File\New\File..。 从提供的选项中选择 iOS Source Header File 并选择 Next。 将文件命名为 Greetings-Bridging-Header.h 并选择 Create。

打开 bridging header 并修改成以下所示:

#ifndef Greetings_Bridging_Header_h
#define Greetings_Bridging_Header_h

#import "greetings.h"

#endif
复制代码

咱们须要告诉 Xcode 怎么连接 bridging header。 从项目导航器中选择 Greetings 项目,而后确保选择 Greetings target 并打开 Build Settings 选项卡。 将 Objective-C Bridging Header设置为 $(PROJECT_DIR)/Greetings/Greetings-Bridging-Header.h

咱们还须要告诉 Xcode Rust 库连接地址。在 Build SettingsLibrary Search Paths 添加 $(PROJECT_DIR)/../../cargo/target/universal/release

按下 Command + R,编译成功。

如今咱们已经将 Rust 库导入到咱们的 iOS 项目中,并成功连接。可是咱们尚未调用 Rust 库。咱们新建一个 swift 文件,命名为 RustGreetings

添加如下代码:

class RustGreetings {
    func sayHello(to: String) -> String {
        let result = rust_greeting(to)
        let swift_result = String(cString: result!)
        rust_greeting_free(UnsafeMutablePointer(mutating: result))
        return swift_result
    }
}
复制代码

这里建立了一个调用 Rust 库的新类用做接口。这将为咱们在 APP 的主逻辑使用提供抽象,不用关心 Rust 库调用的细节,这些细节包括从 C 字符串到 Swift 字符串的转换,和咱们不用调用释放内存的函数也不会出现内存泄漏。

打开 ViewController.swift 中的 viewDidLoad 方法并添加如下代码。

let rustGreetings = RustGreetings()
print("\(rustGreetings.sayHello(to: "world"))")
复制代码

如今构建您的项目并运行它。 模拟器将打开并开始运行你的应用程序。 当视图加载时, Xcode 的控制台将输出 “Hello world”。

你能够在 Github 上找到这个示例工程的代码。

相关文章
相关标签/搜索