目前我们新产品的安装包是 x86_64 架构的,在 mac m1/m2 芯片的电脑上运行时,性能、流畅性相比在 intel 芯片 mac 上运行都明显卡顿。所以迫切的需要针对 apple silicon 出包。
ffi 支持 arm64
在 m1 上直接运行 x86_64 的程序,ffi 加载动态库时报错,这个报错是 ffi 库加载本身失败了
load libsscronet.dylib failed ["Error","No native build was found for platform=darwin arch=arm64node=16.15.0 electron=20.1.0
loaded from: node_modules/ref-napi
"]
原因是 ffi-napi 输出的 native 模块缺少 arm64 的版本
ffi-napi 的发布原理是在 travis ci 中使用 prebuildify 构造 native 模块,prebuildify 根据当前运行的平台和架构打出对应架构的 Addons 文件,但是 ff-napi 已经很久没人维护了,ci 貌似也没维护运行。
找到一个 koffi,支持多平台、多架构,用来替换 ffi-napi
koffi 在 webpack 打包时会将它所支持的所有平台的 node 二进制文件都放到包里,每个平台接近 4m 的 node ,会导致安装包体积增加较多,所以需要修改 eletron-builder 的打包配置,只将需要的的平台打包进安装包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// 所有平台的 node 文件夹名
const KoffiNodes = [
'freebsd_arm64',
'freebsd_ia32',
'freebsd_x64',
'linux_arm32hf',
'linux_arm64',
'linux_ia32',
'linux_riscv64hf64',
'linux_x64',
'openbsd_ia32',
'openbsd_x64',
'win32_arm64',
DarwinArm64FFI,
DarwinX64FFI,
Win32FFI,
Win64FFI
]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function appendKoffiNode(nodes) {
return `!node_modules/koffi/build/koffi/{${nodes.join(',')}}`
}
// windows 只保留 win32/win64 对应的文件
const files = [...commFiles].concat(['**/*.ico'])
const nodes = [...KoffiNodes]
nodes.splice(nodes.indexOf(arch == 'x64' ? Win64FFI : Win32FFI), 1)
files.push(appendKoffiNode(nodes))
// mac 只保留 x64/arm64 对应的文件
const files = [...commFiles]
const nodes = [...KoffiNodes]
nodes.splice(nodes.indexOf(arch == 'x64' ? DarwinX64FFI : DarwinArm64FFI), 1)
files.push(appendKoffiNode(nodes))
|
如果打 universal 包,则需要将 darwin_x64 和 darwin_arm64 都打到包里
动态库支持 arm64
ffi 支持了 arm64 之后,在 arm64 机器上加载动态库失败,动态库的架构和机器的架构不匹配
1
2
3
4
5
6
|
load libimcppsdk.dylib failed Error Failed to load shared library: dlopen(libimcppsdk.dylib, 0x0002): tried: 'libimcppsdk.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')), '/System/Volumes/Preboot/Cryptexes/OSlibimcppsdk.dylib' (no such file), 'libimcppsdk.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')) Error: Failed to load shared library: dlopen(libimcppsdk.dylib, 0x0002): tried: 'libimcppsdk.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')), '/System/Volumes/Preboot/Cryptexes/OSlibimcppsdk.dylib' (no such file), 'libimcppsdk.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64'))
at rr (xxx.app/Contents/Resources/app.asar/index.js:1:455903)
at async Object.load (xxx.app/Contents/Resources/app.asar/index.js:1:466442)
at async wr.init (xxx.app/Contents/Resources/app.asar/index.js:1:468301)
at async eo.initIMSDK (xxx.app/Contents/Resources/app.asar/index.js:1:496819)
at async node:electron/js2c/browser_init:241:563
|
universal 打包
universal 打包会先打出 x64 和 arm64 的产物,然后使用 lipo 将两种产物打到一个安装包里。
lipo 在对我们用到的动态库进行合并操作时报错:
have the same architectures (x86_64) and can't be in the same fat output file
失败原因在于,我们打包时只将一个架构的动态库拷贝到了资源目录,没有分架构拷贝,导致 lipo 在最后的合并操作时,发现库1 和 库2 其实对应的是同一个文件,所以不能合并为一个新的文件。
electron-builder 提供了 afterPack hook,可以在打包之后、产生分发包、签名之前执行自定义操作。
1
2
3
4
5
6
7
8
9
|
// afterPack 函数可以拿到的 context
interface AfterPackContext {
outDir: string
appOutDir: string
packager: PlatformPackager<any>
electronPlatformName: string
arch: Arch
targets: Array<Target>
}
|
在 afterPack hook 中,根据 electron-builder 当前正在处理的架构,将对应的动态库拷贝到资源目录中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
const builder = require('electron-builder');
const { Arch, Platform } = builder;
exports.default = async function (context) {
const { appOutDir, arch, packager } = context
const { platform } = packager
if (platform.name == Platform.MAC.name) {
const arm64 = arch == Arch.arm64
const x64 = arch == Arch.x64
if (!arm64 && !x64) {
console.warn('Not arm64 and not x64')
return
}
const dest = path.join(appOutDir, packager.appInfo.productFilename + '.app', 'Contents', 'Resources', 'lib', 'mac')
const source = path.join(__dirname, '..', 'lib', 'mac', arm64 ? 'arm64' : 'x64')
await copy(source, dest)
}
};
|
包分发
采用分架构分发还是 universal 通用包分发
- 分架构分发 x64 和 arm64 和安装包
- 优点:安装包小,下载快一些
- 缺点:需要用户自己选择对应架构下载
- 通用包
- 优点:用户下载时不用自己选择对应架构
- 缺点:安装包文件较大
链接
欢迎体验新产品, 抖音聊天