量子计算9 发表于 2022-10-1 20:52

在你的ios、android应用中嵌入官方版nodejs是什么感觉?

目前网上找到的ios嵌入nodejs介绍,都是指向nodejs-mobile项目,nodejs-mobile对nodejs项目做了一定魔改,可以预想会难以及时的随nodejs升级,该项目目前的nodejs版本12.19.0,比起官方版本落后太多。而本文介绍的办法只需对nodejs的gyp添加少些修改以支持ios、android的编译,该方式编译的16.16.0版本nodejs已经在真机上测试通过并应用到puerts项目上。而且该修改方式也已经提PR给nodejs官方并合入到主干: libnode for ios app embedding
念念不忘的移动端nodejs支持

尽管我们反复的解释了nodejs是“JavaScript Runtime”之一,如果一个js库用了nodejs专有的api是不能直接在其它“JavaScript Runtime”,比如browser,puerts里运行的。
但奈何nodejs已经事实上约等于js,用puerts的童靴有时候找资料,找到的“如何用ts/js完成XXX”系列文章往往都是nodejs的。很自然的跑过来问puerts为啥不行,不是说支持js么?
其次puerts项目的出发点之一就是把繁荣的js生态引入到游戏开发,如果缺失了nodejs生态,这将是一个重大遗憾。
于是在去年,puerts就尝试在桌面平台支持nodejs脚本后端:《UE引擎里头跑个nodejs服务器是怎样一种体验?》 、【PuerTS】我们把Node.js放进了Unity里(一),但由于移动平台的缺失,我们只是推荐用在做引擎编辑器扩展开发。
不过发现还是有些没有发移动端需求的项目用了nodejs版本,而且反馈对他们开发十分有帮助:网络方面比UE提供的好用,除了界面相关,其它需求都很容易找到相应的nodejs组件。而界面相关又恰好是游戏引擎的强项,所谓强强联合了。于是我对移动端nodejs的支持更期待了,但nodejs并没有移动端的官方支持,特别是ios。
我在网上找移动端nodejs的支持情况,ios只找到nodejs-mobile,它支持nodejs版本远低于我们要求的版本,并不适用,而android下发现官方提供了个android-configure脚本,既然官方提供的,我想应该是能用的吧?但事实上没那么简单,一个接一个的坑,所幸最终还是搞定了,于是实现了puerts对nodejs的双引擎(UE、Unity)×多平台(Window,Mac,Linux,iOS、Android)支持。
具体做了那些修改就不细说了,这些修改都以git patch的形式放到这个项目,可自行查阅:https://github.com/puerts/backend-nodejs
如何嵌入nodejs

相关的资料不多,可以看看我之前两篇相关文章:

[*]《UE引擎里头跑个nodejs服务器是怎样一种体验?》
[*]《c++游戏服务器嵌入v8 js引擎胎教级教程》
未来展望:游戏领域的electron?

nodejs本身除了GUI大多数任务都能完成,于是有人把chrome和nodejs缝合,做成个跨平台的桌面框架electron,十分火爆,这里是它的showcase ,我们程序员耳熟能详的VSCode也赫然此列。
如果把chrome换成专业的游戏引擎:UE、Unity。从2维升级到3维,而且除了桌面平台,还支持移动平台,甚至主机平台,是不是很有想象空间呢?期望有人能把它搞起来。
接下来的章节记录的是探索nodejs移动平台时踩过的坑,可以跳过,直接翻到文章结尾有现成编译好的全平台libnode。如果你个人需要定制什么编译参数,需要自行编译可以再来翻看。
nodejs移动平台支持踩过的坑

一个好开端

我抱着试试看的心态用android-configure编译了一下arm64的libnode.so(用的是puerts当时用的nodejs版本14.16.0),竟然顺利编译成功,习惯了困难模式的我隐隐觉得事情没那么简单。
哪有什么岁月静好

放到Unity版本的puerts在真机上测试,果然失败了,提示libnode.so失败。按经验应该是libnode.so依赖的某些库缺失。 用ndk提供的工具查看依赖
aarch64-linux-android-readelf -d libnode.so

