[NodeJS for Android]完美編譯大全

NodeJS for Android完美編譯大全

完美地編譯了NodeJS for android-{arm,arm64,x86,x64,mipsel},而且提供預編譯版,和做爲持續編譯環境的Docker image。html

  • 完美, 意思是不去掉任何功能(不加--without-...選項),儘可能不修改任何源碼(包括編譯設定文件)。 藉助工具android-gcc-toolchain 實現了這個目標。見Full Build)。這個工具node

    讓人快捷地使用NDK的獨立toolchain作交叉編譯,而且有些奇妙的功能。python

  • 編譯好了的二進制文件(arm,arm64,x86,x64,mipsel構架)能夠直接下載。linux

  • 一個編譯環境用的Docker image osexp2000/build-nodejs-for-android能夠用來按本身的需求編譯. 見 Docker Images.android

由頭

交叉編譯,是個不大不小的土活兒,很無聊,很乾擾正題。c++

一開始我也沒想要搞什麼完美編譯,我只是由於對NDK有怨念, 因此作了個輔助工具android-gcc-toolchain (這裏有簡單介紹), 以便順利地作交叉編譯。因而通常的交叉編譯過程變輕鬆了以後,就凸顯出NodeJS的編譯錯誤了。git

編譯NodeJS for Android,目前都是去掉某些功能,或者修改源碼裏的編譯設定,才能編譯成功,例如:github

  • --without-snapshot (*1)(*2)
  • --openssl-no-asm (*1)
  • --without-intl (*2)
  • --without-inspector (*2)

(*1):NodeJS源碼裏的android-configure 裏用了這個。(*2):這個選項經常被用到。docker

例如在Mac上編譯NodeJS for android-arm64,不去掉任何功能,不修改任何源碼(包括編譯設定文件),這樣的完美編譯方法,竟然沒找到(arm的也是)!shell

複雜之處是:不只使用用Android的編譯器,還有用Host(編譯工做機器例如Mac/Linux)編譯器,幹嗎呢?生成一些Host上運行的臨時的執行文件(mksnapshot,icupkg,genccode...)。 並且,編譯設定環節層次太多(gyp,autoconf,CMake,...),不容易徹底掌控。

就算把gyp所須要的環境變量CC_target...,GYP_DEFINES="host_os=<mac|linux>" 以及通用的CXX_host,CXX_FLAGS等設好並添加一些選項也依然會有某些工程不遵循設定。

我發現最終阻攔完美編譯的有幾個問題,大部分是host這邊編譯時出的問題。

  • ar: 靜態庫生成器ar誤用(用Mac的ar命令處理Android的lib),致使鏈接錯誤。
  • -lrt: 試圖鏈接linux特有的librt但實際Mac沒有,致使鏈接錯誤。
  • -m32: 有的編譯成32bit,有的是64bit,致使鏈接出錯。
  • -lpthread: 忘了加必要的-l某lib的鏈接選項了,例如-lpthread,致使鏈接時報莫名其妙的"DSO missing from command line"錯誤。
  • gnustl(libstdc++) vs libc++: 有的代碼使用更新的libc++的一點東西,例如std::snprintf,這在標準的gnustl裏命名空間不同。

都是源碼裏的錯誤形成的(就是編譯設定文件有錯誤,可是不太好找,每次都要伺候這些很煩)。

碰巧都想出了通用的方法,雖然方法有點黑,可是管用,能夠完美編譯了,因而記錄下來。

開發環境

源碼:

編譯工做機器:

  • Mac: OS X 10.11.5/10.11.6 EI Capitan (64bit), (可選)Xcode 8.0(8A218a)
  • Linux: Ubuntu 16.04 (64bit), (可選)gcc/g++ 5.4.0
  • Windows: Windows Pro 7 (64bit). Docker-Toolbox. 雖然android-gcc-toolchain支持MINGW,可是NodeJS的編譯系統把全部的路徑都混合使用了mix和/,因此致使make失敗

NDK:

輔助工具 tool:

(可選) CCACHE

  • 若是重複的clean&make,那最好安裝CCACHE以便用編譯緩存來加速這種重複編譯.
    • 安裝: brew install ccache on Mac或者sudo apt-get install ccache on Linux
    • export USE_CCACHE=1 告訴android-gcc-toolchain使用CCACHE.
    • 可選: export CCACHE_DIR=some_dir_in_fast_disk(默認是~/.ccache).
    • 可選: 執行一次ccache -M 50G來設定最大緩存大小(默認是5G).

