ArkTS Compile
在这一节中,我会介绍ArkTS
的四大组件以及如何编译并成功运行ArkTS
的前端和运行时。
ArkTS Components
方舟编译器(ArkCompiler)是为支持多种编程语言、多种芯片平台的联合编译、运行而设计的统一编译运行时平台。它支持包括动态类型和静态类型语言在内的多种编程语言,如JS、TS、ArkTS;它是支撑OpenHarmony系统成为打通手机、PC、平板、电视、车机和智能穿戴等多种设备的操作系统的编译运行时底座。
从结构上看,ArkCompiler主要分成两个部分:编译工具链与运行时。
ArkCompiler的编译工具链以ArkTS/TS/JS源码作为输入,将其编译生成为abc(ArkCompiler Bytecode,即方舟字节码)文件。其主要是通过在编译中产生的二进制程序es2abc
进行工作:
1 | es2abc hello.js |
ArkCompiler运行时直接运行字节码文件,实现对应语言规范的语义逻辑。
主要由四个子系统组成:
- Core Subsystem
- Core Subsystem主要由与语言无关的基础运行库组成,包括承载字节码的File组件、支持Debugger的Tooling组件、负责适配系统调用的Base库组件等。
- Execution Subsystem
- Execution Subsystem包含执行字节码的解释器、快速路径内联缓存、以及抓取运行时信息的Profiler。
- Compiler Subsystem
- Compiler Subsystem包含Stub编译器、基于IR的编译优化框架和代码生成器。
- Runtime subsystem
- Runtime Subsystem包含了ArkTS/TS/JS运行相关的模块。
- 内存管理:对象分配器与垃圾回收器(并发标记和部分内存压缩的CMS-GC和Partial-Compressing-GC)
- 分析工具:DFX工具、cpu和heap的profiling工具
- 并发管理:actor并发模型中的abc文件管理器
- 标准库:Ecmascript规范定义的标准库、高效的container容器库与对象模型
- 其他:异步工作队列、TypeScript类型加载、跟C++交互的JSNAPI接口等。
在本次任务中,我们着重关注Compiler Subsystem
中的stub
和assember
。以下给出了具体的一个工作目录结构:
1 | /arkcompiler |
Compile ETS
本次编译过程中的项目组织结构为:
1 | ├── arkcompiler |
Dependency && Compile
在正式开始编译之前,我们首先需要解决依赖配置。目前,我所使用的环境为Ubuntu 22.04 LTS
发行版,
1 | sudo apt-get install git-lfs git bison flex gnupg build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses-dev x11proto-core-dev libx11-dev libc++1 lib32z1-dev ccache libgl1-mesa-dev libxml2-utils xsltproc unzip m4 libtinfo5 bc npm genext2fs liblz4-tool libssl-dev ruby openjdk-8-jre-headless gdb python3-pip libelf-dev libxcursor-dev libxrandr-dev libxinerama-dev |
这里是参考了方舟运行时使用指南的依赖配置。
需要注意的是,在Ubuntu18.04
或Ubuntu20.04
时,可能还存在python
(通常新式的系统都直接支持python3
)。因此需要通过符号链接生成一个python
可执行文件:
1 | ln -s python3 python |
完成这一步后,我们需要下载一些额外的依赖,这一点可以通过项目中的下载脚本自动配置:
1 | toolchain/llvm-project/llvm-build/env_prepare.sh |
下载完毕后,就可以通过build.py
进行自动化构建ArkTS
编译所需要的clang
,在此处,我们需要的x64
和riscv64
架构的构建:
1 | python3 ./toolchain/llvm-project/llvm-build/build.py \ |
我当前的配置为11th Gen Intel i5-11400H (8) @ 2.688GHz
,使用的虚拟机配置为八核16G内存,通常编译clang
的时长在三个半小时左右。编译完成后,需要将产出的clang
拷贝到prebuilts
文件夹中:
1 | mv prebuilts/clang/ohos/linux-x86_64/llvm prebuilts/clang/ohos/linux-x86_64/llvm.origin |
为了构建在riscv64
架构下适配的ArkTS
,我们需要单独构建riscv64
架构的LLVM lib
:
1 | python3 toolchain/llvm-project/llvm-build/build-ohos-riscv64.py |
执行build-ohos-riscv64.py
通常需要半个小时左右,完成后,需要将riscv64
架构的LLVM lib
产出拷贝到prebuilts
目录下:
1 | mkdir -p prebuilts/ark_tools/ark_js_prebuilts/llvm_prebuilts_riscv64/{build,llvm} |
这样,前期准备就完成了。现在,就可以开始执行ark.py
脚本来编译对应架构下的ets_*
工具链了。
编译x86_64
的ArkTS
前端、运行时:
1 | python3 ./arkcompiler/toolchain/build/compile_script/ark.py x64.release --verbose |
编译riscv64
的ArkTS
前端、运行时:
1 | python3 ./arkcompiler/toolchain/build/compile_script/ark.py riscv64.release --verbose |
需要注意的是,在未修改的依赖项中,riscv64
架构下编译不会对ets_frontend
生效。关于如何启用和修改bug
,将在后面进行介绍。
然后你可以看到在out
目录中的一个目录树结构:
1 | out/ |
我们的目标生成文件位于:
1 | out/x64.release/ |
在这里我们也可以发现,riscv64
架构下的arkcompiler
目录中缺少了ets_frontend
的生成。
Dependency Detail && Fix
在上面我们发现,我们实际真正需要的riscv64
的ArkTS
工具链中,ets_frontend
缺失,并且,关于stub
实际上也没有真正编译。在这一小节中,我会对arkcompiler
的GN
依赖进行分析,然后逐步修复错误使得ets_frontend
至少可用。
Dependency Detail
我们是通过ark.py
进行自动化构建的,现在对ark.py
进行溯源,其原始目录位于:arkcompiler/toolchain/build/compile_script/
,然后进入ark.py
中进行解析:
1 | def build_for_gn_target(self, out_path: str, gn_args: list, arg_list: list, log_file_name: str): |
build_for_gn_target
是主要构建函数,通过gn
来进行构建,而gn
会通过同级目录下的.gn
文件进行配置:
1 | # The location of the build configuration file. |
我们暂时不需要关注buildconfig
中的配置,我们主要的构建逻辑位于root
中。并且,在开启riscv64
后,我们需要首先知道这个信息:
1 | current_os=ohos, current_cpu=riscv64 |
首先//root
设置了一个基本的默认构建逻辑,如果host_os
不在MacOS
上运行时,则对于ArkTS
的四个组件都应该构建:
1 | group("default") { |
ets_runtime
然后,我们来看一看ets_runtime
的构建逻辑:
1 | group("ets_runtime") { |
可以看见,在以来中,我们会生成两个可执行文件ark_js_vm
和quick_fix
。我们主要关注ark_js_vm
的生成。在后面我们发现,这里只对于x64
架构的ets_runtime
提供了ark_stub_compiler
提供了构建依赖,而对于riscv64
并没有,因此,我猜测,在后续实现时,我们需要在这里添加对于riscv64-ohos
架构的支持。
但是目前而言,因为riscv stub
的实现暂时有些许问题,因此先忽视。在这里,ets_runtime
都是能够正常编译的。
ets_frontend
现在,我们来看一看ets_frontend
的构建逻辑:
1 | group("ets_frontend") { |
在这里我们看到,ets_frontend
只支持了x64
的完整支持,因此需要做一定的修改:
1 | group("ets_frontend") { |
修改后我们尝试运行:
1 | ERROR at //arkcompiler/toolchain/build/templates/cxx/cxx.gni:182:7: Script returned non-zero exit code. |
就会产生报错,通过查看错误日志后发现,这里是有一个依赖不能够被external_deps_handler.py
处理,该依赖是[hilog:libhilog]
。
通过日志发现,最先出现的调用栈为:arkcompiler/ets_frontend/merge_abc/BUILD.gn:148:5
,因此可以查看:
1 | # Cause Proto -> hilog:libhilog |
可以发现是protobuf_lite_static
依赖出现问题,因此进入arkcompiler/toolchain/build/third_party_gn/protobuf/BUILD.gn:85:1
中进行查看:
1 | ohos_static_library("protobuf_lite_static") { |
可以发现,在ohos_static_library("protobuf_lite_static")
逻辑中,因为这里只是简单的判断current_toolchain != host_toolchain
然后就添加了external_deps = [ "hilog:libhilog" ]
从而导致无法处理[ hilog:libhilog ]
。
因此,我们做出以下修改:
1 | # To Append the `enable_hilog` identifier |
现在再次运行riscv64
的构建脚本:
1 | python3 ./arkcompiler/toolchain/build/compile_script/ark.py riscv64.release --verbose |
runtime_core && toolchain
对于这两个结构而言,没有太多需要注意的地方,只有runtime_core
构建出的两个可执行文件在后续可能会用上ark_asm
和ark_disasm
。
1 | group("runtime_core") { |