开头
说实话,我第一次接到"Ubuntu中交叉编译armadillo库"这个需求时,内心是有点懵的——armadillo不是个header-only的模板库吗?直接拷贝头文件不就行了,为什么要走交叉编译这么麻烦的流程?
后来真正动手做嵌入式ARM平台的项目,才发现这事远没有"拷贝头文件"那么简单。armadillo自身确实是模板库,但它在处理矩阵分解、特征值求解这类高性能运算时,依赖的是BLAS和LAPACK这类底层数值库。你一旦需要armadillo在嵌入式板卡上跑出高性能,就得把这些依赖库也一并交叉编译出来,然后解决工具链、sysroot、链接参数、运行时路径等一系列问题。
这篇文章就基于我在Ubuntu 20.04上为aarch64(ARM64)平台交叉编译armadillo的完整过程来写。适合需要把C++矩阵运算项目部署到ARM Linux设备上的开发者,不管是做机器人、嵌入式视觉,还是边缘计算,都值得参考。我会把为什么这样做的逻辑、每一步的具体操作、以及我在实际过程中踩过的坑都讲清楚,保证你能照着复现。
1. 为什么交叉编译armadillo这件事,比想象中麻烦
1.1 先理解armadillo的真实依赖结构
armadillo是一个C++模板库,接口层面确实只需要包含头文件。但仔细看它的源码就能发现,它有两层架构:上层是模板化的用户接口,下层是"可选的"BLAS/LAPACK后端。
这层"可选"可不是真正可选的。当你的代码里出现求逆、特征值分解、SVD、QR分解这类操作时,armadillo会调用底层BLAS/LAPACK的C接口(比如dgemm_、dgesvd_)。如果编译时没有链接这些库,最常见的结果就是链接阶段报一堆undefined reference to dgemm_之类的错误,或者程序能跑起来但性能差得离谱——armadillo的模板层内置了一套"退化实现",它做矩阵乘法的方式你可能不太想用。
所以交叉编译armadillo,本质上要做的是三件事:第一,把armadillo头文件放到目标平台的include路径下;第二,为目标平台准备好BLAS/LAPACK实现;第三,让你的项目能以正确的交叉编译方式找到这些库并链接成功。
1.2 直接apt安装为什么不行
很多人在Ubuntu上第一次装armadillo,直接sudo apt install libarmadillo-dev就完事了,确实方便。但一旦涉及交叉编译,这个套路立刻失效。
apt默认安装的是为当前宿主机架构(x86_64)编译好的.deb包,里面的头文件虽然能直接用,但自带的CMake配置文件和预编译库都是宿主机的。你拿aarch64的交叉编译器去链接宿主机的libarmadillo.so,链接器第一时间就报错说架构不匹配,librariy的ELF class根本不对。
更麻烦的是,嵌入式目标平台往往没有apt可用,或者系统版本极老、包仓库早就停止维护。你没办法在板子上直接apt install libopenblas-dev。这种时候,从宿主机交叉编译再拷贝过去,几乎是唯一可靠的路。
1.3 哪些项目才需要这么干
如果你的项目只用了armadillo的基础模板运算(比如自己实现的简单迭代算法),那确实可以只拷贝头文件,完全不依赖BLAS/LAPACK。但这种场景其实很少,因为大部分值得用armadillo的项目,看中的就是它背后成熟的BLAS/LAPACK数值能力。
有几种典型场景,我建议老老实实完整交叉编译:
- 嵌入式视觉项目,涉及大量矩阵运算、协方差矩阵求逆、特征值分解;
- 卡尔曼滤波、EKF、状态估计类算法落地到ARM板卡;
- 需要在板子上做实时控制、滤波、信号处理的C++程序。
我这次的项目就是一套需要做批量矩阵运算和最小二乘拟合的嵌入式信号处理程序,跑在aarch64的开发板上,操作系统是厂家给的Linux 5.x内核。这种情况完全没法绕开交叉编译。
2. 动手之前,先把依赖关系和工具链选型理顺
2.1 三层依赖,每一层都得交叉编译
armadillo对底层的依赖,可以拆成三层来看:
| 层级 | 作用 | 是否必须交叉编译 |
|---|---|---|
| armadillo头文件 | 提供C++模板接口 | 需要(纯头文件,可直接拷贝,但建议用CMake安装管理) |
| BLAS实现 | 矩阵乘、矩阵向量乘等基础运算 | 必须,否则性能不可接受 |
| LAPACK实现 | 特征值、SVD、QR、线性方程组求解 | 必须,否则求逆和特征分解无法使用 |
BLAS和LAPACK的范畴经常被人混淆。简单说,BLAS管的是gemm、gemv这类基础线性代数运算,LAPACK在BLAS之上提供更高层的数值分解功能。armadillo两者都要用。
在ARM嵌入式平台上,最常用的BLAS/LAPACK实现是OpenBLAS。它对ARM架构做了大量针对性优化,比如基于NEON指令集的向量化、针对不同ARM核的微架构调优(Cortex-A53/A72/A76都有专门的优化等级)。对比一下通用的netlib BLAS,在ARM上性能差距可以有数倍。既然都已经折腾交叉编译了,没理由不用OpenBLAS。
2.2 交叉编译工具链怎么选:优先级和理由
工具链是整个交叉编译流程的地基,选不好后面全是问题。我强烈建议根据你的目标平台架构来定,主流就是两种:
- aarch64架构(ARM64):使用
aarch64-linux-gnu-g++; - 32位ARM硬浮点(armhf):使用
arm-linux-gnueabihf-g++。
在Ubuntu 20.04下,安装命令分别是:
sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf我这次目标平台是aarch64,所以用的是前者。
但有一个隐藏坑需要提前说明:Ubuntu官方源里这套交叉编译器版本可能跟你板子上的glibc版本不匹配。比如Ubuntu 20.04自带的gcc交叉工具链用的是glibc 2.31,如果你的板子系统较老、glibc版本低于编译器要求的版本,编出来的程序在板子上会报version GLIBC_2.29 not found这类错误。
遇到这种情况有两条路:一是找开发板厂商官方SDK里携带的工具链(通常跟板子系统严格匹配);二是用Linaro提供的工具链。先跑一下aarch64-linux-gnu-gcc --version,再在板子上查ldd --version,两者对不上就先停下来解决问题,别急着开始编译。
2.3 sysroot是什么,没有它寸步难行
所谓sysroot,你可以理解成目标平台根文件系统的一份精简副本,里面包含目标平台的glibc库文件、链接器脚本、系统头文件。交叉编译器拿到sysroot之后才知道目标平台的C标准库长什么样,链接时也才能正确处理动态库依赖。
这里就容易出现一个非常常见的错误:交叉编译时,编译器默认可能尝试用宿主机的/usr/include和/usr/lib,结果编出来的程序要么链接了x86_64的库,要么头文件版本混乱导致各种奇怪的宏错误。
获取sysroot最好的方式,是从开发板的rootfs或者SDK里直接拷贝一份。部分板卡厂商的SDK会直接提供一个完整的rootfs目录,里面有完整的lib、usr/include、usr/lib等目录结构。如果没有现成的,可以在板子上执行这样的命令,把关键目录打包拷回宿主机:
# 在目标板上 tar czf /tmp/sysroot.tar.gz /lib /usr/include /usr/lib然后拷到宿主机解压,放到统一目录比如/opt/aarch64-sysroot。至少要有libc.so.6、ld-linux-aarch64.so.1、libm.so.6这些基本库在:
find /opt/aarch64-sysroot -name "libc.so.6" -o -name "ld-linux-aarch64.so.1"有一个能正常工作的sysroot,后面的所有步骤才有了立足点。
3. 交叉编译OpenBLAS:整个流程的地基
3.1 为什么先编OpenBLAS,而不是直接编armadillo
从纯流程上讲,armadillo的CMake在配置阶段会主动探测BLAS/LAPACK库。如果探测不到,它会退回到自己的内置TinyBLAS实现,或者干脆禁用掉相关功能。为了确保armadillo配置出来能用上高性能后端,我选择先把OpenBLAS编译安装到sysroot里,这样armadillo的CMake探测阶段就能直接找到它。
3.2 OpenBLAS交叉编译的完整命令与参数注解
先拉源码:
git clone https://github.com/xianyi/OpenBLAS.git cd OpenBLAS实际上交叉编译OpenBLAS比想象中简单,它自带对交叉编译的良好支持。关键在编译参数:
make CC=aarch64-linux-gnu-gcc FC=aarch64-linux-gnu-gfortran HOSTCC=gcc TARGET=ARMV8 -j$(nproc) make PREFIX=/opt/aarch64-sysroot/usr/local install这里几个参数需要逐个说清楚:
CC=aarch64-linux-gnu-gcc:指定C编译器为交叉编译器;FC=aarch64-linux-gnu-gfortran:指定Fortran编译器。OpenBLAS里部分代码用Fortran编写,实例代码里LAPACK的某些部分也需要Fortran编译器。如果没装Fortran交叉编译器,先执行sudo apt install gfortran-aarch64-linux-gnu;HOSTCC=gcc:这个参数最容易忽略。OpenBLAS在构建过程中需要在宿主机上生成一些辅助工具(比如代码生成器),这些工具必须用宿主机的gcc编译,否则会编译出aarch64的可执行文件然后在x86_64上运行直接报cannot execute binary file;TARGET=ARMV8:指定目标CPU微架构。这里是强制指定为ARMv8通用架构。如果你明确知道板子CPU型号(比如Cortex-A53),可以改成TARGET=CORTEXA53,性能还能再提升一点。不指定的话,OpenBLAS尝试运行时自动探测,交叉编译环境下探测脚本跑不了,大概率会失败。
编译完成后,确认一下产物架构:
file /opt/aarch64-sysroot/usr/local/lib/libopenblas.a输出里必须有ARM aarch64的字样,看到这个就说明编译架构对了。
3.3 链接器脚本的问题:比想象中隐蔽
OpenBLAS编译完成后,我遇到过一个比较隐蔽的问题:它在lib目录下生成了一些.so链接文件,指向实际的版本号后缀。这些链接文件在交叉编译的sysroot里需要保持有效,拷贝到目标板时也不能漏。
另外,当你的板子系统较老,动态库的.so.0符号链接缺失时,程序运行时会报告找不到libopenblas.so.0。排查时先用aarch64-linux-gnu-readelf -d 你的程序查看依赖列表,再去sysroot里检查对应的库文件是否存在、符号链接是否完整,基本能定位问题。
4. 配置CMake工具链文件:交叉编译armadillo的正式步骤
4.1 环境变量方案的问题
如果你在网上搜索交叉编译的简易方法,可能会看到有人建议直接设CC、CXX环境变量再跑cmake:
export CC=aarch64-linux-gnu-gcc export CXX=aarch64-linux-gnu-g++ cmake ..这种方式在简单项目上确实能跑通,但做交叉编译时隐患很大。最典型的问题在于,CMake对库和头文件的探测逻辑不会因为CXX被改了就去sysroot里找依赖,它仍然倾向于搜索宿主机的/usr/lib、/usr/include。一旦armadillo探测到宿主机的BLAS库,后面编译出来的东西就乱套了。
所以正规起见,必须使用CMake工具链文件。
4.2 我使用的工具链文件全文
在armadillo源码根目录下创建toolchain-aarch64.cmake,内容如下:
set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_SYSROOT /opt/aarch64-sysroot) set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++) set(CMAKE_FIND_ROOT_PATH /opt/aarch64-sysroot) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_CROSSCOMPILING_EMULATOR qemu-aarch64)文件里几个关键点值得展开:
CMAKE_SYSROOT:让编译器把sysroot作为根目录来寻找头文件和库;CMAKE_FIND_ROOT_PATH:告诉CMake的find_package和find_library命令去哪里搜索依赖;CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER:程序搜索不走sysroot。这个很关键,因为CMake在探测过程中需要执行一些辅助程序,这些程序必须是宿主机版本的,去sysroot里找一个aarch64的可执行文件执行会直接失败;CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY:只允许在sysroot里搜索库。这样就不会误抓到宿主机里的liblapack.so;CMAKE_CROSSCOMPILING_EMULATOR qemu-aarch64:如果宿主机装了QEMU,CMake可以在配置阶段用qemu-aarch64跨架构运行目标平台的测试程序。这一步很有用但也可选,前提是sysroot里的动态库能配合QEMU正常工作,通常还需要设置QEMU_LD_PREFIX环境变量指向sysroot。如果没装QEMU,可以先把这一行注释掉,不影响主要的交叉编译。
4.3 armadillo的CMake配置命令与关键选项
下载armadillo源码并解压后,进入源码目录执行:
wget https://sourceforge.net/projects/arma/files/armadillo-12.6.6.tar.xz tar xf armadillo-12.6.6.tar.xz cd armadillo-12.6.6然后创建build目录,避免直接在源码目录下留一堆临时文件:
mkdir build && cd build cmake .. \ -DCMAKE_TOOLCHAIN_FILE=../toolchain-aarch64.cmake \ -DCMAKE_INSTALL_PREFIX=/opt/aarch64-sysroot/usr/local \ -DDETECT_HDF5=OFF这里必须解释两个选项:
DETECT_HDF5=OFF是因为armadillo的CMake配置阶段会探测HDF5库,这个库在大多数嵌入式场景下用不到,而且它的跨平台探测经常会出问题。干脆关掉,省得它干扰整个配置流程。
配置过程中最需要关注的输出,是类似这样的两行:
-- Found BLAS: /opt/aarch64-sysroot/usr/local/lib/libopenblas.a -- Found LAPACK: /opt/aarch64-sysroot/usr/local/lib/libopenblas.a注意看路径,两个必须指向sysroot里的OpenBLAS。如果这两行显示的是/usr/lib/x86_64-linux-gnu/libblas.so,说明工具链文件的FIND_ROOT_PATH没有起效,立刻停下来检查路径配置。此时继续往下编译,armadillo安装出来的配置是错的,后面联调你的项目时还会踩坑。
确认无误后执行编译和安装:
make -j$(nproc) make install来看看安装后的产物:
find /opt/aarch64-sysroot/usr/local/include/armadillo* -maxdepth 0 ls /opt/aarch64-sysroot/usr/local/lib/libarmadillo*armadillo的安装产物主要是一个armadillo头文件目录、一个armadillo_bits目录,以及若干libarmadillo.so相关的库文件。它本质上是把模板实现和一层薄封装打包成库,编译项目时仍然需要头文件路径和库链接信息。
4.4 验证armadillo是否真的找到了OpenBLAS
一个我在实际项目中养成的好习惯是:安装完成后,马上检查生成的libarmadillo.so链接了哪些库:
aarch64-linux-gnu-readelf -d /opt/aarch64-sysroot/usr/local/lib/libarmadillo.so | grep NEEDED正常情况下能看到libopenblas.so在NEEDED列表中。如果只看到libm.so、libgcc_s.so这些基础库而完全没有OpenBLAS,很可能armadillo没探测到LAPACK,退化使用了内部实现。这种问题不会让编译失败,但你的矩阵乘法性能会惨不忍睹。
5. 编写测试程序,验证全套环节跑通
5.1 测试程序应该覆盖哪些功能
交叉编译完成不等于万事大吉。我建议写一个覆盖多类矩阵运算的测试程序,用file、readelf检查架构,有条件的话直接在目标板或QEMU里跑一遍。
测试代码可以这样写,把基础乘法、求逆、特征值分解都覆盖到:
#include <armadillo> #include <iostream> int main() { arma::mat A = arma::randu<arma::mat>(6, 6); // 矩阵乘法,走BLAS的dgemm arma::mat B = A * A.t(); // 求逆,走LAPACK arma::mat C = arma::inv(B); std::cout << "inv(0,0) = " << C(0, 0) << std::endl; // 对称矩阵特征值分解 arma::vec eigval; arma::mat eigvec; arma::eig_sym(eigval, eigvec, B); std::cout << "eigval = " << eigval.t(); // 求解线性方程组 arma::vec x = arma::solve(A, arma::ones<arma::vec>(6)); std::cout << "solve ok, x(0) = " << x(0) << std::endl; return 0; }这几个操作分别覆盖了BLAS层(乘法)和LAPACK层(求逆、特征分解、线性方程组)。如果链接的后端有问题,很容易在其中一个调用上暴露出来。
5.2 手动编译测试程序,理解链接参数
测试程序单独建目录,不放在armadillo源码目录里,避免CMake探测路径出问题。编译命令:
aarch64-linux-gnu-g++ test_arma.cpp \ -I/opt/aarch64-sysroot/usr/local/include \ -L/opt/aarch64-sysroot/usr/local/lib \ -larmadillo -lopenblas \ -o test_arma执行后先看架构:
file test_arma输出必须包含ELF 64-bit LSB executable, ARM aarch64这样的内容。再检查动态库依赖:
aarch64-linux-gnu-readelf -d test_arma | grep NEEDED注意-larmadillo和-lopenblas的顺序。GNU链接器在静态链接场景下对库的顺序非常敏感,被依赖的库要放在依赖它的库后面。虽然这里armadillo主要提供动态库,但为了规范起见保持这个顺序:先-larmadillo,再-lopenblas,能避免不少莫名其妙的undefined reference问题。
5.3 在QEMU里先跑一遍:不用板子也能验证
宿主机装了QEMU用户态模拟器的话,就算板子不在手边也能直接运行aarch64程序:
sudo apt install qemu-user qemu-aarch64 -L /opt/aarch64-sysroot ./test_arma-L指定sysroot路径,QEMU通过它加载aarch64版本的动态链接器和共享库。如果程序能正常跑出特征值结果,说明整条工具链、sysroot、OpenBLAS、armadillo的交叉编译全部正确。
如果QEMU运行时报错invalid ELF header或者FATAL: kernel too old,多半是sysroot里的库版本和QEMU模拟的内核版本不匹配,优先检查sysroot的glibc版本,再检查QEMU版本是否太旧。
5.4 上板运行时的真实排错过程
板子上的真实运行和QEMU还是有区别。把test_arma连同需要的动态库拷贝到开发板(比如放到/opt/test_arma和/opt/lib下),然后在板子上执行:
export LD_LIBRARY_PATH=/opt/lib:$LD_LIBRARY_PATH ./test_arma常见的板端运行错误无非三类,排查方法如下表:
| 报错信息 | 根因 | 处理方法 |
|---|---|---|
error while loading shared libraries: libarmadillo.so.11: cannot open shared object file | 动态库路径不对 | export LD_LIBRARY_PATH指向库所在目录,或用-rpath链接选项 |
error while loading shared libraries: libopenblas.so.0: cannot open shared object file | 拷贝库时遗漏了OpenBLAS | 检查readelf -d输出的所有NEEDED库,全量拷贝 |
Illegal instruction (core dumped) | OpenBLAS的TARGET指定了板子不支持的指令集 | 用/proc/cpuinfo查实际CPU型号,重新编译OpenBLAS,降低TARGET级别 |
其中第三种情况最坑。我第一次在开发板上跑OpenBLAS时,测试程序一执行就直接Illegal instruction,第一反应还以为是编译器的问题。后来逐条排查才发现,OpenBLAS默认启用的线程本地存储机制在特定内核配置下偶发异常,加上部分ARM核的原子指令支持差异,导致非法指令。把OPENBLAS_NUM_THREADS=1环境变量加上,或者重编OpenBLAS时加入USE_OPENMP=0参数,问题就解决了。
针对性能敏感的程序,在上板运行时建议统一用环境变量控制OpenBLAS线程数:
export OPENBLAS_NUM_THREADS=4 export OPENBLAS_VERBOSE=0嵌入式板子的CPU核心数通常有限,默认的自动线程检测有时会开太多线程反而拖慢性能。
6. 交叉编译过程中绕不开的几个坑
6.1 缓存污染和宿主机库干扰
交叉编译时,CMakeCache和宿主机库干扰是两个最隐蔽的问题。CMake的探测结果会缓存起来,如果你在同一个build目录里先做了宿主机构建(或者试过不同工具链),再切到交叉编译,缓存里可能残留着宿主机路径。哪怕工具链文件写对了,缓存仍然把路径指向/usr/lib/x86_64-linux-gnu/libopenblas.so。
处理方式很粗暴也很有效:每次切换工具链前,直接删掉build目录重新创建。别心存侥幸,CMakeCache的坑不值得花时间研究。
另外,包含路径也要小心。在交叉编译项目里,-I/usr/include、-L/usr/lib这类宿主机路径不要出现在编译参数里。一旦混入,头文件冲突和架构不匹配的问题会成串出现,而且报错信息非常难定位。所有依赖的头文件和库,统一走sysroot路径。
6.2 C++ ABI版本不一致的隐性报错
宿主机编译器和交叉编译器版本不一致,会引发一类特别隐蔽的链接错误。比如宿主机gcc是11.x,交叉编译器是9.x,C++标准库的std::string、std::list等类内部布局不同,链接时会出现符号对不上或程序运行到字符串处理就崩溃。
这种问题排查起来极其痛苦,根因就是宿主机编译器和交叉编译器的_GLIBCXX_USE_CXX11_ABI宏定义不一致。我在项目里吃过一次亏:宿主机装了一堆新版本库,交叉编译的sysroot里是旧版本,两边混合用了,最后所有代码统一用交叉编译工具链来编,问题才消失。
所以交叉编译要有洁癖:宿主机上编译的C++库,绝不直接拿给交叉编译器链接;sysroot里所有的库,必须全部由交叉工具链编译生成。混用一次可能没问题,但出现问题的时候,先怀疑这个。
6.3 链接器找不到共享库或静态库时的排查思路
链接阶段报cannot find -lopenblas或者cannot find -larmadillo时,按这个顺序排查:
ls /opt/aarch64-sysroot/usr/local/lib/libopenblas*确认库文件确实存在;- 检查
-L参数是否指向了正确路径,特别注意/usr/local/lib在sysroot里对应的完整路径是/opt/aarch64-sysroot/usr/local/lib; - 检查库文件后缀:是否有
.so或.a; - 检查
CMAKE_FIND_ROOT_PATH是否把sysroot的/usr/local也算进去了。
我在流程中把OpenBLAS和armadillo都安装到了/opt/aarch64-sysroot/usr/local下,就是为了让CMake的find_library在搜索/usr/local时直接命中sysroot里的库,避免手动加一堆-L参数。
6.4 用CMake管理的项目应该怎么引入这套环境
如果你的实际项目是用CMake管理的,强烈建议在项目中复用上面写的工具链文件。在项目根目录的CMakeLists.txt中,通过find_package(armadillo REQUIRED)来引入,然后让用户在配置阶段显式指定交叉工具链:
cmake -DCMAKE_TOOLCHAIN_FILE=path/to/toolchain-aarch64.cmake ..项目里的CMakeLists.txt可以这样写:
find_package(armadillo REQUIRED) add_executable(my_app main.cpp) target_link_libraries(my_app PRIVATE armadillo)armadillo自带的CMake配置在find_package时,会读取安装时生成的ArmadilloConfig.cmake文件,这个文件里记录了它编译时链接的BLAS/LAPACK库。因为我们安装时配置正确,它会把libopenblas自动带出来,不需要在CMakeLists里手动写-lopenblas。
当然,前提是ArmadilloConfig.cmake文件必须位于sysroot内,并且find_package能通过CMAKE_PREFIX_PATH或默认路径找到它。我安装到/opt/aarch64-sysroot/usr/local后,在CMake配置中添加:
-DCMAKE_PREFIX_PATH=/opt/aarch64-sysroot/usr/local这样find_package(armadillo)就能稳当地找到sysroot里的安装版本,不会去翻宿主机目录。
7. 一点收尾的个人经验
整套流程跑通之后,你会发现交叉编译armadillo其实没有想象中可怕,核心就是三件事:一个跟目标平台匹配的工具链、一个完整的sysroot、一套严格控制查找路径的CMake配置。把这三点理顺,后面不管编OpenBLAS还是armadillo,以及你项目里其他需要交叉编译的第三方库,都是同一个思路。
我个人还有一个习惯,就是在拿到新板子的第一天,就把板子的CPU型号、glibc版本、根文件系统路径等信息记下来,连同板厂SDK里的工具链版本一起写进项目的README。后面每次做交叉编译,先对一遍这些基线信息,能省下大量排查问题的时间。
另外,如果真的只是为了在板子上跑一个不算复杂的armadillo程序,上面这套流程可能显得有点重。但现在嵌入式开发的项目规模和复杂度都在涨,一套可持续复用的交叉编译环境,从长远看一定是值得投入的。至少我在这套环境上已经把好几个数值计算模块都编译进去了,后面再加新依赖库,整个流程基本是复制粘贴的节奏。