(可選) 輔助工具 build-nodejs-for-android: (就在這個project裏)

  • 近一步簡化了編譯命令. 例如,以下這些命令作了其餘全部章節裏的事,編譯v6.5.0的全部構架(arm,...),限制版和徹底版,放到指定的目錄裏。

    一個命令

    cd node && build-nodejs-for-android v6.5.0

    或者以下組合命令:

    cd node && git checkout v6.5.0
    build-nodejs-for-android arm    -o ../nodejs-6.5.0-android-arm        
    build-nodejs-for-android arm    -o ../nodejs-6.5.0-android-arm-full    --full
    build-nodejs-for-android arm64  -o ../nodejs-6.5.0-android-arm64      
    build-nodejs-for-android arm64  -o ../nodejs-6.5.0-android-arm64-full  --full
    build-nodejs-for-android x86    -o ../nodejs-6.5.0-android-x86        
    build-nodejs-for-android x86    -o ../nodejs-6.5.0-android-x86-full    --full
    build-nodejs-for-android x64    -o ../nodejs-6.5.0-android-x64        
    build-nodejs-for-android x64    -o ../nodejs-6.5.0-android-x64-full    --full
    build-nodejs-for-android mipsel -o ../nodejs-6.5.0-android-mipsel     
    build-nodejs-for-android mipsel -o ../nodejs-6.5.0-android-mipsel-full --full

共同說明

  • 你能夠查看到每一個編譯命令的命令行, 只要是從build-nodejs-for-android或者android-gcc-toolchain裏引起的,直接的或者間接的都行。

    只要export AGCC_VERBOSE=1或者把-v(--verbose)選項加到上述工具。 這裏編譯命令也包括了ar as ranlib ld strip nm。

Limited Build

去掉NodeJS的一些功能(指定--without-snapshot --without-inspector --without-intl)就能夠在Mac/Linux上編譯。

android-gcc-toolchain arm    <<< "./configure --dest-cpu=arm    --dest-os=android --without-snapshot --without-inspector --without-intl && make"
android-gcc-toolchain arm64  <<< "./configure --dest-cpu=arm64  --dest-os=android --without-snapshot --without-inspector --without-intl && make"
android-gcc-toolchain x86    <<< "./configure --dest-cpu=x86    --dest-os=android --without-snapshot --without-inspector --without-intl && make"
android-gcc-toolchain x64    <<< "./configure --dest-cpu=x64    --dest-os=android --without-snapshot --without-inspector --without-intl --openssl-no-asm && make"
android-gcc-toolchain mipsel <<< "./configure --dest-cpu=mipsel --dest-os=android --without-snapshot --without-inspector --without-intl && make"

對於x64: 須要加上--openssl-no-asm,由於openssl的配置裏都沒有支持android-x64.

Full Build

android-gcc-toolchain --host ... -C能夠編譯nodejs,包含全部機能.

這個--host ...Mandatory host compiler rules, 是在$PATH裏超越本機編譯器命令而後加減一點選項。

Full build須要用host(本機)的編譯器生成運行於本機的執行文件,因此須要安裝Xcode(for Mac) 或者gcc/g++(for linux) by sudo apt-get install gcc g++ gcc-multilib g++-multilib

爲了NodeJS 6.6.0, 若是使用低版本(<1.9.1)的android-gcc-toolchain,那得

  • 加上--stl libc++選項給android-gcc-toolchain來切換C++ STL,以便使用C++11 API(例如std::snprintf), 不然它會報錯說std:snprintf not defined
  • export CCACHE_NODIRECT= 來讓CCACHE用更精確的(可是慢一些)得方式來工做,以便檢測出系統include文件的變化。

Full Build on Mac

