Rust?搭建一個(gè)小程序運(yùn)行環(huán)境的方法詳解
搭建一個(gè)FinClip社區(qū)版docker運(yùn)行環(huán)境,安裝設(shè)置Rust開(kāi)發(fā)編譯iOS代碼的環(huán)境,設(shè)置xcode的項(xiàng)目配合,集成FinClip SDK,準(zhǔn)備好實(shí)現(xiàn)從FinClip小程序到Rust算法邏輯的端到端融合。
本篇以 iOS 為例介紹開(kāi)發(fā)環(huán)境的準(zhǔn)備。
從零到一:構(gòu)建一個(gè)能運(yùn)行小程序的App
我們先從FinClip官網(wǎng)下載最新的FinClip SDK,解壓后應(yīng)獲得 FinApplet.framework、FinAppletExt.framework、FinAppletWebRTC.framework、FinAppletBLE.framework 等一系列庫(kù)。
用 xcode 創(chuàng)建一個(gè)新項(xiàng)目,簡(jiǎn)單起見(jiàn)我們建一個(gè)基于 Objective-C 的 Storyboard。
此處注意,我們要小心命名這個(gè) App 并記住它的 Bundle ID,如下圖,我們這個(gè)App 的 Bundle ID 是 com.finogeeks.rustful.clip。
然后把 FinClip SDK 解壓包里的 FinApplet.framework 添加至工程里,注意勾選“Copy items if needed”。
在macOS 11.1以上使用xcode較新的版本(筆者所用版本為13.0)編譯上述項(xiàng)目,會(huì)出現(xiàn)報(bào)錯(cuò)無(wú)法繼續(xù),如果你有這個(gè)情況,可在項(xiàng)目Build Settings處作以下配置。
clip.xcodeproj Building for iOS Simulator, but the linked and embedded framework 'FinApplet.framework' was built for iOS + iOS Simulator
報(bào)錯(cuò)信息
Apple 從 Xcode 12.3 開(kāi)始推薦使用 xcframework 替代 Framework,本文所依賴的FinClip SDK 2.36.5 尚未提供 xcframework 版本,所以有上述問(wèn)題,編譯過(guò)程且有系列warning,但不影響運(yùn)行。在未來(lái)版本應(yīng)會(huì)被解決。
FinClip SDK 中包含 x86_64 架構(gòu),便于我們開(kāi)發(fā)時(shí)用模擬器調(diào)試。本文主要目的是試驗(yàn)在 iOS 上 FinClip 小程序和 Rust 代碼的集成,以能運(yùn)行在 simulator 為要。但是x86_64 架構(gòu)的 SDK,打包上傳應(yīng)用市場(chǎng)時(shí)會(huì)報(bào)錯(cuò),如何打包時(shí)自動(dòng)去除模擬器架構(gòu)的腳本,可以讓我們既可以用模擬器開(kāi)發(fā)調(diào)試,又能正常提交應(yīng)用市場(chǎng),不在本文探討范圍,詳情可參考官網(wǎng)iOS集成。
FinClip 安全沙箱的初始化
FinClip SDK 代碼庫(kù)成功編譯構(gòu)建至 App后,是時(shí)候進(jìn)行代碼集成。這里包括注冊(cè)生成 SDK Key 和 SDK Secret,用最少至僅 4 行代碼即可在 App 中把 FinClip SDK 初始化,準(zhǔn)備好加載運(yùn)行 FinClip 小程序。
獲得 SDK Key 以及 SDK Secret 的兩種方式
FinClip 技術(shù)分成端側(cè)和云(服務(wù)器)側(cè)兩大部分,端側(cè)即 FinClip SDK,云(服務(wù)器)側(cè)則是 FinClip 小程序管理中心/小程序商店,用于實(shí)時(shí)、動(dòng)態(tài)管理小程序的上下架以及小程序開(kāi)發(fā)者的管理(正如你所熟悉的互聯(lián)網(wǎng)小程序平臺(tái)一樣)。凡泰極客提供整套方案的兩種部署使用方式:
- FinClip.com Managed Service 方式:即由凡泰極客運(yùn)行云側(cè),開(kāi)發(fā)者把小程序的上下架管理托管。從而降低自己在服務(wù)器端的運(yùn)維成本
- On-Premise 方式:即由開(kāi)發(fā)者或開(kāi)發(fā)者所在的機(jī)構(gòu),自行部署運(yùn)維FinClip服務(wù)器側(cè),自行管理自己的開(kāi)發(fā)者,自行管控自己的小程序開(kāi)發(fā)生態(tài)。普通開(kāi)發(fā)者也可以自行免費(fèi)體驗(yàn)和使用社區(qū)版(功能和企業(yè)版版無(wú)異),在一臺(tái)個(gè)人電腦即可以運(yùn)行完整環(huán)境。
取決于我們打算用誰(shuí)的服務(wù)器端,則 SDK Key 和SDK Secret 需要在該服務(wù)器生成,因?yàn)樽罱K App 所嵌入的 SDK 需要被所連接的目標(biāo)服務(wù)器作安全授權(quán)。
方式一:采用 FinClip.com 托管服務(wù)
這是最簡(jiǎn)單直接的方式,也就是說(shuō)我們準(zhǔn)備開(kāi)發(fā)的小程序,將上架至 FinClip.com。(注意:本系列所描述內(nèi)容的驗(yàn)證,需要使用自己部署安裝的社區(qū)版。FinClip.com服務(wù)在此為了完整起見(jiàn)作簡(jiǎn)單介紹)。
首先,需到 FinClip.com 注冊(cè)一個(gè)開(kāi)發(fā)者賬戶。
其次,登錄后在管理頁(yè)面「應(yīng)用管理-新增合作應(yīng)用」,添加要集成 SDK 的目標(biāo)應(yīng)用。
具體操作詳情見(jiàn)關(guān)聯(lián)移動(dòng)端應(yīng)用
你將獲得類(lèi)似以下的 Key 和 Secret:
準(zhǔn)備把它們粘貼、復(fù)制至初始化的代碼中。
方式二:自行部署 FinClip 社區(qū)版
如果閣下按捺不止自己動(dòng)手搭建一套 FinClip、擁有一個(gè)自己掌控的小程序商店,那么也可以輕而易舉的在自己的開(kāi)發(fā)環(huán)境部署個(gè)社區(qū)版(作為前置條件,注意先安裝好 docker 相關(guān)工具):
mkdir my-finclip cd my-finclip sudo sh -c "$(curl -fsSL https://static.finogeeks.club/deploy/mop/release/install.sh)"
成功安裝后,在上述目錄下運(yùn)行:
docker-compose up -d
假如你用 MacOS 上的 Docker Desktop,打開(kāi) Dashboard 應(yīng)能看到下圖,其中每一個(gè) container 都應(yīng)該處于 running 狀態(tài)(除了mop-init "EXITED(0)" 為正常)。
此時(shí) FinClip 管理后臺(tái)(分成面向開(kāi)發(fā)者的“企業(yè)端”以及面向運(yùn)營(yíng)管理者的“運(yùn)營(yíng)端”)可通過(guò)以下 URL 訪問(wèn):
- 企業(yè)端:“http://127.0.0.1:8000/mop/mechanism/#/login
- 運(yùn)營(yíng)端:http://127.0.0.1:8000/mop/operate/#/login
登錄企業(yè)端與運(yùn)營(yíng)端的默認(rèn)用戶名為“finclip@finogeeks.com”,密碼為“123Abc”。
首先,我們自己扮演管理角色,在運(yùn)營(yíng)端登記自己準(zhǔn)備開(kāi)發(fā)移動(dòng)端應(yīng)用的 Bundle ID,Bundle ID 是你在 Apple App Store 或者某個(gè) Android 應(yīng)用商店準(zhǔn)備發(fā)布的 App 的應(yīng)用標(biāo)識(shí)。
在這里作為例子,我們新增了一個(gè) Bundle ID "com.finogeeks.rustful.clip"(記得之前在 Xcode 創(chuàng)建 App 的時(shí)候所定義的名字):
輸入后在 FinClip 也關(guān)聯(lián)了同樣的 Bundle ID
其次,我們扮演開(kāi)發(fā)者角色,到企業(yè)端中,添加一款合作應(yīng)用,姑且稱(chēng)之為 rust-ios,并關(guān)聯(lián)相應(yīng)的 Bundle ID:
至此,我們把以下信息關(guān)聯(lián)了起來(lái):
- 我們要開(kāi)發(fā)的 App 名稱(chēng),在這個(gè)例子里,叫“rust-ios”
- 這個(gè) App 的 Bundle ID 是:com.finogeeks.rustful.clip,它將適用于iOS和Android,雖然在本文我們只針對(duì) iOS 作開(kāi)發(fā)。我們首先是在xcode創(chuàng)建項(xiàng)目的時(shí)候采用了這個(gè) ID,現(xiàn)在我們把它登記到 FinClip,目的是讓平臺(tái)知道一個(gè)小程序可以運(yùn)行在什么 App 中;
- FinClip SDK 嵌入到這個(gè) App 時(shí),需要使用一對(duì)指定的 Key 以及 Secret 去對(duì)接服務(wù)器端
- 服務(wù)器端,取決于你用的是 FinClip.com的托管/SaaS 服務(wù),還是用自己部署的社區(qū)版。前者的 API Server是api.finclip.com;后者的話,缺省是127.0.0.1:8000。
FinClip SDK 在 App 中的初始化
現(xiàn)在我們準(zhǔn)備好在 Xcode 創(chuàng)建的 clip 項(xiàng)目中寫(xiě)初始化 SDK 的代碼。在AppDelegate.m,加入以下代碼:
// // AppDelegate.m // clip // #import "AppDelegate.h" #import <FinApplet/FinApplet.h> @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSString *appKey = @"22LyZEib0gLTQdU3MUauARgvo5OK1UkzIY2eR+LFy28NAKxKlxHnzqdyifD+rGyG"; FATConfig *config = [FATConfig configWithAppSecret:@"8fe39ccd4c9862ae" appKey:appKey]; config.apiServer = @"http://127.0.0.1:8000"; [[FATClient sharedClient] initWithConfig:config error:nil]; [[FATClient sharedClient] setEnableLog:YES]; return YES; } #pragma mark - UISceneSession lifecycle - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; } - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } @end
Rust 開(kāi)發(fā)環(huán)境的準(zhǔn)備
安裝R ust 環(huán)境比較簡(jiǎn)單,例如在 Mac/Linux上,一行腳本即可:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
其他相關(guān)內(nèi)容可參考官網(wǎng)。
為了能把 Rust 代碼編譯成 iOS、Android 的組件庫(kù),我們需要安裝一些平臺(tái)架構(gòu)的target:
# Android targets rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android # iOS targets rustup target add aarch64-apple-ios armv7-apple-ios armv7s-apple-ios x86_64-apple-ios i386-apple-ios
此外,我們還需要安裝兩個(gè)工具,用于構(gòu)建 iOS 的 universal library,以及從 Rust 代碼生成 C/C++ 頭文件,供 Objective-C/Swift 的項(xiàng)目在導(dǎo)入靜態(tài)庫(kù)時(shí)使用:
# 安裝 Xcode build tools(如果已經(jīng)安裝,請(qǐng)忽略) xcode-select --install # 這個(gè)cargo subcommand用于構(gòu)建iOS上的universal library cargo install cargo-lipo # 這個(gè)工具用于自動(dòng)生成 C/C++11 頭文件 cargo install cbindgen # 在Android環(huán)境,請(qǐng)先安裝Android Studio和NDK,但不在本文討論范圍 cargo install cargo-ndk
關(guān)于 cargo-lip 的介紹,可以看這里,關(guān)于 cbindgen,可以參考這里。但實(shí)際上你也可以以后有興趣慢慢看,知其然不知其所然在這里沒(méi)毛病,不影響使用。
Rust 代碼編譯成 iOS 靜態(tài)庫(kù)的驗(yàn)證
在開(kāi)始正式的開(kāi)發(fā)前,我們可以寫(xiě)一個(gè)簡(jiǎn)單的“Hello World”驗(yàn)證一下上述環(huán)境。首先用 cargo 創(chuàng)建一個(gè)新的 Rust 的 Library 工程類(lèi)型的項(xiàng)目,原因是我們會(huì)把這個(gè)library 導(dǎo)入到 iOS 的項(xiàng)目中并把其中函數(shù)注冊(cè)至 FinClip SDK 供小程序側(cè)通過(guò) JavaScript 接口調(diào)用。
cargo new --lib hello
Cargo 自動(dòng)生成以下目錄:
hello |--src | |--lib.rs |--Cargo.toml
我們的 Cargo.toml 如下:
[package] name = "rustylib" version = "0.1.0" authors = ["me <finclip@finogeeks.com>"] edition = "2021" [lib] name = "rustylib" # 構(gòu)建iOS和Android版本需要的兩個(gè)crate crate-type = ["staticlib", "lib"] # 編譯Android版時(shí)需要,本文不涉及,但列出在此供Android開(kāi)發(fā)者參考 [target.'cfg(target_os = "android")'.dependencies] jni = { version = "0.19.0", default-features = false }
現(xiàn)在我們修訂一下 lib.rs。因?yàn)檫@個(gè)實(shí)驗(yàn)項(xiàng)目并不是為了簡(jiǎn)單跑一個(gè) Rust 'Hello World',而是為了驗(yàn)證輸出一個(gè)可以供異構(gòu)語(yǔ)言調(diào)用的 C Library,所以在這里我們用了 Rust FFI(Foreign Function Interface)來(lái)寫(xiě)(看上去比“正常”的'Hello World'復(fù)雜):
// lib.rs use std::ffi::{CStr, CString}; use std::os::raw::c_char; #[cfg(target_os = "android")] mod android; #[no_mangle] pub unsafe extern "C" fn hello(to: *const c_char) -> *mut c_char { let c_str = CStr::from_ptr(to); let recipient = match c_str.to_str() { Ok(s) => s, Err(_) => "you", }; CString::new(format!("From Rust: {}", recipient)) .unwrap() .into_raw() } #[no_mangle] pub unsafe extern "C" fn hello_release(s: *mut c_char) { if s.is_null() { return; } drop(CString::from_raw(s)); } #[no_mangle] pub extern "C" fn hello_world() { println!("Hello, World"); }
Rust 編譯器編譯代碼時(shí),會(huì)修改我們定義的函數(shù)名稱(chēng),增加一些用于其編譯過(guò)程的額外信息。為了使 Rust 函數(shù)能在其它語(yǔ)言(例如Objective-C、Swift)中被調(diào)用,必須禁用 Rust 編譯器的名稱(chēng)修改功能。所以我們使用了 no_mangle 的函數(shù)屬性聲明去指示編譯這些準(zhǔn)備注冊(cè)到 FinClip SDK 的函數(shù)。
另外,我們還使用了 extern "C"的聲明,以告知編譯器這些被如此聲明的函數(shù)是為了供 Rust 以外的其他語(yǔ)言代碼調(diào)用,編譯器需要保證按C語(yǔ)言的標(biāo)準(zhǔn)規(guī)范去編譯輸出。
其他更多關(guān)于 FFI(Foreign Function Interface)以及 unsafe 等 Rust 語(yǔ)言的能力,不是本文焦點(diǎn),可參考 Rust 相關(guān)方面的內(nèi)容。在本系列后面的章節(jié)也會(huì)繼續(xù)涉及。
為了能驗(yàn)證一下上述函數(shù)能否運(yùn)行,我們編寫(xiě)一個(gè)測(cè)試?yán)樱?/p>
cd hello mkdir examples touch examples/test.rs
一個(gè)簡(jiǎn)單的測(cè)試如下:
// test.rs use std::ffi::{CStr, CString}; use rustylib::{hello, hello_release}; fn main() { let input = CString::new("Hello, world!").unwrap(); unsafe { let c_buf = hello(input.as_ptr()); let slice = CStr::from_ptr(c_buf); println!("{}", slice.to_str().unwrap()); hello_release(c_buf); } }
在 hello 項(xiàng)目的根目錄下,運(yùn)行測(cè)試:
cargo run --example test
應(yīng)產(chǎn)生如下結(jié)果:
Blocking waiting for file lock on build directory
Compiling rustylib v0.1.0 (/Users/myself/projects/hello)
Finished dev [unoptimized + debuginfo] target(s) in 5.89s
Running `target/debug/examples/test`
From Rust: Hello, world!
現(xiàn)在可以嘗試為 iOS 進(jìn)行編譯:
$ cargo lipo --release
我們可以檢查一下生成的靜態(tài)庫(kù):
lipo -info target/aarch64-apple-ios/release/librustylib.a Non-fat file: target/aarch64-apple-ios/release/librustylib.a is architecture: arm64 ipo -info target/x86_64-apple-ios/release/librustylib.a Non-fat file: target/x86_64-apple-ios/release/librustylib.a is architecture: x86_64
但我們用于開(kāi)發(fā)的是一個(gè)合并了上述兩個(gè)架構(gòu)的通用庫(kù) Fat library,在以下目錄中:
ls -l target/universal/release/librustylib.a
要在 iOS 驗(yàn)證這部分代碼,可以先生成一個(gè) C 的頭文件,在 hello 這個(gè) Rust 項(xiàng)目的根:
cbindgen src/lib.rs -l c > rustylib.h
然后把這個(gè)頭文件添加至AppDelegate.m,再對(duì)其進(jìn)行修訂,把'hello'和'hello_release'直接按C的方式調(diào)用一下即可。代碼非常簡(jiǎn)單,不在此贅述。但是在xcode中需要把上述生成的librustylib.a以及rustylib.h添加至項(xiàng)目中(如果不是iOS開(kāi)發(fā)者不熟悉xcode,可以跳過(guò)本部分驗(yàn)證,繼續(xù)閱讀本系列后續(xù)篇章的詳細(xì)介紹)。
至此,我們把iOS Native App、FinClip SDK和Rust library三個(gè)部分集成起來(lái),接下來(lái)的內(nèi)容,將是聚焦開(kāi)發(fā)一個(gè)比“Hello World”復(fù)雜點(diǎn)的、確實(shí)適合用Rust實(shí)現(xiàn)的library,并讓它通過(guò)FinClip小程序來(lái)展現(xiàn)人機(jī)交互的界面。開(kāi)發(fā)過(guò)程所用到的工具有點(diǎn)多,你需要:
- xcode:用于編譯構(gòu)建“殼”應(yīng)用,以及通過(guò)simulator測(cè)試你的應(yīng)用
- FinClip IDE:用于開(kāi)發(fā)調(diào)試小程序
- FinClip.com(或者運(yùn)行在你本地電腦上的FinClip社區(qū)版)的企業(yè)端和運(yùn)營(yíng)端
- vscode以及一些有助于開(kāi)發(fā)測(cè)試Rust代碼的extension(當(dāng)然,你也可以用其他vscode替代工具)
對(duì)于首次開(kāi)發(fā)Rust的朋友,在vscode推薦安裝以下extension:
- Better TOML,用于支持Cargo.toml文件的syntax highlight
- crates,用于支持Cargo.toml中crate的版本依賴關(guān)系管理
- rust-analyzer,似乎優(yōu)于官方的rust extension
- CodeLLDB,能支持C++、Rust等編譯語(yǔ)言的debugger
- Tabnine AI Auto-complete,一句話,智能好使
到此這篇關(guān)于Rust 搭建一個(gè)小程序運(yùn)行環(huán)境的文章就介紹到這了,更多相關(guān)Rust 小程序運(yùn)行環(huán)境內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
rust中間件actix_web在項(xiàng)目中的使用實(shí)戰(zhàn)
這篇文章主要介紹了rust中間件在項(xiàng)目中的使用實(shí)戰(zhàn),包括自定義中間件,日志中間件,Default?headers,用戶會(huì)話,錯(cuò)誤處理的用法實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01詳解在Rust語(yǔ)言中如何聲明可變的static類(lèi)型變量
在Rust中,可以使用lazy_static宏來(lái)聲明可變的靜態(tài)變量,lazy_static是一個(gè)用于聲明延遲求值靜態(tài)變量的宏,本文將通過(guò)一個(gè)簡(jiǎn)單的例子,演示如何使用?lazy_static?宏來(lái)聲明一個(gè)可變的靜態(tài)變量,需要的朋友可以參考下2023-08-08rust語(yǔ)言基礎(chǔ)pub關(guān)鍵字及Some語(yǔ)法示例
這篇文章主要為大家介紹了rust語(yǔ)言基礎(chǔ)pub關(guān)鍵字及Some語(yǔ)法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07