在 OpenHarmony 开发板上运行 WasmEdge 原创 精华
@toc
作者:翁纯仪
在 OpenHarmony OS 上运行 WasmEdge,能够改善开发者的开体验。
Why
移动与 IoT 设备的特点是资源受限,软硬件不统一,用户体验却要求很高。设备要能安全,跨平台地运行第三方开发者提供的软件应用(例如,应用商店),因而直接原生编译的软件应用(Native Client, or NaCl)并不主流。鸿蒙与安卓这样的主流操作系统一般提供基于 Java 或者 JavaScript 的软件执行沙盒,来支持第三方应用。但是这样的软件执行沙盒有几个大问题:
- 支持的编程语言很有限
- 支持的语言有 IP 与法律风险
- 性能一般
- 资源开销大
- 无法支持实时系统
- 安全性一般(所以应用商店需要审查制)
而 WebAssembly 作为一个多语言,跨平台,高性能,轻量级,安全的软件执行环境,能让开发者兼得性能,可移植性,与安全性。WebAssembly 是移动与 IoT 设备上系统中间件的最佳选择。
WasmEdge 是由 CNCF 托管的轻量级、高性能和可扩展的 WebAssembly runtime,适用于云原生、边缘和去中心化应用程序。WasmEdge 可以运行 C/C++、Rust、Swift、AssemblyScript 或 Kotlin 等语言编译的标准 WebAssembly 字节码程序。
OpenHarmony 是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目,目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促进万物互联产业的繁荣发展。
WasmEdge 为 OpenHarmony 提供了一个与 JVM 与 JS engine 同级的 runtime,但是比 JVM、JS engine 更安全、更快、更小、更易于管理。通过 WasmEdge,可以在设备上安全地运行第三方开发者用 C、C++、Rust 等语言编写的 Wasm 程序,扩大 OpenHarmony 的开发者群体。 WasmEdge 相当于 OpenHarmony 的一个完全开源的开发执行环境。社区开发者可以方便地运行编译好的 WebAssembly 程序,降低门槛。
WasmEdge 目前已经支持了 Linux、macOS、Windows 与 实时操作系统 seL4。添加 OpenHarmony 的支持,将丰富 WasmEdge 的生态。
介绍完毕 ,下面进入编程时间。请参考下面的教程从源码在 OpenHarmony 开发板中构建和测试 WasmEdge。
1. 全量编译 OpenHarmony OS
2. 获取 WasmEdge 源码
3. 修改 OpenHarmony 标准系统配置文件
4. 构建 WasmEdge 与 OpenHarmony
5. 烧录到开发板
6. 运行 WasmEdge 提供的测试用例
配合视频观看,效果更佳。
环境准备
OpenHarmony 标准系统
OpenHarmony 标准系统为开发者提供的 Docker 环境封装了对应的编译工具链,本文档主要介绍在 Docker 环境下构建 WasmEdge 的步骤.
OpenHarmony 源码的获取与编译可以参考 Open Harmony 提供的文档 搭建Ubuntu环境-Docker方式。
请注意,在构建 WasmEdge 前需要将 Openharmony 进行一次全量编译以便后续 WasmEdge 的交叉编译过程.
# 获取到 docker 镜像后
$ docker run -it -v $(pwd):/home/openharmony openharmony-docker-standard:0.0.5
$ ./build.sh --product-name Hi3516DV300
获取 WasmEdge 源码
OpenHarmony 将第三方库项目放在了 third_party 文件夹下,因此我们需要在 third_party 文件夹下获取 WasmEdge 源码。
这之后,用户可以根据需要更改路径并修改相关配置文件中的路径。
$ docker run -it -v $(pwd):/home/openharmony openharmony-docker-standard:0.0.5
$ cd third_party
$ git clone https://github.com/WasmEdge/WasmEdge.git
$ cd WasmEdge
修改 OpenHarmony 标准系统配置文件
添加 WasmEdge 子系统配置
修改 OpenHarmony 的 build 目录下的 subsystem_config.json
文件,如下添加 wasmedge
子系统。
{
...
"wasmedge": {
"path": "third_party/WasmEdge",
"name": "wasmedge"
},
...
}
将组件添加到产品配置中
修改 OpenHarmony 产品配置文件,标准系统对应的配置文件为:productdefine/common/products/Hi3516DV300.json
。
在该配置文件中添加 "wasmedge:wasmedge":{}
,表示该产品会编译并打包 wasmedge 子系统下的 wasmedge
模块到版本中。
{
...
"parts":{
...
"wasmedge:wasmedge":{}
}
}
构建 WasmEdge 与 OpenHarmony
说明
在 OpenHarmony 中构建的 WasmEdge 目前仅支持 wasmedge
,即 wasm 的通用运行时。
wasmedge
可以在解释器模式下执行一个WASM
文件, 也可以执行从 WASM 文件 AOT 预编译产生的机器码二进制格式文件。但目前还不支持在 OpenHarmony 中对 WASM 文件进行 AOT 预编译 。
执行构建脚本
执行 WasmEdge 源码下的 utils/build_for_ohos.sh
命令行脚本,将自动执行以下工作:
- 将
.gn
等 OpenHarmony 需要的构建配置文件移动到 WasmEdge 项目根目录; - 使用 OpenHarmony 的编译工具链进行交叉编译构建 WasmEdge;
- 运行 OpenHarmony 的构建脚本
build.sh
进行全量编译,该步骤将wasmedge
添加进 OpenHarmony OS;
$ docker run -it -v $(pwd):/home/openharmony openharmony-docker-standard:0.0.5
$ cd third_party/WasmEdge/utils/ohos
$ ./build_for_ohos.sh /home/openharmony
当 terminal 显示以下信息时,表明编译完成.
...
post_process
=====build Hi3516DV300 successful.
2021-12-15 03:18:50
++++++++++++++++++++++++++++++++++++++++
检查 wasmedge
是否编译打包进 OpenHarmony OS。
$ cd /home/openharmony/out/ohos-arm-release/packages/phone/system/bin
$ ls
当输出的文件名中存在 wasmedge
时,就表明 WasmEdge 已经成功导入到 OpenHarmony OS。
测试
烧录镜像
将重新编译后的 OpenHarmony 标准系统镜像烧录进开发板,具体见 OpenHarmony 提供的文档 Hi3516DV300 开发板烧录。
运行应用
WasmEdge 在 tools/wasmedge/examples/
文件夹提供了测试样例。在 OpenHarmony 标准系统中,这些样例写入了 system 镜像中,依然可以进行测试。通过串口工具连接上开发板并启动OpenHarmony 标准系统后,我们就可以进行以下的测试。
# cd /system/usr/wasmedge_example
# wasmedge hello.wasm 1 2 3
hello
1
2
3
# wasmedge --reactor add.wasm add 2 2
4
# wasmedge --reactor fibonacci.wasm fib 8
34
# wasmedge --reactor factorial.wasm fac 12
479001600
#
# cd js
# wasmedge --dir .:. qjs.wasm hello.js 1 2 3
Hello 1 2 3
下一步
接下来,你可以参考 WasmEdge Book 在 OpenHarmony 标准系统中使用 WasmEdge Runtime 来运行你自己的 WebAssembly 应用。
移植过程踩过的坑
最后和大家分享一下,在移植 WasmEdge 到 OpenHarmony OS 过程出现的一些问题与值得注意的地方。
交叉编译
cmake 项目进行交叉编译需要配置工具链,官方的交叉编译配置给出了参考,但需要在此基础上细化,如指明 Clang 及 Clang++ 的位置。此外,标准版 sysroot 的路径也有所不同,具体可以参考 WasmEdge 中的配置:
set(TOOLSCHAIN_PATH "${OHOS_DIR_PATH}/prebuilts/clang/ohos/linux-x86_64/llvm")
set(TOOLCHAIN_HOST "${TOOLSCHAIN_PATH}/bin")
set(OHOS_SYSROOT_PATH "${OHOS_DIR_PATH}/out/ohos-arm-release/obj/third_party/musl")
set(CMAKE_SYSROOT ${OHOS_SYSROOT_PATH})
set(CMAKE_CROSSCOMPILING TRUE)
set(CMAKE_SYSTEM_NAME "Generic")
set(CMAKE_CXX_COMPILER_ID Clang)
set(CMAKE_TOOLCHAIN_PREFIX llvm-)
set(LLVM_PATH "${OHOS_DIR_PATH}/prebuilts/clang/ohos/linux-x86_64/llvm")
include_directories(${LLVM_PATH}/include/c++/v1)
include_directories(${OHOS_SYSROOT_PATH}/usr/include/arm-linux-ohosmusl)
link_directories(${OHOS_SYSROOT_PATH}/usr/lib/arm-linux-ohosmusl)
set(TOOLCHAIN_CC "${TOOLCHAIN_HOST}/clang")
set(TOOLCHAIN_CXX "${TOOLCHAIN_HOST}/clang++")
set(CMAKE_C_COMPILER ${TOOLCHAIN_CC})
set(CMAKE_C_FLAGS "--target=arm-linux-ohosmusl -D__clang__ -march=armv7-a -mfloat-abi=softfp -mtune=generic-armv7-a -mfpu=neon -mthumb -w --sysroot=${OHOS_SYSROOT_PATH}")
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_CXX})
set(CMAKE_CXX_FLAGS "--target=arm-linux-ohosmusl -D__clang__ -march=armv7-a -mfloat-abi=softfp -mtune=generic-armv7-a -mfpu=neon -mthumb -w --sysroot=${OHOS_SYSROOT_PATH}")
set(MY_LINK_FLAGS "--target=arm-linux-ohosmusl --sysroot=${OHOS_SYSROOT_PATH}")
set(CMAKE_LINKER clang)
set(CMAKE_CXX_LINKER clang++)
set(CMAKE_C_LINKER clang)
set(CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINKER} ${MY_LINK_FLAGS} <FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINKER} ${MY_LINK_FLAGS} <FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
编译工具链
OpenHarmony OS 使用 gn+ninja
进行编译。对于 cmake 组织编译的项目来说,官方提供的 cmake 项目移植文档给出的案例是基于轻量级系统的,并不完全适用于标准版系统上的移植。要想将项目写入 OpenHarmony OS 编译生成的镜像烧录到开发板上,需要编写 gn 脚本参与进 OpenHarmony OS 的编译过程。
在 WasmEdge 编译过程中,需要使用到 spqlog 项目,构建过程中存在 spdlog 项目拉取及编译生成 sqdlog 静态库的动作,这意味着只将 WasmEdge 相关库的编译过程改写为 gn 脚本是不够的,还需要对 spdlog 的编译过程进行改写,使得工作量急剧增加。
那么对于项目所依赖但并不属于 OpenHarmony OS 中的静态库模块,编译过程中要如何将这一模块引入 OpenHarmony OS 呢?
OpenHarmony OS 提供的 gn 编写模板中有 ohos_copy
,它可以将生成的静态库移至生成的目标文件夹。这样在真正编译需要链接时,就能将这一静态库视为 OpenHarmony OS 的原生模块而不是查无此库。在 WasmEdge 的移植过程中,所执行的编译脚本便是事先进行一遍交叉编译,生成需要 copy 的 spdlog 静态库,然后再执行 OpenHarmony OS 的编译脚本,从而按照项目目录下的 BUILD.gn
内的定义组织编译。
在 WasmEdge 的 BUILD.gn
中,关于 spdlog 静态库的描述如下:
ohos_copy("spdlog"){
sources = [
"$WASMEDGE_ROOT_DIR/build/_deps/spdlog-build/libspdlog.a",
]
outputs = [
target_out_dir + "/lib/libspdlog.a"
]
module_install_name = ""
}
标准 C 库
平时我们常用的标准 C 库是 GNU 发布的 libc 库,而 OpenHarmony 中使用的是 Musl-libc,因此如果需要移植的项目代码中使用了 glibc 的宏变量的代码,那么需要进行修改或者在开头重新定义为 Musl-libc 中的宏变量。
链接项
../../third_party/WasmEdge/lib/system/allocator.cpp:64:40: error: unused variable 'k4G' [-Werror,-Wunused-const-variable]
static inline constexpr const uint64_t k4G = UINT64_C(0x100000000);
^
../../third_party/WasmEdge/lib/system/allocator.cpp:65:40: error: unused variable 'k12G' [-Werror,-Wunused-const-variable]
static inline constexpr const uint64_t k12G = UINT64_C(0x300000000);
^
1 warning and 2 errors generated.
诸如这类报错,在 BUILD.gn
中使用到该源码的模块中添加 cflags
.例如,对上面的报错,可以添加如下的 cflags
:
cflags = [
...
"-Wno-unused-const-variable",
...
]
如果出现下面的 C++ 的链接编译报错,
../../third_party/WasmEdge/lib/host/wasi/inode-linux.cpp:745:3: error: cannot use 'try' with exceptions disabled
try {
^
则添加 cflags_cc
:
# BUILD.gn 使用到该源码的相应模块
{
...
cflags_cc = [
...
"-fexceptions",
...
]
}
移植过程中还有许多诸如此类的编译报错,在此不进行一一列举,有兴趣的朋友欢迎在下面留言,一起讨论。
厉害呀,围观一波
这个好 先收藏了