android-gcc-toolchain arm    --host ar-dual-os,gcc-no-lrt,gcc-m32 -C <<< "./configure --dest-cpu=arm    --dest-os=android && make"
android-gcc-toolchain arm64  --host ar-dual-os,gcc-no-lrt         -C <<< "./configure --dest-cpu=arm64  --dest-os=android && make"
android-gcc-toolchain x86    --host ar-dual-os,gcc-no-lrt,gcc-m32 -C <<< "sed -i.bak 's/cross_compiling = target_arch != host_arch/cross_compiling = True/' configure && ./configure --dest-cpu=x86 --dest-os=android $(grep -o -- --cross-compiling configure) && make"
android-gcc-toolchain x64    --host ar-dual-os,gcc-no-lrt         -C <<< "sed -i.bak 's/cross_compiling = target_arch != host_arch/cross_compiling = True/' configure && ./configure --dest-cpu=x64 --dest-os=android $(grep -o -- --cross-compiling configure) --openssl-no-asm && make"
android-gcc-toolchain mipsel --host ar-dual-os,gcc-no-lrt,gcc-m32 -C <<< "./configure --dest-cpu=mipsel --dest-os=android && make"

sed命令是修改源碼裏configure腳本里的錯誤. Note: 從node.js 7.4.0開始, 這段sed命令能夠去掉,而改爲添加--cross-compiling到configure命令。

Full Build on Linux

android-gcc-toolchain arm    --host gcc-lpthread,gcc-m32 -C <<< "./configure --dest-cpu=arm    --dest-os=android && make"
android-gcc-toolchain arm64  --host gcc-lpthread         -C <<< "./configure --dest-cpu=arm64  --dest-os=android && make"
android-gcc-toolchain x86    --host gcc-lpthread,gcc-m32 -C <<< "sed -i.bak 's/cross_compiling = target_arch != host_arch/cross_compiling = True/' configure && ./configure --dest-cpu=x86 --dest-os=android $(grep -o -- --cross-compiling configure) && make"
android-gcc-toolchain x64    --host gcc-lpthread         -C <<< "sed -i.bak 's/cross_compiling = target_arch != host_arch/cross_compiling = True/' configure && ./configure --dest-cpu=x64 --dest-os=android $(grep -o -- --cross-compiling configure) --openssl-no-asm && make"
android-gcc-toolchain mipsel --host gcc-lpthread,gcc-m32 -C <<< "./configure --dest-cpu=mipsel --dest-os=android && make"

對於x86:必須先安裝一些32bit的lib:sudo apt-get install -y g++-multilib gcc-multilib, 不然它報錯說sys/cdefs.h找不到。

Full build on Windows

運行以下的docker images.


Docker images

Docker image osexp2000/build-nodejs-for-android包含了一個便於編譯的環境,還有NodeJS 6.5.0, 6.6.0版的預編譯結果

Notes:

  • 進入這個linux容器的話,執行docker run -it osexp2000/build-nodejs-for-android
  • 編譯已經完成了。生成物主要在nodejs-VER-ARCH[-full]/bin(node),lib,include,share,和extras(cctest, openssl-cli...).
  • 最開始是使用了NoeJS v6.5.0, 6.6.0源碼. 在~/node下,是能夠用git管理的,例如:git log -1 --oneline --decorate來確認版本。
  • 能夠在容器裏運行build-nodejs-for-android ...來本身編譯, 未改變的源碼因爲被ccache了因此速度很快。
  • 關於Docker的文件in/out的tips:
    • 可使用卷映射-v HOST_DIR_OR_FILE:CONTAINER_DIR_OR_FILE來把本機的目錄或者文件映射到容器裏。 注意: Docker-Toolbox on Windows要求:host(就是PC機)這邊的目錄或文件必須是爲C:\Users\...之下(例如C:\Users\q\Downloads), 而且HOST_DIR_OR_FILE必須轉換成/c/Users/...形式。另外還須要環境變量MSYS_NO_PATHCONV=1
    • 能夠用docker cp來copy進出容器,這在有時候忘了作卷映射時能夠救急.

在Android運行NodeJS

在實機和模擬器裏測試成功:

  • nodejs-6.5.0-android-arm-full
  • nodejs-6.5.0-android-arm
  • nodejs-6.5.0-android-arm64-full
  • nodejs-6.5.0-android-arm64
  • nodejs-6.6.0-android-arm-full

一些經驗:

安裝到Android裏

以nodejs-6.5.0-arm爲例

adb push /home/devuser/nodejs-6.5.0-arm/bin/node /data/local/tmp/
adb push /home/devuser/nodejs-6.5.0-arm/lib /data/local/tmp/
adb shell chmod -R 755 /data/local/tmp/node /data/local/tmp/lib

NodeJS自己只須要/home/devuser/nodejs-6.5.0-arm/bin/node, lib那個是爲了npm的

運行NodeJS

運行/data/local/tmp/node就好了。可是以前得先export NODE_REPL_HISTORY=/data/local/tmp/node_history, 否則會獲得Error: Could not open history file. REPL session history will not be persisted..

若是是用libc++而不是默認的gnustl(libstdc++)來編譯的, 那還得把libc++_shared.so從NDK複製到android /data/local/tmp/, 而後設定export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/data/local/tmp.

這個 libc++_shared.so在:

  • $NDK/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_shared.so, 這是默認的 .so,爲arm的.
  • /sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so, 這是默認的 .so,爲arm64的.
  • ... please use find $NDK -name libc++_shared.so to find all

運行npm(NodeJS Package Manager)

用這個script代替npm, 而後就能夠用npm install, -g也行.

export HOME=/data/local/tmp
export NODE_REPL_HISTORY=$HOME/node_history
mkdir $HOME/npm-global 2> /dev/null
export NPM_CONFIG_PREFIX=$HOME/npm-global
$HOME/node $HOME/lib/node_modules/npm/bin/npm-cli.js "$@"

###具體的測試內容

  • 多語言支持

    > Buffer.from('新')
    <Buffer e6 96 b0>
  • 網絡和DNS

    > net.connect({host:"nodejs.org", port:443}, () => {console.log("------connected------")})
    ...
    ------connected------
  • SSL

    > https.get('https://encrypted.google.com/', (res) => {res.on('data', (d) => {process.stdout.write(d);});})
    ...
    <!doctype html><html ...>...</html>
  • Debug

    generic_arm64:/data/local/tmp $ ./node debug a.js
    debug> 
    (To exit, press ^C again or type .exit)
    < Debugger listening on [::]:5858
    connecting to 127.0.0.1:5858 ... ok
    break in a.js:1
    > 1 console.log(0)
      2 console.log(1)
    ...
  • npm

    generic_arm64:/data/local/tmp $ ./npm install -g http-server
    /data/local/tmp/npm-global/bin/http-server -> /data/local/tmp/npm-global/lib/node_modules/http-server/bin/http-server
    ...

具體通過的備忘錄

一兩年前

NodeJS對Android支持度很弱,想要Android版的,那就得折騰。那時大體有兩種方法:

  • 各路高中低手都提供了修改源碼裏編譯配置文件的方法。

    如今大都都已經被合併進來了,不提了。

  • Android真機debian Kit裏編譯。

    在一個root過的Android裏裝上debian Kit後,就能夠輕鬆編譯成功。

    /configure --without-snapshot --without-npm
    export LDFLAGS=-static  #靜態鏈接
    make

    美中不足的是,生成的NodeJs竟然連localhost這樣的名字都解析不了,只能直接用IP。 這種編譯方法沒使用android的libc,因此水土不服,例如glibc裏的getaddrinfo 會尋找/etc/resolv.conf之類的東西,而Android實際不用那套機制。

最近

