对于nodejs的交叉编译。该文章的目标是编译一套aarch64 Linux Debian嵌入式版本上可以运行的库,本来想在网上找些现成的,然而 交叉编译靠谱的资料也是少的一13,接下来就开始趟坑。老套路,先把linux桌面版搞好,然后 移植到嵌入式Linux Debian 板子上。
1 Nodejs简介
Node.js发布于2009年5月,由Ryan Dahl开发,是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型,让JavaScript 运行在服务端的开发平台,它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。
简单来说就是用来运行js的,基于v8引擎,效率更高,跑的更快。嵌入式Linux上运行js不可或缺的运行时runtime软件之一。
nodejs相关网站信息:
- nodejs官网:Node.js
- nodejs github 项目地址: GitHub NodeJs
2 Linux 本地编译 & 交叉编译
2.1 Linux(ubuntu20.04)上 nodejs的编译
按照文档的指示,关注以下3个关键软件的版本:
* `gcc` and `g++` >= 6.3 or newer, or
* GNU Make 3.81 or newer
* Python (see note above)
* Python 2.7
* Python 3.5, 3.6, 3.7, and 3.8.
在linux上编译比较简单,按照README.md、BUILDING.md文档的说明,只需要执行命令:
$ ./configure
$ make -j4
$ make install
Linux桌面版的编译简单的一他糊涂,但是 嵌入式版的编译难度吗,相比之下,完全不是一个等级阿,坑还是比较多的,不过好在最终上岸。
2.2 使用clang编译nodejs
为了满足某些特殊需求,这使用clang编译而不是默认的gcc/g++编译器来编译,首先需要安装clang编译器体系,这里以clang-14(ubuntu20.04)为例,编辑/etc/apt/source.list文件,追加以下两行内容:
deb http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main
deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main
执行命令集:
#安装前执行update
sudo apt update
#有时候会报key不识别的问题,直接记录key值,然后执行以下指令即可
#sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys [key ID]
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 78BD65473CB3BD13
#install clang14 step process (order,step by step)
# LLVM
sudo apt-get install -y libllvm-14-ocaml-dev libllvm14 llvm-14 llvm-14-dev llvm-14-doc llvm-14-examples llvm-14-runtime
# Clang and co
sudo apt-get install -y clang-14 clang-tools-14 clang-14-doc libclang-common-14-dev libclang-14-dev libclang1-14 clang-format-14 python3-clang-14 clangd-14 clang-tidy-14
# libfuzzer
sudo apt-get install -y libfuzzer-14-dev
# lldb
sudo apt-get install -y lldb-14
# lld (linker)
sudo apt-get install -y lld-14
# libc++
sudo apt-get install -y libc++-14-dev libc++abi-14-dev
# OpenMP
sudo apt-get install -y libomp-14-dev
# libclc
sudo apt-get install -y libclc-14-dev
# libunwind
sudo apt-get install -y libunwind-14-dev
# mlir
sudo apt-get install -y libmlir-14-dev mlir-14-tools
安装好后编译用到的编译工具链需要单独配置,这里就需要一个自定义的configure文件了,在nodejs的目录下创建一个文件clang-configure(和android-configure同级),内容如下:
#!/bin/bash
export CC=clang-14
export CXX=clang++-14
export LD=ld
export AS=llvm-as-14
export AR=llvm-ar-14
export STRIP=llvm-strip-14
export OBJCOPY=llvm-objcopy-14
export OBJDUMP=llvm-obidump-14
export RANLIB=llvm-ranlib-14
export NM=llvm-nm-14
export STRINGS=llvm-strings-14
export READELF=llvm-readelf-14
#execute configure
if [ -f "configure" ]; then
./configure --shared --debug --debug-node --debug-lib
fi
接下来创建一个目录clang(和clang-configure同级),在该目录下构建一个文件build-node-clang.sh脚本,内容如下:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
#MAKE_OPTS="-j8"
cd $DIR
rm -rf dist
mkdir -p dist
#检查编译结果,如果编译失败, 则退出
function CheckBuildResult() {
if [ $? -ne 0 ]; then
exit 1
fi
}
cd ..
# build clang
./clang-configure
echo make start
make $MAKE_OPTS > /dev/null
CheckBuildResult
echo "make end"
rm -rf $DIR/tmp
mkdir $DIR/tmp
./tools/install.py install '' $DIR/tmp
cp -r $DIR/tmp/include $DIR/dist/
mkdir -p $DIR/dist/bin/clang
mkdir -p $DIR/dist/lib/clang
cp $DIR/tmp/lib/libnode.so.83 $DIR/dist/lib/clang
cp $DIR/../out/Release/node $DIR/dist/bin/clang
cd -
到这里,脚本编写的相关工作就结束了,然后给build-node-clang.sh加权限:
$chmod +x build-node-clang.sh
然后在该目录下运行脚本build-node-clang.sh即刻开始编译即可。这里最终是要拿到libnode.so 和一个node二进制文件(用于运行js)
注意:在使用make -j多线程进行编译时有可能会报错,线程数量越少报错概率越低。
2.3 aarch64交叉编译nodejs
对于嵌入式Linux Debian,使用aarch64编译也需要提前安装好相关软件,aarch64相关软件apt安装如下:
sudo apt install binutils-aarch64-linux-gnu-dbg \
binutils-aarch64-linux-gnu cpp-aarch64-linux-gnu gcc-aarch64-linux-gnu \
g++-10-aarch64-linux-gnu g++-9-aarch64-linux-gnu g++-aarch64-linux-gnu g++ \
gcc-10-aarch64-linux-gnu-base gcc-9-aarch64-linux-gnu-base \
pkg-config-aarch64-linux-gnu qemu-efi-aarch64 gcc arch-test
同时编译用到的交叉编译工具链需要单独配置,这里就需要一个自定义的configure文件了,在nodejs的目录下创建一个文件aarch64-configure(和android-configure同级),内容如下:
#!/bin/bash
ARCH="aarch64"
DEST_CPU="arm64"
#linux host env
HOST_OS="linux"
HOST_ARCH="x86_64"
export LINK_host="g++"
export CXX_host="g++"
export CC_host="gcc"
export AR_host="ar"
#cross-compile env
export CC=aarch64-linux-gnu-gcc
export CXX=aarch64-linux-gnu-g++
export LD=aarch64-linux-gnu-ld
export RANLIB=aarch64-linux-gnu-l-ranlib
export AR=aarch64-linux-gnu-l-ar
export AS=aarch64-linux-gnu-l-as
#execute configure
if [ -f "configure" ]; then
./configure \
--dest-cpu=$DEST_CPU \
--dest-os=linux \
--without-snapshot \
--openssl-no-asm \
--cross-compiling \
--shared \
--debug \
--debug-node \
--debug-lib
fi
接下来创建一个目录aarch64(和aarch64-configure同级),在该目录下构建一个文件build-node-aarch64.sh脚本,内容如下:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
MAKE_OPTS="-j24"
cd $DIR
rm -rf dist
mkdir -p dist
cd ..
# build arm64
./aarch64-configure
echo make start
make $MAKE_OPTS > /dev/null
echo "make end"
rm -rf $DIR/tmp
mkdir $DIR/tmp
./tools/install.py install '' $DIR/tmp
cp -r $DIR/tmp/include $DIR/dist/
mkdir -p $DIR/dist/bin/aarch64
mkdir -p $DIR/dist/lib/aarch64
cp $DIR/tmp/lib/libnode.so.83 $DIR/dist/lib/aarch64
cp $DIR/../out/Release/node $DIR/dist/bin/aarch64
cd -
到这里,脚本编写的相关工作就结束了,然后给build-node-aarch64.sh加权限:
$chmod +x build-node-aarch64.sh
然后运行脚本build-node-aarch64.sh即刻开始编译,效果如下:
Node.js configure: Found Python 3.8.10...
WARNING: building --without-snapshot is no longer possible
WARNING: --openssl-no-asm will result in binaries that do not take advantage
of modern CPU cryptographic instructions and will therefore be slower.
Please refer to BUILDING.md
WARNING: warnings were emitted in the configure phase
INFO: configure completed successfully
make start
...
#一堆警告啥的忽略即可
tools/v8_gypfiles/run_torque.target.mk:13: 警告:覆盖关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/source-text-module-tq-csa.cc”的配方
tools/v8_gypfiles/run_torque.host.mk:13: 警告:忽略关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/source-text-module-tq-csa.cc”的旧配方
tools/v8_gypfiles/run_torque.target.mk:13: 警告:覆盖关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/stack-frame-info-tq-csa.cc”的配方
tools/v8_gypfiles/run_torque.host.mk:13: 警告:忽略关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/stack-frame-info-tq-csa.cc”的旧配方
tools/v8_gypfiles/run_torque.target.mk:13: 警告:覆盖关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/string-tq-csa.cc”的配方
tools/v8_gypfiles/run_torque.host.mk:13: 警告:忽略关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/string-tq-csa.cc”的旧配方
tools/v8_gypfiles/run_torque.target.mk:13: 警告:覆盖关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/struct-tq-csa.cc”的配方
tools/v8_gypfiles/run_torque.host.mk:13: 警告:忽略关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/struct-tq-csa.cc”的旧配方
tools/v8_gypfiles/run_torque.target.mk:13: 警告:覆盖关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/synthetic-module-tq-csa.cc”的配方
tools/v8_gypfiles/run_torque.host.mk:13: 警告:忽略关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/synthetic-module-tq-csa.cc”的旧配方
tools/v8_gypfiles/run_torque.target.mk:13: 警告:覆盖关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/template-objects-tq-csa.cc”的配方
tools/v8_gypfiles/run_torque.host.mk:13: 警告:忽略关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/template-objects-tq-csa.cc”的旧配方
tools/v8_gypfiles/run_torque.target.mk:13: 警告:覆盖关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/template-tq-csa.cc”的配方
tools/v8_gypfiles/run_torque.host.mk:13: 警告:忽略关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/objects/template-tq-csa.cc”的旧配方
tools/v8_gypfiles/run_torque.target.mk:13: 警告:覆盖关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/wasm/wasm-objects-tq-csa.cc”的配方
tools/v8_gypfiles/run_torque.host.mk:13: 警告:忽略关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/src/wasm/wasm-objects-tq-csa.cc”的旧配方
tools/v8_gypfiles/run_torque.target.mk:13: 警告:覆盖关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/test/torque/test-torque-tq-csa.cc”的配方
tools/v8_gypfiles/run_torque.host.mk:13: 警告:忽略关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/test/torque/test-torque-tq-csa.cc”的旧配方
tools/v8_gypfiles/run_torque.target.mk:13: 警告:覆盖关于目标“/data/OpenSource/nodejs/out/Release/obj/gen/torque-output-root/torque-generated/../../deps/v8/third_party/v8/builtins/array-sort-tq-csa.cc”的配方
...
这里最终是要拿到libnode.so 和一个node二进制文件(用于运行js)