拥抱Android:编译nodejs搭建移动平台 - Go语言中文社区

拥抱Android:编译nodejs搭建移动平台


 

Android编译系列篇:

1 - Python

2 - NodeJS

3 - Nginx

4 - MariaDB

 

编译好的版本 android (>6), (arm) v8.12.0, v10.10.0, (arm64) v14.15.4: https://github.com/dna2github/dna2oslab/releases

可用的编译脚本支持 v0.12.6 和 v14.15.4

 

疯魔:编译新的node

最近发现编译node的脚本都不work了,所以重新test。重整以后,让ndk先生成一套toolchain再编译node,发现node的android_config越来越方便了,可是用了以后才是更深的坑;我们就来编译最新的v13代码;以前torque子系统需要先编译一遍node的host版本再生成一些代码,然后再编译target,现在node帮我们整合了。然后我编!我去,我的centos gcc才几,你要——6.3?于是直接写了脚本自动在host上编译gcc 9.3;编译好以后,继续整。torque不能运行?改LD_LIBRARY_PATH……然后是seekdir和telldir;啥?android没有实现,google一下;好的,android代码里有人已经有hack的compat了,复制过来…兴高采烈,编译出了新鲜出炉的node,拿到手机上跑,很好,挂了… `__emutls_get_address` 是蛇马玩意?libgcc?toolchain里有,我加还不行么;加完,哦了,编译成功,运行成功。好了,后面慢慢整之前的版本…

渐渐的,node也开始完善了cross compile,最近又编译了一遍node的最新稳定版本,坑基本已经消除了……只要配置好各种cc和cxx,靠时间完成所有了。编译后的大小也从50MB降到了30MB;所以,开心得使用吧。

 

优化:如何使用编译的node

原来一直使用terminal运行node,然后想用node运行app的人越来越多,那就开发一个android app吧。

https://github.com/dna2github/NodeBase

 

Github的链接上,nodeBase有两个部分,platform就是apk的部分,是android app的node平台,把编译好的node文件放到指定位置,然后编译成apk就能在android上跑了。

另一个部分是modules,就是各种各样的node apps。现在暂时只放上去了文件交换的app,就是用platform运行express app,这个app可以支持指定文件的下载,还使用multer支持其他设备上传文件到android,用浏览器访问就好了。

那么再写个文件编辑器吧,这样在哪都可以coding了。

前进:nodeJS各项扩展

这几天在想怎么把SQLite 19万行的代码不用编译转成纯javascript,google一下,已经有人做好了!

https://github.com/kripken/emscripten

而且这个project将很多native的项目转化成纯jiavascript,比如unity游戏引擎,qemu虚拟机模拟器等!

这样nodejs的使用又可以得心应手很多。继续将javascript部署在手机上当server吧!

J.Y.Liu

2016.11.20

 

优化:NodeJS和机器学习