又想隨手搞一個最新的,記得NodeJS有了很多進展的。結果的確有進展,可是依然不簡單。

  • 官網裏的那個linux-arm執行文件是怎麼回事?

    惋惜在Android裏用不了。這大都得怪Android搞了一套和linux不同的linker和.so。

    • loader不同。執行時爆"No such file or directory"

      $ readelf --headers ~/Downloads/node-v4.4.7-linux-armv7l/bin/node | grep interpreter
      [Requesting program interpreter: /lib/ld-linux-armhf.so.3]

      而Android變態不提供這個linux標準loader,而是用/system/bin/linker來調執行文件。

      黑實驗:強行改掉,結果引發下一個問題。

    • Android裏各個.so的名稱都和通常的linux的不同

      $ readelf -d ~/Downloads/node-v4.4.7-linux-armv7l/bin/node
      ... Shared library: [libdl.so.2] [librt.so.1] [libstdc++.so.6] ...

      黑實驗:強行改掉依賴的*.so名稱,結果引發下一個問題。

    • 沒有編譯成PIE風格(像*.so同樣位置無關以便運行位置隨機化),因此會被Android 5+拒絕運行

      $ readelf -d ~/Downloads/node-v4.4.7-linux-armv7l/bin/node | grep Type:
      Type:                              EXEC (Executable file)

      而PIE風格的執行文件,至少得和.so同樣的類型DYN (Shared object file)

      黑實驗:那就在4.4上作實驗,結果引發下一個問題。

    • 有些api在android裏不存在。

      黑實驗:沒法繼續了。更不要提/etc之類的設定文件不存在引發的主機名解析等問題了。

    跑了一下子題,接着試試源碼裏提供的android-configure編譯腳本。

  • 在Mac上執行android-configure,make時出錯

    ./android-configure $NDK && make

    出錯信息:

    sh: /Users/q/Downloads/node/out/Release/icupkg: cannot execute binary file

    並且,不能編譯arm64構架的。

    因而試試源碼裏提供的configure編譯腳本。

  • 在Mac上執行configure,也在make時出錯

    ./configure --dest-cpu=arm64 --dest-os=android && make

    出錯信息 openssl裏"unsupported ARM architecture":

    cc .../obj.target/...openssl...
    ../deps/openssl/openssl/crypto/arm_arch.h:46:6: error: "unsupported ARM architecture"

    obj.target目錄放得都是爲android生成的東西,obj.host裏的都是爲了當前機器生成的東西。

    而這個光禿禿的cc命令就是指host(個人機器)的cc。 顯然這是用錯了cc,得告訴他用android的cc。這個看來不能怪NodeJS。

    那就照慣例定義一下$CC什麼的,部分參照android-configure,生成單獨的toolchain也是必須的,假設生成到/tmp/tc下。

    $ $NDK/build/tools/make_standalone_toolchain.py --arch arm64 --install-dir /tmp/tc --force
    $ export CC=/tmp/tc/bin/aarch64-linux-android-gcc
    $ export CXX=/tmp/tc/bin/aarch64-linux-android-g++
    $ export AR=/tmp/tc/bin/aarch64-linux-android-ar
    $ export LINK=/tmp/tc/bin/aarch64-linux-android-g++
    $ ./configure --dest-cpu=arm64 --dest-os=android && make

    果真那一步經過了,cc變成了制定的android gcc了。

    /tmp/tc/bin/aarch64-linux-android-gcc .../obj.target/...openssl...

    可是接着出別的錯。

折騰之路

