HBuilderX 5.31+ 支持 uni-app x 蒸汽模式中使用 uasm插件
webassembly 是浏览器中动态加载二进制库的方案,让js把部分高压力计算交给更高性能的c/c++等语言编译的二进制库来处理。
在小程序上,微信、支付宝、抖音也支持webassembly,但规范略有差异。
app平台,也都有加载二进制库的方案:
每个平台都有使用二进制库的能力,但并没有一套统一的方案。
既然 uni-app x 跨了所有平台,那么能不能统一二进制库的加载方案?
这就是 UniAssembly ,简称 uasm。
让一个c/c++写的库,比如zstd压缩解压库,可以在 uni-app x 的全平台用统一的方式来调用。
uasm插件,是和uts插件并列的一种新插件。
uts插件uasm同样在uni_modules下,它在专属目录uasm中,和utssdk目录并列。
uasm插件和uts插件都能兼容全平台,但定位不同:
当然在理论上,uasm插件,也可以覆盖uts插件的需求,即调用原生能力,但这需要开发者编写c++代码了:
uasm插件提供了这样一种能力:
一个二进制库,比如c写的zstd解压库,可以生成所有 uni-app x 支持的平台的胶水层,包括3个App平台、web和小程序平台,并在 uni-app x 的页面中通过统一的api uni.loadUasm() 来调用这个二进制库的能力进行解压。
uasm插件,虽然对js提供了统一的API,但在内部,App和非App的技术方案有差异。
uni-app x 蒸汽模式,在3个App平台使用了不同的js引擎,Android的V8、iOS的jscore、鸿蒙的arkTS/jsvm。 但 uni-app x 对外提供了一套统一的 Node-API,屏蔽了各js引擎的差异。
这相当于把nodejs部分引入了手机端。
也就是一个c库,只要有webassembly版本、node版本,就可以直接变成uasm插件,在所有 uni-app x 支持的平台被调用。这大大丰富了uasm插件生态。
如果一个c库没有webassembly,也没有 node 对接封装,那么在AI时代,封装 webassembly 和 node-api 胶水也很简单。
DCloud提供了一些工具,方便二进制库对接为uasm。
首先已经支持webassembly的库,可以直接把胶水层和库放到uasm的web目录下。
对于app平台,DCloud提供了uni-gyp工具,如果一个二进制库已经有 node 对接封装,则可以直接利用该工具生成 uasm 库。
如果一个c库没有webassembly,也没有 node 对接封装,那么在AI时代,封装也很简单。让 AI 把这个c库生成 webassembly 和 node-api 胶水,然后再用 uni-gyp工具 转为 uasm 的 app平台库。
uts插件封装二进制库,在很多地方不如uasm:
可以并存于一个uni_modules中。
js引擎的webassembly能力受限于web规范,功能和性能不如OS原生本来就提供的二进制库调用。 而uasm,是对原生二进制能力的包装,功能性能更优。
c、c++、rust、zig、go、kmp很多语言都可以生成二进制,都可以封装为uasm插件。
sqlite、crypto、cmark、FFmpeg、mqtt、lame、Tesseract、Pandoc、Tree-sitter...很多库都已经有webassembly版本或node版本,这些都可以封装为uasm。
使用 uasm 插件分为两步:
uni.loadUasm() 或 uni.loadUasmSync() 加载插件,获得其导出的方法集。两个 API 的返回类型如下:
| API | 返回类型 | 说明 |
|---|---|---|
uni.loadUasm() | Promise<UserExport> | 异步加载,全平台适用 |
uni.loadUasmSync() | UserExport | 同步加载,仅 App 平台支持 |
其中 UserExport 为插件作者自定义的导出结构,即插件对外暴露的方法集合。
// 已有 uni_modules/hello-uasm
// 同步调用,仅支持 App 平台
function invokeUasmSync(a, b) {
var libHelloUasm = uni.loadUasmSync("uni_modules/hello-uasm")
var result = libHelloUasm.add(1, 2)
console.log('result', result); //输出3
}
// 异步调用,需要跨平台 同时支持 web/微信小程序/支付宝小程序时使用异步风格
async function invokeUasm(a, b) {
try {
var libHelloUasm = await uni.loadUasm("uni_modules/hello-uasm")
var result = libHelloUasm.add(1, 2)
console.log('result', result); //输出3
} catch (e) {
console.log(e);
}
}
// 提示:不用每次都调用 uni.loadUasm/uni.loadUasmSync,当前代码仅做演示
// 上面调用的 add 方法由 hello-uasm 模块提供
真机运行说明:
插件作者在开发uasm时,注意需要安装原生编译环境。
包括 Android studio的ndk编译环境、xcode、deveco,emscripten(web/微信小程序/支付宝小程序)
然后新建一个uni_modules,选择uasm插件。
package.json 为 uni_modules 插件配置清单文件,负责描述插件的基本配置。
{
"id": "uasm-helloworld",
"displayName": "uasm插件名称",
"version": "0.1",
"description": "uasm插件描述",
"uni_modules": {
}
}
在uasm目录中,同样有 web、mp-weixin、app-android、app-ios、app-harmony等目录。
下面的结构是 uni-zstd 插件示例,zstd是一种压缩算法,该库是c语言开发的。
uni-gyp编译输出.// uni-app x 项目根目录
|-- static
|-- uni_modules
| |-- uni-zstd // 插件目录名
| | |-- uasm // 包含app平台的编译产物so/xcframework; 小程序/web平台的wasm|js文件
| | | |-- app-android // Android平台
| | | | |-- libs
| | | | | |-- arm64-v8a // arm64架构
| | | | | | |-- libUasmUniZstd.so // elf格式so库
| | | | | |-- x86_64 // x86_64架构
| | | | | | |-- libUasmUniZstd.so // elf格式so库
| | | |-- app-harmony // HarmonyOS平台
| | | | |-- libs
| | | | | |-- arm64-v8a // arm64架构
| | | | | | |-- libUasmUniZstd.so // elf格式so库
| | | | | |-- x86_64 // x86_64架构
| | | | | | |-- libUasmUniZstd.so // elf格式so库
| | | |-- app-ios // iOS平台
| | | | |-- frameworks
| | | | | |-- UasmUniZstd.xcframework // 不参与主项目的链接,首次使用时通过dlopen加载,不支持卸载否者可能导致无法预知的意外
| | | | | | |-- ios-arm64 // arm64真机
| | | | | | | |-- UasmUniZstd.framework
| | | | | | | | |-- UasmUniZstd // mach-o格式动态库
| | | | | | | | |-- Headers // 头文件目录
| | | | | | | | | |-- swift_binding.h // 通过dlopen加载后和swift绑定
| | | | | | | | | |-- uni-zstd.h // 引用swift_binding.h
| | | | | | | | |-- Info.plist
| | | | | | |-- ios-arm64_x86_64-simulator // 同时支持arm/x86架构模拟器
| | | | | | | |-- UasmUniZstd.framework
| | | | | | | | |-- UasmUniZstd // mach-o格式动态库(肥胖型)
| | | | | | | | |-- Headers // 头文件目录
| | | | | | | | | |-- swift_binding.h // 通过dlopen加载后和swift绑定
| | | | | | | | | |-- uni-zstd.h // 引用swift_binding.h
| | | | | | | | |-- Info.plist
| | | | | | |-- Info.plist
| | | |-- mp-alipay // 支付宝小程序目录,目录下的.wasm|.wasm.br只能使用其中一个
| | | | |-- uni-zstd.js // 支付宝小程序限制必须在worker中使用,创建Worker使用异步API风格拉齐差异
| | | | |-- uni-zstd.wasm // wasm文件
| | | | |-- uni-zstd.wasm.br // Brotli-compressed格式
| | | |-- mp-weixin // 微信小程序目录,目录下的.wasm|.wasm.br只能使用其中一个
| | | | |-- uni-zstd.js // 胶水绑定文件
| | | | |-- uni-zstd.wasm // wasm文件
| | | | |-- uni-zstd.wasm.br // Brotli-compressed格式
| | | |-- web // Web平台目录,目录下的.wasm|.wasm.br只能使用其中一个
| | | | |-- uni-zstd.js // 胶水绑定文件
| | | | |-- uni-zstd.wasm // wasm文件
| | | | |-- uni-zstd.wasm.br // Brotli-compressed格式
| | | |-- index.d.ts // 仅用于IDE语法提示
| | |-- utssdk // 支持在UTS插件中使用时需要该目录
| | | |-- app-android
| | | | |-- index.uts // 包装UniZstd.kt并导出
| | | | |-- UniZstd.kt // 通过jni和so绑定
| | | |-- app-ios
| | | | |-- index.uts // 包装UniZstd.swift并导出
| | | | |-- UniZstd.swift // 和xcframework绑定
| | |-- workers // uni-app worker目录规范
| | | |-- mp-alipay
| | | | |-- uni-zstd.js // wasm编译出的胶水文件,在uni-zstd-worker-index中调用
| | | | |-- uni-zstd-worker-index.js // worker入口
| | |-- package.json // 插件配置
提示: .wasm.br中的 .br 为 Brotli-compressed 格式和wasm没有直接关系,web平台需要服务器支持响应头
<plugin-id>.js | <plugin-id>.wasm 拷贝到 uni_modules/<plugin-id>/uasm/web<plugin-id>.js | <plugin-id>.wasm.br 拷贝到 uni_modules/<plugin-id>/uasm/mp-weixin<plugin-id>.js 文件拷贝到 uni_modules/<plugin-id>/workers/mp-alipay,<plugin-id>.wasm.br 文件拷贝到 uni_modules/<plugin-id>/uasm/mp-alipay注意:推荐在小程序上使用 wasm.br 减少文件大小,web可以由服务器配置gzip压缩
web平台编译参数说明
-s MODULARIZE=1 模块导出 因为需要异步加载等待wasm文件就绪-s EXPORT_ES6=1 使用ES6-s ENVIRONMENT=web 仅需支持web平台小程序编译参数说明
-s DYNAMIC_EXECUTION=0 禁用JS动态行为 因微信小程序不支持如 new Function-s MODULARIZE=1 模块导出 因为需要异步加载等待wasm文件-s EXPORT_ES6=1 使用ES6-s ENVIRONMENT=web 仅需支持web平台注意事项:
uni_modules/<plugin-id>/workers/mp-alipay)转发到js调用层,受支付宝限制只能在worker中使用,需要真机验证,否者抛异常BigInt,如果编译出的胶水中生成了 0n 语法会出现加载错误,可暂时通过 emscripten 已弃用参数 -s WASM_BIGINT=0 移除该语法本节以 double 相加和 string 拼接两个最简单的方法为例,完整演示 App 平台 uasm 插件的封装过程。
示例模块名为 test-uasm。
JS 层调用示例如下:
const uasm = uni.loadUasmSync("uni_modules/test-uasm");
uasm.add(1, 2); // 3
uasm.concat('uni', '-app'); // uni-app
新建项目目录,在项目根目录执行:
# 初始化
npm init
# 安装uni-gyp
pnpm add -D @dcloudio/uni-gyp
# or
npm install --save-dev @dcloudio/uni-gyp
# 可选napi风格,添加node-addon-api
npm i node-addon-api -save
# 可选桌面测试,添加node-gyp
npm i node-gyp -save
执行后会生成 package.json
uni-gyp 提供如下项目模版:
uni-gyp create <plugin-id>uni-gyp create <plugin-id> --template basic-and-uts,提示:插件id和UTS插件的JNI包名有映射关系 如果调整了名称对应的JNI也需要更新
文件名 binding.cc
#include <cmath>
#include <cstring>
#include <cstdint>
#include <limits>
#include <memory>
#include <new>
#include <string>
#include <node_api.h>
double add(double a, double b) {
return a + b;
}
napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_value fn;
status = napi_create_function(env, nullptr, 0, [](napi_env env, napi_callback_info info) -> napi_value {
size_t argc = 2;
napi_value args[2];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
double a, b;
napi_get_value_double(env, args[0], &a);
napi_get_value_double(env, args[1], &b);
napi_value result;
napi_create_double(env, add(a, b), &result);
return result;
}, nullptr, &fn);
status = napi_set_named_property(env, exports, "add", fn);
napi_value concat_fn;
status = napi_create_function(env, nullptr, 0, [](napi_env env, napi_callback_info info) -> napi_value {
size_t argc = 2;
napi_value args[2];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (argc < 2) {
napi_throw_type_error(env, nullptr, "concat expects two strings");
return nullptr;
}
napi_valuetype type_a, type_b;
napi_typeof(env, args[0], &type_a);
napi_typeof(env, args[1], &type_b);
if (type_a != napi_string || type_b != napi_string) {
napi_throw_type_error(env, nullptr, "concat expects two strings");
return nullptr;
}
size_t length_a, length_b;
napi_get_value_string_utf8(env, args[0], nullptr, 0, &length_a);
napi_get_value_string_utf8(env, args[1], nullptr, 0, &length_b);
// Read both values directly into one output buffer to avoid three
// intermediate string allocations and the final c_str() scan.
std::string result(length_a + length_b + 1, '\0');
size_t actual_a = 0;
size_t actual_b = 0;
napi_get_value_string_utf8(env, args[0], result.data(), length_a + 1, &actual_a);
napi_get_value_string_utf8(env, args[1], result.data() + actual_a, length_b + 1, &actual_b);
napi_value value;
napi_create_string_utf8(env, result.data(), actual_a + actual_b, &value);
return value;
}, nullptr, &concat_fn);
status = napi_set_named_property(env, exports, "concat", concat_fn);
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
{
'variables': {
"uni-gyp-root-dir%": "<!(node -p \"require('uni-gyp').root_dir\")",
'module-root-dir%': '<!(node -p "require(\'path\').resolve(\'.\')")',,
'test-sources': [
'binding.cc',
],
},
'targets': [
{
'target_name': 'UasmTestUasm',
'type': 'shared_library',
"cflags_cc": [ "-std=c++20", "-fno-exceptions" ],
'conditions': [
['OS == "android"', {
'ldflags': [
'-Wl,--unresolved-symbols=ignore-all',
# The app supplies libc++_shared.so from its default native lib
# directory; prevent the toolchain from adding libc++ statically.
'-nostdlib++',
],
'libraries': [
'-lc++_shared',
],
}],
['OS == "ios"', {
'mac_bundle': 1,
'xcode_settings': {
'PRODUCT_BUNDLE_IDENTIFIER': 'uts.sdk.modules.testUasm',
},
'ldflags': [
'-Wl,-undefined,dynamic_lookup',
],
}],
['OS == "harmony"', {
'libraries': [
'-lace_napi.z',
],
"ldflags": [
"-Wl,--unresolved-symbols=ignore-all",
],
}],
],
'sources': [
'<@(test-sources)',
]
}
]
}
{
"name": "test-uasm",
"version": "1.0.0",
"description": "test-uasm-plugin",
"gypfile": true,
"scripts": {
"build:test-uasm:android:release": "uni-gyp -platform=android --depth=. -arch=arm64 --build=Release binding.gyp && uni-gyp -platform=android --depth=. -arch=x64 --build=Release binding.gyp",
"build:test-uasm:harmony:release": "uni-gyp -platform=harmony --depth=. -arch=arm64 --build=Release binding.gyp && uni-gyp -platform=harmony --depth=. -arch=x64 --build=Release binding.gyp",
"build:test-uasm:ios:release": "uni-gyp -platform=ios --depth=. -sdk=all -arch=arm64,x64 --build=Release binding.gyp",
"build:test-uasm:pack:uni-module:release": "uni-gyp module-pack --target test-uasm",
"build:test-uasm:all": "npm run build:test-uasm:android:release && npm run build:test-uasm:harmony:release && npm run build:test-uasm:ios:release && npm run build:test-uasm:pack:uni-module:release",
}
}
一个命令生成 uni-module
// 在项目的根目录下执行,生成目录为 `uni_modules/test-uasm`
npm run build:test-uasm:all
将生成的 test-uasm 目录拷贝到 uni-app x 项目的 uni_modules/ 目录下
注意:上面的命令包含编译iOS的xcframework,仅在macOS上支持
最终编写 uni-app x 项目下的 uni_modules/test-uasm/uasm/index.d.ts 类型声明文件。
该文件仅用于 IDE 的语法提示,声明 uni.loadUasm() / uni.loadUasmSync() 返回的导出结构。封装 App 平台时,可让 AI 参考 C/C++ 导出代码自动生成。
interface TestUasm {
add(a: number, b: number): number
concat(a: string, b: string): string
}
declare const testUasm: TestUasm
export = testUasm
前文讲述的是uasm面向js的封装。如果要在uts插件,也就是kotlin/swift中使用,还得继续封装。
文件 jni_binding.cc
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jdouble JNICALL
Java_uts_sdk_modules_testUasm_TestUasm_nativeAdd(JNIEnv *, jobject, jdouble a, jdouble b) {
return a + b;
}
extern "C" JNIEXPORT jstring JNICALL
Java_uts_sdk_modules_testUasm_TestUasm_nativeConcat(JNIEnv *env, jobject, jstring a, jstring b) {
if (!a || !b) return nullptr;
const char *a_chars = env->GetStringUTFChars(a, nullptr);
const char *b_chars = env->GetStringUTFChars(b, nullptr);
if (!a_chars || !b_chars) {
if (a_chars) env->ReleaseStringUTFChars(a, a_chars);
if (b_chars) env->ReleaseStringUTFChars(b, b_chars);
return nullptr;
}
std::string result(a_chars);
result += b_chars;
env->ReleaseStringUTFChars(a, a_chars);
env->ReleaseStringUTFChars(b, b_chars);
return env->NewStringUTF(result.c_str());
}
// uni_modules/test-uasm/utssdk/app-android/TestUasm.kt
package uts.sdk.modules.testUasm
private object TestUasmLibrary {
init {
// 不需要手动加载编译出的so库,当用户调用 uni.loadUasm()/uni.loadUasmSync() 时自动加载
// 在开发期间 .so 库的位置不在android默认的lib目录下
// System.loadLibrary("UasmTestUasm")
}
fun load() = Unit
}
// uni.loadUasm()` / `uni.loadUasmSync() 返回类型 模块名的驼峰格式,首字母大写
object TestUasm {
// init {
// TestUasmLibrary.load()
// }
fun add(a: Double, b: Double): Double = nativeAdd(a, b)
fun concat(a: String, b: String): String = nativeConcat(a, b)
// 对应C/C++层的方法
private external fun nativeAdd(a: Double, b: Double): Double
private external fun nativeConcat(a: String, b: String): String
}
注意:C层的JNI命名和Kotlin层的包名必须对应
// 仅展示需要新增的配置
{
'targets': [
{
'conditions': [
['OS == "android"', {
'sources': [
'jni_binding.cc',
],
'ldflags': [
'-Wl,--version-script=<(module-root-dir)/android.exports',
],
}],
]
}
]
}
android.exports文件内容
{
global:
napi_register_module_v1;
node_api_module_get_api_version_v1;
Java_uts_sdk_modules_testUasm_TestUasm_nativeAdd;
Java_uts_sdk_modules_testUasm_TestUasm_nativeConcat;
local:
*;
};
swift_binding.h
#ifndef UASM_TEST_SWIFT_BINDING_H_
#define UASM_TEST_SWIFT_BINDING_H_
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__GNUC__)
#define UASM_TEST_SWIFT_API __attribute__((visibility("default")))
#else
#define UASM_TEST_SWIFT_API
#endif
UASM_TEST_SWIFT_API double uasm_test_add(double a, double b);
UASM_TEST_SWIFT_API const char *uasm_test_concat(const char *a, const char *b);
UASM_TEST_SWIFT_API void uasm_test_free_string(const char *value);
#ifdef __cplusplus
}
#endif
#endif
swift_binding.cc
#include "swift_binding.h"
#include <cstdlib>
#include <cstring>
double uasm_test_add(double a, double b) { return a + b; }
const char *uasm_test_concat(const char *a, const char *b) {
if (!a || !b) return nullptr;
const size_t a_size = std::strlen(a), b_size = std::strlen(b);
char *result = static_cast<char *>(std::malloc(a_size + b_size + 1));
if (!result) return nullptr;
std::memcpy(result, a, a_size);
std::memcpy(result + a_size, b, b_size + 1);
return result;
}
void uasm_test_free_string(const char *value) { std::free(const_cast<char *>(value)); }
// uni_modules/test-uasm/utssdk/app-ios/TestUasm.swift
import Darwin
import Foundation
private typealias AddFunction = @convention(c) (Double, Double) -> Double
private typealias ConcatFunction = @convention(c) (UnsafePointer<CChar>?, UnsafePointer<CChar>?) -> UnsafePointer<CChar>?
private typealias FreeStringFunction = @convention(c) (UnsafePointer<CChar>?) -> Void
private final class TestUasmAPI {
let add: AddFunction
let concat: ConcatFunction
let freeString: FreeStringFunction
private let handle: UnsafeMutableRawPointer
private init(handle: UnsafeMutableRawPointer) throws {
self.handle = handle
self.add = try Self.symbol("uasm_test_add", from: handle, as: AddFunction.self)
self.concat = try Self.symbol("uasm_test_concat", from: handle, as: ConcatFunction.self)
self.freeString = try Self.symbol("uasm_test_free_string", from: handle, as: FreeStringFunction.self)
}
static let shared: TestUasmAPI = {
let path = Bundle.main.privateFrameworksURL?.appendingPathComponent("UasmTestUasm.framework/UasmTestUasm").path ?? ""
guard let handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL) else { fatalError("unable to load UasmTestUasm.framework") }
do { return try TestUasmAPI(handle: handle) } catch { fatalError("unable to load UasmTestUasm symbols") }
}()
private static func symbol<T>(_ name: String, from handle: UnsafeMutableRawPointer, as type: T.Type) throws -> T {
guard let value = dlsym(handle, name) else { throw NSError(domain: "TestUasm", code: 1) }
return unsafeBitCast(value, to: type)
}
}
// uni.loadUasm()` / `uni.loadUasmSync() 返回类型 模块名的驼峰格式,首字母大写
public enum TestUasm {
public static func add(_ a: Double, _ b: Double) -> Double {
TestUasmAPI.shared.add(a, b)
}
public static func concat(_ a: String, _ b: String) -> String {
a.utf8CString.withUnsafeBufferPointer { aBuffer in
b.utf8CString.withUnsafeBufferPointer { bBuffer in
guard let value = TestUasmAPI.shared.concat(aBuffer.baseAddress, bBuffer.baseAddress) else {
return a + b
}
defer { TestUasmAPI.shared.freeString(value) }
return String(cString: value)
}
}
}
}
xcframework 不参与主程序的链接,当用户调用 uni.loadUasm() / uni.loadUasmSync() 时自动加载
// 仅展示需要新增的配置
{
'targets': [
{
'conditions': [
['OS == "ios"', {
'sources': [
'swift_binding.cc',
'swift_binding.h',
],
'mac_framework_headers': [
'swift_binding.h',
],
'ldflags': [
'-Wl,-exported_symbols_list,<(module-root-dir)/ios.exports',
],
}],
]
}
]
}
ios.exports文件内容
_napi_register_module_v1
_node_api_module_get_api_version_v1
_uasm_test_add
_uasm_test_concat
_uasm_test_free_string
uni_modules/test-uasm/utssdk/app-harmony/TestUasm.ets// 调用 uni.loadUasmSync("uni_modules/test-uasm") 后返回该类型
export interface TestUasm {
add(a: number, b: number): number
concat(a: string, b: string): string
}
uni_modules/test-uasm/utssdk/app-harmony/index.utsfunction testInvoke() {
try {
const libTestUasm = uni.loadUasmSync("uni_modules/test-uasm")
libTestUasm.add(1, 2) // 3
libTestUasm.concat("uni", "-app") // uni-app
} catch (e) {
}
}
示例如下:
{
'variables': {
"uni-gyp-root-dir%": "<!(node -p \"require('uni-gyp').root_dir\")",
'module-root-dir%': '<!(node -p "require(\'path\').resolve(\'.\')")',
},
'targets': [
{
'conditions': [
['OS == "android"', {
'ldflags': [
'-Wl,--unresolved-symbols=ignore-all',
# The app supplies libc++_shared.so from its default native lib
# directory; prevent the toolchain from adding libc++ statically.
'-nostdlib++',
],
'libraries': [
'-lc++_shared',
],
}],
['OS == "ios"', {
'mac_bundle': 1,
'xcode_settings': {
'PRODUCT_BUNDLE_IDENTIFIER': 'uts.sdk.modules.testUasm',
},
'ldflags': [
'-Wl,-undefined,dynamic_lookup',
'-Wl,-exported_symbols_list,<(module-root-dir)/ios.exports',
],
}],
['OS == "harmony"', {
'libraries': [
'-lace_napi.z',
],
"ldflags": [
"-Wl,--unresolved-symbols=ignore-all",
"-Wl,--version-script=<(uni-gyp-root-dir)/harmony.exports",
],
}],
]
}
]
}
平台自定义导出见uni-gyp使用教程
{
"scripts": {
"build:test-uasm:android:release": "uni-gyp -platform=android --depth=. -arch=arm64 --build=Release binding.gyp && uni-gyp -platform=android --depth=. -arch=x64 --build=Release binding.gyp",
"build:test-uasm:harmony:release": "uni-gyp -platform=harmony --depth=. -arch=arm64 --build=Release binding.gyp && uni-gyp -platform=harmony --depth=. -arch=x64 --build=Release binding.gyp",
"build:test-uasm:ios:release": "uni-gyp -platform=ios --depth=. -ios_sdk=all -arch=arm64,x64 --build=Release binding.gyp",
"build:test-uasm:pack:uni-module:release": "uni-gyp module-pack --target test-uasm",
"build:test-uasm:all": "npm run build:test-uasm:android:release && npm run build:test-uasm:harmony:release && npm run build:test-uasm:ios:release && npm run build:test-uasm:pack:uni-module:release",
}
}
uni-gyp教程,另见uni-gyp使用教程
如何保障各平台封装的调用一致性?
因node api运行时存在差异,harmony平台的node api运行时由鸿蒙系统提供,虽然使用了node api规范,在参数支持并没有和node api对齐
差异点如下: