「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」
公司梭哈了 Protobuf
,为了提效,我们开发了一套完整的工具链,转换工程、PB Mock、各语言筛选工具等等,今天主要来讲讲筛选工具的事情。
转换工程把 .proto
文件转换成各个语言,我们目前支持 java
、go
、js
、swift
、dart
、python
等语言,转换结果保存在各自的结果仓库。
不同的客户端需要用到的 PB
文件不尽相同,全量引用会增加代码体积,对客户端非常不友好,所以需要一个工具来做筛选工作,客户端只需要引入接口使用到的 PB
文件。工具根据一份开发人员维护的 JSON
列表从结果仓库查找指定文件,输出到指定目录下。
第一版筛选工具是用 Node
开发的,使用 pkg
这个包进行可执行文件的打包,功能上满足需求,也在持续优化迭代。最大的问题就是打包后输出的可执行文件实在太大了,足足 45M
,如果上传到 git 显得很笨重,不上传的话每次新拉取项目都要去下载工具,着实有些麻烦。所以再想有没有可以优化的地方。
最近团队组织学习 Rust
,在测试以后发现,编译出来的可执行文件远远小于 Node pkg
的体积,用 Rust
来写新一代工具的想法便浮上心头。
然而就算用 Rust
来写,还是需要下载,还有没有更偷懒一点的方法呢?突然想到 Rust
可以编译成 Node
模块,是不是能整合成一个项目,既可以用在 VSCode
插件项目又能编译出可执行文件,这样使用 VSCode
的小伙伴就不用单独下载了,也能完成更多原来单可执行文件做不到的事情。
Node 模块编译选型
在选择 Rust to Node
模块编译工具的时候,考虑了两个方向:
- WebAssembly
- Node 原生模块
WebAssembly
在测试中使用的是 wasm-pack
这个工具,支持将 Rust
编译为多种 js
引用方式,比如 nodejs
、web
、webpack
等。[详见官网]
生成的 wasm 经测试的确可用,但是因为安全性并不支持系统级别的文件操作,不论 Node
或者浏览器环境都行,所以这个方向放弃。
这边提一下,目前在使用苹果自家芯片(M1、M1X)的 mac
上,用官网的安装方法,或者 npm
直接安装会失败。
唯一成功的方式是使用 cargo
安装,如果用默认的方式也会失败,爬了很多文后才发现,需要将安装目标设置为老版本的Mac OS
,这样 cargo
会认为平台是 x86
,这样编译安装就可以了。
经测试,这样安装的 wasm-pack 编译的结果没有什么问题,也有可能是测试程度较浅,朋友们自行探索。
env MACOSX_DEPLOYMENT_TARGET=10.7 cargo install wasm-pack
Node 原生模块
node_bindgen
安装简单,但是安装后编辑器一直报无法找到该模块,却又能编译出 Node 模块,强迫症不能忍,让了。
Neon
主角来了,它的表现非常好,只需要在 Rust
下加一个连接层,就能实现 Node
和 Rust
都成功调用。直到把它引入 VSCode
插件项目后,噩梦开始了,VSCode
调试的时候一直报 NODE_MODULE_VERSION
要求的是 89
,而引入的 Node
模块是 83
。
经过搜索发现,这个问题的原因是 Electron
没有使用 Node
的 ABI
版本,而是自定义了版本,而VSCode
是基于Electron
开发的。这样就算把本地的 Node
切换到跟 Electron
一致,也编译不出可用的 Node
模块。
这个问题困扰了我很久,后来在一个Neon
官方提供的的 Demo
中,找到了一份环境变量的配置,在执行编译 Node
模块之前,先引入这份环境变量,就可以编译出对应版本的模块了。
export NEON_NODE_ABI=89
export npm_config_target=13.5.1
export npm_config_disturl=https://atom.io/download/electron
# export npm_config_arch=x64 M1/M1X 芯片不需要
# export npm_config_target_arch=x64 M1/M1X 芯片不需要
export npm_config_runtime=electron
export npm_config_build_from_source=true
这样虽然可以使用,但是有个问题,VSCode
如果更新了Electron
版本,支持的ABI
版本发生变化以后,我们编译出来的模块就不能使用了。目前想到的方法有三个:
- 预先构建从 89 - 102(目前最高)的
Node
模块。 - 插件加载的时候根据当前
VSCode
内环境构建对应版本的Node
模块。 VSCode
更新时及时更新插件版本。
第一个方法显然是一个笨方法,插件体积会非常大,非常不可取。
第二个方法需要宿主环境安装有 Rust
,否则无法完成编译。
第三个方法目前开来是最靠谱的方法了,只能期待巨硬更新Electron
版本慢一点了。
项目结构
技术选型暂定以后,我们项目的结构如下:
bb-pb-tools
├─ .eslintrc.json
├─ .gitignore
├─ .vscode
│ ├─ extensions.json
│ ├─ launch.json
│ ├─ settings.json
│ └─ tasks.json
├─ .vscodeignore
├─ CHANGELOG.md
├─ README.md
├─ env.sh
├─ native Rust 目录,Node 模块、Rust 可执行文件的编译在这个目录下进行
│ ├─ Cargo.lock
│ ├─ Cargo.toml
│ ├─ artifacts.json
│ ├─ build.rs
│ ├─ index.node 编译后的 Node 模块,后面考虑输出到其他目录
│ └─ src
│ ├─ lib
│ │ └─ index.rs
│ ├─ lib.rs
│ └─ main.rs
├─ package-lock.json
├─ package.json
├─ src VSCode 插件目录
│ ├─ extension.ts
│ └─ test
│ ├─ runTest.ts
│ └─ suite
│ ├─ extension.test.ts
│ └─ index.ts
├─ tsconfig.json
├─ vsc-extension-quickstart.md
└─ yarn.lock
完整流程
好了朋友们,今天的分享就到这里,潮水马上就要涨上来了。