那就在Mac上繼續一個個看看怎麼回事,爲何編譯一個東西還要人費神呢?

  • gyp交叉編譯系統更須要$CC_target...而不是$CC...

    新的錯誤和android-configure的那個同樣:

    sh: /Users/q/Downloads/node_tmp/out/Release/icupkg: cannot execute binary file

    看來NodeJS用到icu這個東西,他要生成一個我本機用的執行文件,狀況複雜了啊。

    不但要生成Android的,還要本機用的一些關聯的東西,這就是混合模式了。

    在往一點看,icupkg是由這個命令生成的:

    /tmp/tc/bin/aarch64-linux-android-g++ ... -o ...icupkg .../obj.host/...icu...

    這是發神經了嗎? 拿android-g++編譯obj.host的東西,可icupkg是要在host(也就是個人PC)上執行的啊, 爲何用android-g++編譯呢。

    發如今決定host編譯器時,用的邏輯在make.py:

    GetEnvironFallback(('CXX_host', 'CXX'), 'g++')
    • 優先用$CXX_host
    • 要麼就用$CXX
    • 要麼就用$PATH裏的g++

    看來原來設定的$CXX得換成$CXX_target之類的,因而從新開個bash:

    $ export CC_target=/tmp/tc/bin/aarch64-linux-android-gcc
    $ export CXX_target=/tmp/tc/bin/aarch64-linux-android-g++
    $ export AR_target=/tmp/tc/bin/aarch64-linux-android-ar
    $ export LINK_target=/tmp/tc/bin/aarch64-linux-android-g++
    $ ./configure --dest-cpu=arm64 --dest-os=android && make

    果真,本地編譯的都經過了,包括剛纔的icu模塊。

  • V8(Chromium JavaScript)工程裏的host_os設定錯誤

    新的錯誤是試圖編譯一個linux特有的文件:

    g++ ... -c -o .../obj.host/... ...platform-linux.cc
    .../v8/src/base/platform/platform-linux.cc:53:10: fatal error: 'sys/prctl.h' file not found

    顯然這是在編譯本地產品時誤包括了linux的源碼。

    查到platform-linux.cc是在deps/v8/tools/gyp/v8.gyp#L1769 裏被選定爲本地編譯對象的。

    'conditions': [
      ['host_os=="mac"', {
        'target_conditions': [
          ['_toolset=="host"', {
            'sources': [
              '../../src/base/platform/platform-macos.cc'
            ]
          }, {
            'sources': [
              '../../src/base/platform/platform-linux.cc'
            ]
          }],
        ],
      }, {
        'sources': [
          '../../src/base/platform/platform-linux.cc'
        ]
      }],
    ],

    看起來邏輯正確啊,host_os是mac就platform-macos.cc不然就platform-linux.cc。 強行把上述.cc改爲_xxxx1.cc,_xxxx2.cc,_xxxx3.cc後,從新執行configure,就會發現_xxxx3 出如今一個./out/deps/v8/tools/gyp/v8_libbase.host.mk裏。

    試着把host_os=="mac"換成host=="android"反倒好了。

    肯定是host_os搞錯了,在看這東西是誰設定的: deps/v8/build/toolchain.gypi#L88

    'host_os%': '<(OS)',

    上下看看,明白了OS就是target OS,就是android了。這個host_os在這個文件其餘地方都沒有被用到。

    而另外一個它的使用者deps/v8/build/standalone.gypi#L264

    'host_os%': "<!(uname -s | sed -e 's/Linux/linux/;s/Darwin/mac/')",

    正確的檢測了host_os,結果是"mac". 用這句話替換掉'host_os%': '<(OS)',那麼就OK了。

    可是,這個修改影響較大(例如Windows沒有uname命令,還得換成一段python才行),搞很差就是這麼設計的,讓使用者在外層設定。 反正一時半會兒考慮不全很差提交修改。

    因而一查怎麼把host_os變量從./configure那層傳遞給gyp,發現又轉到了android-configure裏:

    GYP_DEFINES+=" host_os=linux OS=android"
    export GYP_DEFINES
    ./configure ...

    不過顯然,做者當時用的是linux主機作這事兒的,我得設成mac。因而:

    $ export GYP_DEFINES=host_os=mac
    $ export CC_target=/tmp/tc/bin/aarch64-linux-android-gcc
    $ export CXX_target=/tmp/tc/bin/aarch64-linux-android-g++
    $ export AR_target=/tmp/tc/bin/aarch64-linux-android-ar
    $ export LINK_target=/tmp/tc/bin/aarch64-linux-android-g++
    $ ./configure --dest-cpu=arm64 --dest-os=android && make
  • 試圖鏈接linux特有的librt(經-lrt選項)

    新的錯誤是建立本地產品時的linker錯誤

    g++ ... -o ...mksnapshot .../obj.host/... -dl -lrt
    ld: library not found for -lrt

    這是試圖在host上尋找librt,可是若是手動執行不包括-lrt的命令,就會成功。

    這個錯誤一樣是在deps/v8/tools/gyp/v8.gyp#L1769 裏,做者確定是在Linux主機上工做因此不出問題。

    'libraries': [
        '-ldl',
        '-lrt'
    ]

    這個v8.gyp之後確定是要改的,可是先想了個黑辦法對付過去。 那就是把libdl複製成librt,把路徑包含在LIBRARY_PATH裏,讓ld可以找到。

    $ find -L /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs -type f -name 'libdl.*'
    /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib/libdl.tbd
    $ cp /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib/libdl.tbd \
      /tmp/tc/librt.tbd
    $ export LIBRARY_PATH=/tmp/tc:$LIBRARY_PATH

    OK,這一關經過了。

    近一步試探,發現librt.tbd裏只要寫這麼多就好了(包括...三個字符都得原本來本的寫進去)。

    archs:           [ i386, x86_64 ]
    platform:        macosx
    install-name:    /usr/lib/libSystem.B.dylib
    exports:         
      - archs:           [ i386, x86_64 ]
        re-exports:      [  ]
        symbols:         [  ]
    ...

    2016/09/06:這個方法後來換成了gcc-no-lrt這個host compiler rule,超越系統原有的gcc等命令,把-lrt參數給去掉後在調用本來的gcc等。

  • 靜態庫生成器ar誤用

    新的問題就是一開始碰到的那一堆符號找不到的linker錯誤了。例如:

    .../obj.target/cctest/src/inspector_socket.o: In function `inspector_write(inspector_socket_s*, char const*, unsigned long)':
    inspector_socket.cc:(.text+0x1af4): undefined reference to `uv_buf_init'

    往上查這些.o的用處時,發現本機的ar命令竟然用來生成Android的靜態庫了!亂套了。

    rm -f .../obj.target/tools/icu/libicustubdata.a && \
    ar crsT .../obj.target/tools/icu/libicustubdata.a \
            .../obj.target/...一堆.o文件

    看來有有些子工程不聽話啊,壓根不鳥$AR_target所指定的ar。 這查起來就囉嗦了,真不想進那些散發着腐朽味道的工程裏。 在Linux上看不出問題,由於Linux主機上的ar使用的格式和Android上的同樣,而Mac上的略有不一樣。 從Wiki上看,ar命令內部文件格式歷來就沒有統一過。

    怎麼辦?彷佛就差這一步了。突然想起一個黑主意:作個wrapper ar代替本機的,裏面判斷輸入的*.o文件格式, 從而決定調用本機的仍是Android的ar。判斷就用toolchain裏的objdump好了,若是不出錯說明是Android的。

    ...省略,枯燥無味。都集成到android-gcc-toolchain了。

