擁抱Android:編譯nodejs搭建移動平臺
Android編譯系列篇:
2 - NodeJS
3 - Nginx
4 - MariaDB
可用的編譯指令碼支援 v8.12.0 和 v10.10.0
優化:如何使用編譯的node
原來一直使用terminal執行node,然後想用node執行app的人越來越多,那就開發一個android app吧。
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一下,已經有人做好了!
而且這個project將很多native的專案轉化成純jiavascript,比如unity遊戲引擎,qemu虛擬機器模擬器等!
這樣nodejs的使用又可以得心應手很多。繼續將javascript部署在手機上當server吧!
J.Y.Liu
2016.11.20
優化:NodeJS和機器學習
Wow! TensorflowJS 來啦!把你的Android變成移動智慧平臺吧!可是最近發現TensorflowJS在手機上執行不正常(手機訪問
只要把圖片檔案放在images資料夾,然後執行mxnet的nodejs app,我們訪問 http://127.0.0.1:9090/test/<圖片名> 就可以分類這個圖片了。比如將
這張貓的圖片下載到images/cat.jpg,然後 http://127.0.0.1:9090/test/cat.jpg 就可以輸出為:
|
剩下的就是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的依賴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?
下面就和大家一起進入nodejs的Android編譯,本文target是node-v0.10。
Android 執行nodejs | NPM安裝Grunt |
nodejs的關聯三方庫很到位,下載nodejs的source,c-ares,v8,openssl和zlib都包含了,很開心。
這裡先列出注意事項:
- 第一個要說的是./configure的時候一定要加--without-snapshot,不然之後snapshot生成還是很麻煩的。我的教訓是開始沒有加這個引數,直接把Makefile裡的snapshot部分註釋掉,結果編譯出來的nodejs在Android上segment fault了。
- 下面就是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);
}
J.Y.Liu
2014.12.08