对比下v8版本的puerts的依赖


发现多了个libc++_shared.so,感觉应该是这个导致的,回头查看nodejs的编译选项,发现--partly-static可能可以解决这个问题。编译后libc++_shared.so确实没了,上真机测试果然能正常跑了!!!
64位机器编译arm架构

感觉更难的arm64都编译通过了,arm应该更简单,没想我还是天真了,碰到两个问题

[*]小问题:arm架构的TOOLCHAIN_NAME错了(估计是后面ndk改了,nodejs没同步)
[*]在64位linux下编译,交叉编译有的模块host用64位,有的用32位,链接失败,解决办法:CC_host和CXX_host强制加个-m32。
iOS支持可行性分析

nodejs的最主要部分:V8我们在iOS已经应用了很久(加--jitless选项)。而android-configure的存在,也证明了其它部分在arm架构下运行问题不大。推测iOS嵌入nodejs最大的门槛在编译。
失败的尝试

我尝试参考android的交叉编译做了一版iOS的交叉编译,结果失败了。
其生成的Makefile根本没法使用,我尝试去看gyp的代码,尝试调整Makefile的输入,仍然是失败的。
虽然失败,但也有收获,而且该收获直接导致后面成功:我初步搞懂了gyp是啥,它是一个python写的程序,该程序会根据gyp的配置生成编译工程:window下的vs工程,linux/unix下Makefile等等。gyp配置解析部分是通用的,然后调用一个个generator(msvs.py,android.py,make.py)去生成工程。
ninja是个好东西

iOS(unix系OS)调用的generator是make.py,但make很复杂,我也不熟悉,搞不定。于是把目光投到其它generator,最终锁定ninja。
ninja的规则十分简单(十分钟能学完),有问题比较容易找到并调整,借助它我最终把nodejs的ios交叉编译调通了。
当然,也不是一帆风顺,期间也碰到几个问题:

[*]"multiple rules generate":这是我耗时最久的坑,iOS编译一直报这错误,但android切换到ninja却能很顺利的编译通过,我只能二分查找去缩写iOS和android的ninja文件的差异,最后却发现是ninja版本导致的,homebrew安装的ninja比较新,dupbuild会认为是错误,而ubuntu的apt-get安装的版本只是告警,加个参数可以临时解决这问题
[*]14.16.0的libuv不兼容iOS,可通过少许代码改造通过(升级nodejs版本也可以解决,后面我升级到当时最新的nodejs LTS版本16.16.0)
iOS静态库

苹果的动态库发布很麻烦,需要签名什么的。我们更希望以静态库提供。于是尝试编译静态库。把configure的--shared改为--enable-static即可,编译也很顺利,但链接找不到符号,这些符号位于这两个文件:http://node_snapshot_stub.cc,http://node_code_cache_stub.cc。构建libnode.so会包含,我们把这两也变成静态库链接即可。
这次很顺利,搞定了编译,用unity应用在iOS上测试直接就通过了。
unreal engine碰到的坑

搞定unity,ue上跑还算顺利,只是碰到一个问题:ue和nodejs都用了openssl,但ue的版本老,两种冲突了。表现为:

[*]用动态库版本的nodejs,编译没问题,运行时崩溃,崩在openssl的调用。
[*]静态库版本编译不过,两个版本的openssl的api就不兼容,并存的时候会报符号冲突,去掉其中一个又会报一些符号找不到
最终我先把nodejs的openssl去掉(加--without-ssl选项),如果自行编译ue引擎的童靴也可以选择保留nodejs的openssl,升级ue引擎的openssl。
全平台libnode下载链接

我们已经把libnode的全平台编译做成github action自动化编译:https://github.com/puerts/backend-nodejs ,也有编译好版本,可以直接使用:有带openssl的版本 ,也有不带openssl的版本 。

KaaPexei 发表于 2022-10-1 20:57

很棒,赞一个。
另外想问下,有分别试过android/ios下的空包大小吗?
毕竟在移动端,大小还是很重要的
页: [1]
查看完整版本: 在你的ios、android应用中嵌入官方版nodejs是什么感觉?