其餘

  • 究竟是啥啊?這個--without-snapshot

    意思是不對js進行預編譯。去掉這個功能彷佛不是什麼大事兒。 不指定這個選項時,make會先生成一個mksnapshot工具,而後運行這個工具生成snapshot.cc, 裏面包含了全部的js的預編譯結果,而後再把這個snapshot.cc編譯成android這邊的機器碼。 這個snapshot彷佛是爲了快速建立新的js context,也許Electron,NW.js之類和UI的時候須要用吧。

  • 究竟是啥啊?這個--without-intl

    意思是不提供Intl機能,一個叫作Intl的全局class。 Intl不是必須的! 別被介紹裏的「Flags that lets you enable i18n features in Node.js」給嚇着了, 搞得好像是沒了他就不支持多語言似的了。

    Intl這個新東西幾乎沒人用,是什麼ECMAScript-402標準裏定義的一個可選項,就是作文字列的排序,日期格式等locale那套功能。

    沒有Intl機能的話,一切都轉的好好的。UTF8,中文,日語什麼的都支持。 無論怎樣,須要轉換文字集時仍是須要iconv-lite等東西。

    NodeJS用icu這個文字集轉換庫實現了intl功能,可是icu常常在交叉編譯是出毛病,由於它要生成一些本地exe,例如genccode,icupkg, 而後調用genccode根據一個數據文件生成C代。

    icu這個東西看起來有種腐朽的味道,看他的網頁就知道了(例如主頁,DEMO,Unicode Browser), 那個土啊,別提了,顯然沒人好好打理。

  • V8(Chromium JavaScript)工程裏的東西好龐大,並且顯得老舊了。很很差參與。

  • gyp那套編譯工具備成功之處,比Makefile好懂。但竟然仍是得一個個加源碼文件?不能"目錄/*.c"外加一些exclude的形式加到sources裏嗎?

  • 在Mac上編譯NodeJS for Android-arm時碰到的錯誤和解決方法

    此次多了一個問題,也是gyp或者別的什麼的錯誤,也是host這邊編譯問題,並且仍是icu那一塊的。

    反正是有的編譯成32bit,有的是64bit。 很差查找。因此煩了,仍是繼續原來的野路子吧,把gcc和g++都給替換了,裏面強制加上-m32選項。

    最終,這一切集成到android-gcc-toolchain裏,經過--host ar-dual-os,gcc-no-lrt,gcc-m32選項能夠實現。

  • 2016/09/02: 在Linux上編譯NodeJS for Android-arm64時碰到的錯誤和解決方法

    此次多了一個問題,又是host這邊編譯問題,是最終鏈接生成mksnapshot時除奇怪的錯:

    /usr/bin/ld: .../obj.host/.../v8/.../platform/condition-variable.o: undefined reference to symbol 'pthread_condattr_setclock@@GLIBC_2.3.3'
    //lib/x86_64-linux-gnu/libpthread.so.0: error adding symbols: DSO missing from command line

    說符號找不到還有DSO什麼莫名其妙的東西,我用nm查了發現符號就在libpthread裏,因此加個-lpthread讓他鏈接libpthread就好了。

    固然,到底在那個配置文件里加這個選項,有得頭痛的層層追尋配置,不想幹了。還不如黑路子快, 最終,把這一切集成到android-gcc-toolchain裏,經過--host gcc-lpthread選項能夠實現。

  • 2016/09/05: 支持ccache這個編譯緩存工具了,重複編譯時速度快了不少。選項--ccache,注意是兩個c。2016/09/22:刪除這個選項了,單純靠USE_CCACHE=1環境變量來表達這個選項。

  • 2016/09/06: 編譯android-mipsel版時,碰到bits/c++config.h找不到之類的錯誤。彷佛之前碰到過查了一下搞好了,可又忘了。得作個memo。

    c++的bits目錄名稱實在操蛋!讓人誤覺得是位操做的東西, 實際裏面放得是c++ STL(template)的頭文件等東西,並且bits是gnu-libstdc++庫裏特有名稱。

    bits目錄出如今三個地方:

    獨立toolchain目錄/include/c++/4.9.x/       #通常的c++的.h文件,可是沒有STL的.h文件。
    獨立toolchain目錄/include/c++/4.9.x/bits/  #STL的.h文件
    獨立toolchain目錄/include/c++/4.9.x/mipsel-linux-android/bits  #子構架關聯的.h文件。

    第一個目錄是c++編譯器默認的包含目錄,天然其下的bits/xxx能夠用include <bits/xxx>。可是第三個目錄就不行了。

    通常狀況下寫個文件a.cc

    #include <functional>  #這個裏面#include <bits/c++config.h>了。

    而後調用toolchain裏的g++ -c -o a.o a.cc是好好的,會自動子構架目錄裏的c++config.h。 用g++ -E a.cc能夠看到c++config.h從哪裏來的。

    V8裏默認添加了一個-mips32r2給g++,表示生成CPU爲mips32r2的代碼,

    結果g++不幹了,報錯說找不到bits/c++config.h。這應該是g++本身的毛病。

    因此在mips構架裏,android-gcc-toolchain把這些文件子構架裏的.h都copy到標準的bits目錄下去了。

  • 2016/09/17: NodeJS 6.6.0出來了,就編了一下,不出意外,Limited build是好的,Full build就出了個"std::snprintf照得不到" 之類的錯誤。換成--stl libc++來編譯就行了。就這麼個破snprintf也沒搞好,C++也真夠亂的。 這個東西是C++11裏明肯定義有的,但是如今用的是gnustl(libstdc++),裏面的<cstdio>裏定義了std::printf都沒有定義std::snprintf, 而<string>裏也沒有包含cstdio,反而包含了一堆拿什麼狗屁bits目錄。反正就不如lbc++裏的清爽。 只不過,據NDK裏說libc++是還不穩定的庫(竟然!,某些case沒經過,arm下有時崩潰),因此,仍是想辦法把gnustl裏的 <cstdio>和<string>給改一下。不過這東西那個該死的GPL3的,...

  • 2016/09/26: 能夠看到每一個編譯命令的命令行。只要加個-v(--verbose)選項給這個工具,或者設定環境變量'export AGCC_VERBOSE=1'。例子:

    $___ ccache '/Users/q/Library/Android/sdk/ndk-bundle/std-toolchains/android-9-arm/bin/arm-linux-androideabi-c++' \
    $___  '-D_GLIBCXX_USE_C99_MATH' \
    $___  '-I../deps/gtest' \
    $___  '-I../deps/gtest/include' \
    $___  '-Wall' \
    $___  '-Wextra' \
    $___  '-Wno-unused-parameter' \
    $___  '-Wno-missing-field-initializers' \
    $___  '-O3' \
    $___  '-fno-omit-frame-pointer' \
    $___  '-fPIE' \
    $___  '-fno-rtti' \
    $___  '-fno-exceptions' \
    $___  '-std=gnu++0x' \
    $___  '-MMD' \
    $___  '-MF' \
    $___  '/Users/q/Downloads/node/out/Release/.deps//Users/q/Downloads/node/out/Release/obj.target/gtest/deps/gtest/src/gtest-filepath.o.d.raw' \
    $___  '-c' \
    $___  '-o' \
    $___  '/Users/q/Downloads/node/out/Release/obj.target/gtest/deps/gtest/src/gtest-filepath.o' \
    $___  '../deps/gtest/src/gtest-filepath.cc'

    這裏的$___純粹爲了grep篩選好用,另外反正$___是空的,就算原封不動copy下來在貼到別的地方執行也不會出錯。

  • 2017/01/10: Node.js v7.4.0開始,configure腳本支持明確的--cross-compiling參數了。

相關文章
相關標籤/搜索