Wow! TensorflowJS 来啦!把你的Android变成移动智能平台吧!可是最近发现TensorflowJS在手机上运行不正常(手机访问 https://storage.googleapis.com/tfjs-examples/mobilenet/dist/index.html 分类结果和Desktop版的Chrome相差太多),于是转mxnet.js。我们一起让Android变成机器学习的平台吧。mxnet.js是LLVM的IR直接转过来的,所以运行的时候速度很慢,但是至少正确输出,我是800块的白菜价Android,处理一张图片要30s左右(squeezenet-model.json)。

https://github.com/dna2github/NodeBase/tree/master/modules/mxnet

只要把图片文件放在images文件夹,然后运行mxnet的nodejs app,我们访问 http://127.0.0.1:9090/test/<图片名> 就可以分类这个图片了。比如将

https://storage.googleapis.com/tfjs-examples/mobilenet/dist/ed21e89820228ec168bfdf72fb128449.jpg

这张猫的图片下载到images/cat.jpg,然后 http://127.0.0.1:9090/test/cat.jpg 就可以输出为:

[1]: n02124075 Egyptian cat, PROB=78.84891629219055%
[2]: n02123045 tabby, tabby cat, PROB=12.727376818656921%
[3]: n02123597 Siamese cat, Siamese, PROB=7.1867793798446655%
[4]: n02119022 red fox, Vulpes vulpes, PROB=0.2718033967539668%
[5]: n04074963 remote control, remote, PROB=0.2662230748683214%
[6]: n02123159 tiger cat, PROB=0.11871062451973557%
[7]: n02127052 lynx, catamount, PROB=0.08755343733355403%
[8]: n02104365 schipperke, PROB=0.059607491130009294%
[9]: n02109961 Eskimo dog, husky, PROB=0.04351081443019211%
[10]: n02971356 carton, PROB=0.035816358285956085%

剩下的就是mark一下微软的模型转换工具:https://github.com/Microsoft/MMdnn 将Tensorflow的模型转化成mxnet的,然后用mxnet.js提供的json化工具打包模型参数,在手机上运行神奇的机器学习算法吧。

J.Y.Liu

2018.05.18

 

陪伴:node-v0.12之后

node到0.12,可以说在编译上改进了很多,尤其是加了arch的选择,可以告诉configure要在android使用node。一些烦人的问题都没有了,也不需要自己再去写代码让npm能够连上网络。node自从分家到再合并,版本号瞬间就上去了,v4和v6依旧在编译上大同小异;

到了node 7.1.0,v8已经有了不少更新;最近有人报了bug:https://github.com/nodejs/node/issues/9707

因为node的依赖v8在cross-copmile上出现了和python相似的情形:python要在host上编译pgen去生成一个code文件,v8要用host版本mkpeephole生成一个table。

my solution is:
1. compile node-v7.x on your host machine with `./configure && make | grep mkpeephole | grep table.cc`
2. copy out `cp out/Release/obj.target/v8_base/geni/bytecode-peephole-table.cc /tmp/table.cc`
3. modify out/deps/v8/src/v8_base.target.mk to skip run mkpeephole: `sed -i 's|"$(builddir)/mkpeephole"|echo|' $MEDIR/../$ME/out/deps/v8/src/v8_base.target.mk`
4. compile node-v7.x on your host machine with cross-compile environment; `mkdir -p out/Release/obj.target/v8_base/geni && cp /tmp/table.cc out/Release/obj.target/v8_base/geni/bytecode-peephole-table.cc`
 

J.Y.Liu

2016.11.20

继V8的mkpeephole要编译为host版然后生成一个应该是平台无关的代码文件,这样会让交叉编译break;后来V8改进了这块,移除了编译时需要mkpeephole;一时间还是在node交叉编译上还是很开心。而到了node version 10,引用的V8引擎里,使用了torque;它也是要生成一个host机器上运行的程序去生成代码,而node Makefile里却编译成target上运行的binary,扎心了。没法子,从头到尾把node的编译在host上运行一遍生成host可以运行的node,这样那些文件也被生成好了,复制出来。接着把Makefile里调用torque的那行echo掉,node version 10于是就可以正常编译了。

J.Y.Liu

2018.09.23

 

初遇:node-v0.10

有了python,可是感觉库不全,于是就想想,要不编译下nodejs?

 

虽然github上已经有人编译了,(https://github.com/InstantWebP2P/node-android)可是用的是Rhino JS engine,我想要v8的…

下面就和大家一起进入nodejs的Android编译,本文target是node-v0.10。

(编译脚本整理在github: https://github.com/dna2github/dna2oslab/tree/master/android/build 可以找到node-v0.12.6, v4.4.4, v6.5.0, v7.1.0, buildv2 上有v14.15.4)

Android 运行nodejsNPM安装Grunt

nodejs的关联三方库很到位,下载nodejs的source,c-ares,v8,openssl和zlib都包含了,很开心。

这里先列出注意事项:

  • 第一个要说的是./configure的时候一定要加--without-snapshot,不然之后snapshot生成还是很麻烦的。我的教训是开始没有加这个参数,直接把Makefile里的snapshot部分注释掉,结果编译出来的nodejs在Android上segment fault了。
  • Android的network头不全,所以编译当头棒就是nameser_compat.h,怎么办?随便找个代替吧,我直接把苹果官网的拽过来了:http://www.opensource.apple.com/source/Libc/Libc-825.40.1/include/arpa/nameser_compat.h。建个arpa文件夹一起放到cares的include里,然后可以顺利编译完c-ares。
  • 下面就是libuv,里面的uv_barrier_t比较乱,一直报pthread_barrier_t找不到,看看Android libc.so和libc.a,确实pthread没有把pthread barrier一簇函数类型编进去。当时第一反应就是自己写一个,写完了发现nodejs竟然自己已经实现了,但是包裹在另一个次元(就是如果定义了__APPLE__宏才会包含)。所以这里要选择性的去掉#if,把uv_barrier_t以及相关的init, wait, destroy全部编译出来(修改deps/uv/src/unix/thread.c, deps/uv/include/uv-unix.h)。
  • 后面就是uv__recvmmsg和uv__utimesat在deps/uv/src/unix/linux-syscalls.c和.h里重复定义了,把.h里的干掉就好了。
  • 接着把src/node.cc里和uid与gid相关的函数全部干掉,比如getpwuid,直接返回NULL之类就好了。之前还有些小break,比如IOV_MAX直接改1024啦什么的,编译的时候一点一点改,都不是问题。
  • 最后就是比较变态的获得网络interface,一开始看看,觉得直接返回ENOENT,不实现好了,结果编译出来nodejs不能用npm下载包。好吧android里又没有ifaddrs.h,怎么办呢?突然想到了busybox,里面的ifconfig是工作的,在Android上也是,就把busybox-1.21.1/networking/interface.c拿出来改改,好了npm最后也工作了。
uv_err_t uv_interface_addresses(uv_interface_address_t** addresses,
  int* count) {
  int numreqs = 30;
  struct ifconf ifc;
  struct ifreq *ifr;
  int n, err = -1;
  int skfd;
  uv_interface_address_t *address;

  ifc.ifc_buf = NULL;

  skfd = socket(AF_INET, SOCK_DGRAM, 0);
  if (skfd < 0) {
        return uv__new_sys_error(-1);
  }

  for (;;) {
    ifc.ifc_len = sizeof(struct ifreq) * numreqs;
    ifc.ifc_buf = realloc(ifc.ifc_buf, ifc.ifc_len);

    if (ioctl(skfd, 0x8912 /*SIOCGIFCONF*/, &ifc) < 0) {
      goto out;
    }
    if (ifc.ifc_len == (int)(sizeof(struct ifreq) * numreqs)) {
      /* assume it overflowed and try again */
      numreqs += 10;
      continue;
    }
    break;
  }

  *count = ifc.ifc_len/sizeof(struct ifreq);
  *addresses = (uv_interface_address_t*)
    malloc(*count * sizeof(uv_interface_address_t));
  if (!(*addresses)) {
    return uv__new_artificial_error(UV_ENOMEM);
  }

  address = *addresses;
  ifr = ifc.ifc_req;
  for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
    // XXX: add exception handler
    ioctl(skfd, 0x8915 /*SIOCGIFADDR*/, &ifr);
    address->name = strdup(ifr->ifr_name);
    if (ifr->ifr_addr.sa_family == AF_INET6) {
      address->address.address6 = *((struct sockaddr_in6 *)&ifr->ifr_addr);
    } else {
      address->address.address4 = *((struct sockaddr_in *)&ifr->ifr_addr);
    }
    ifr++;
    address++;
  }
  err = 0;

out:
  close(skfd);
  free(ifc.ifc_buf);
  if (err == 0) return uv_ok_;
  return uv__new_sys_error(err);
}

欢迎交流指导,微信:

wechat

J.Y.Liu

2014.12.08

 

 

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/prog_6103/article/details/41809677
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-05-15 20:39:54
  • 阅读 ( 1000 )
  • 分类:前端

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