Electron 项目 gyp ERR 之类报错的最终解决方案

1,170 阅读4分钟

「困难」的 electron 安装及打包

安装过程的大坑:gyp ERR!

fatal error: too many errors emitted, stopping now [-ferror-limit=]
warnings and errors generated.
make: *** [Release/obj.target/fse/fsevents.o] Error 
gyp ERR! build error
...

好不容易安装好了,启动的时候来一个这个:

Error: The module '/path/to/native/module.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION $XYZ. This version of Node.js requires
NODE_MODULE_VERSION $ABC. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).

基本上整 Electron 项目的时候多多少少都会遇到上面两个的问题。

gyp ERR 一般上网查就会教你下一大堆乱七八糟的,甚至还有 python3 !你不得不好奇它要 python3 干嘛。

而第二个问题,上网查就会要你开始折腾 electron-rebuild,有时候幸运的你消除了这个报错,有时候则无法解决。

罪魁祸首:Native Node Modules

总所周知 electron-main 可以看作 node ,也可以使用任何 node 的包。

会出现上面两个问题的,实际上都是因为引入了某个包中使用了 native module (也叫 addon),addon 是 node 提供的一个调用动态库(dll dylib)的能力。一般来说这些动态库是 C++ 写的。

然而 dll 是依赖平台的,也就是你在 arm 编译的 dll 后续就只能在 arm 的电脑上用,同时,node 版本不一致会导致 dll 构建时的依赖不一致(即 ABI 不一致)。

结果就是,普通的js包都是纯脚本,也就是都是跨平台的。而包含了 addon 的就只能被某个完全满足 node 版本及机器平台要求的使用。

复杂的生态

为了解决上面说到的问题,基本上开源库都会采用这套解决方案:

  1. 预先构建所有常见版本的 node + 平台的组合,并放在 github 或者什么其他远程位置。通过 package.json 的 post-install hook 去在用户 install 的时候判断并下载预构建产物(.node文件)
  2. (仍然在post-install中)如果确实没有对应的,或者下载不下来,他就会尝试在你机器上直接编译源码

可以预想到这几步全是坑!

  1. 网络不行/被墙:一般来说对应版本是有的,但是网络问题导致下载重试n遍或者下载龟速。
  2. 直接编译:经过第一步一般几分钟十几分钟才报错失败后,第二步进行兜底,这个时候就会发现本机缺少各种 C++ python3 等工具链(node-gyp 需要用...),然后才报错:编译不了!
  3. 莫名其名成功下载了不对应版本,而且还没报错。那么项目启动的时候就会出现报错。
  4. 编译成功了!但是编译的是你命令行里 node 的版本。。。electron 项目启动还是会报错。

(Electron 和 node 是ABI完全不对应,也就是说针对某个node版本构建的 addon 一定不能在 electron 上使用!需要重新构建!)

(可以在 node 官网看到 node version 中间会跳过几个数字,这些其实是给 electron 用的)

Node-API(NAPI)

napi 出现就是为了解决不同版本的 node / electron 需要重复构建的问题。简单来说就是他们出了个新的规范(头文件)去规定 接口的接口,加了一层去抽象 v8.h node.h 频繁变更导致的 addon 依赖也频繁变更。

相应的工具链也进行了升级,到目前为止,大部份都可以只从 平台 + 架构 (darwin + arm) 这种差异来预编译,从而避免在用户本机上编译。

也就是说,我们尽量使上面的第一步成功,就不用担心第二步等带来的麻烦问题了。事实上最近的包好多都不本地编译了。

(一位大佬 github.com/Brooooookly… 就是搞了很多旧方式(nan)迁移到新方式 napi 的工作的)

解决方式

换包,基本上通用的东西,早就有大佬(比如上面说的这个)去把包换成 napi 的实现了。

如果实在没有,可以在成功构建之后把这多个不同平台的 .node 存起来,搞一个私有包(monorepo)跳

PS: 如果想自己整一个 addon 开源库可以参考下我的另一篇文章