赞
踩
在我需要将Qt应用程序运行到嵌入式arm linux平台上时,我了解到了两种方案:
Qt for Device Creation + Boot to Qt,这项技术相当诱人,虽然只有商业版Qt能够使用,但用了这么久的开源版本Qt,为其适当的付费也未尝不可;但是当我在Qt的官网上了解到公司商用Qt需要留下资料协商定价后,我就打消了这个念头(事实上后来还是买了部分license…),因为这充满了未知,谁也不知道他们想要如何定价,在Qt的官网上还有着相当嘲讽的广告:
交叉编译Qt源码,移植至arm linux,网上大部分资料都是较为老的Qt版本,而且这个过程还会伴随大量问题,尤其是需要附加opengl模块的时候更是灾难,但既然第一个方案被自我否决,那就只能使用这个方案了,当然,浮夸又时尚的我们选择一个较为新颖的Qt版本:5.14.2(写于2021年),以下是我交叉编译并移植至Jetson Nano的过程。
首先我们要明确目的:在x86_64架构的PC上,使用arm交叉编译器构建Qt源码,将编译出的Qt运行支持库部署至目标设备嵌入式linux,开发机使用同版本号Qt编写代码并运行测试,需打包发布应用程序时依赖开发机的性能,使用交叉编译套件生成可执行文件。
1.获取Qt源码
建议以下两种方式获取:
(1)如安装Qt的时候勾选了 Source 选项,可从Qt安装目录下的版本号文件夹下获取Src目录使用(如 5.14.2);
(2)Qt官方网站获取,Qt5.14.2源码地址,务必选
择tar.xz后缀的源码包,zip包可能在windows下编辑过,直接使用可能会报字符集的错误,解决起来较为耗时;
2.安装交叉编译器
安装交叉编译器的方式分为两种,推荐第二种方式,速度较快:
(1)官网下载交叉编译器下载地址;
(2)apt-get指令获取
sudo apt-get install gcc-aarch64-linux-gnu #安装一个适配当前系统版本的编译器
加入环境变量:
sudo vim /etc/profile
底部加入:
export PATH="/usr/bin:$PATH"
重启或执行source /etc/profile
生效;
3.安装Jetson Nano文件系统
(1)选择使用英伟达提供的文件系统仿真工具,选择适用于Nano的版本,下载地址
(2)得到 Jetson-210_Linux_R32.6.1_aarch64.tbz2 和 Tegra_Linux_Sample-Root-Filesystem_R32.6.1_aarch64.tbz2 文件;(文件名或许会根据版本而变动)
(3)创建文件夹安置文件系统
mkdir JetsonNano
cd JetsonNano
sudo tar xpf Jetson-210_Linux_R32.6.1_aarch64.tbz2
cd Linux_for_Tegra/rootfs/
sudo tar xpf ../../Tegra_Linux_Sample-Root-Filesystem_R32.6.1_aarch64.tbz2
sudo ../apply_binaries.sh
4.Jetson Nano上链接库的准备
(1)安装Qt依赖库
sudo apt-get install '.*libxcb.*' libxrender-dev libxi-dev libfontconfig1-dev libudev-dev
(2)软连接opengl es库
进入 Jetson Nano 的 /usr/lib/aarch64-linux-gnu/tegra-egl 目录,执行:
sudo ln -s libGLESv2_nvidia.so.2 libGLESv2.so
sudo ln -s libEGL_nvidia.so.0 libEGL.so
实际上只是将这两个英伟达提供的 gl 动态库重命名,不同的设备厂商提供的支持库名字可能会有差异;
5.同步Jetson Nano的文件系统
保证 Nano 设备和自己的开发机在同一局域网下,使用 ifconfig
指令查看 Nano 设备的ip地址;同步 /usr/include 和 /usr/lib 下的所有文件:
sudo rsync -e ssh -avz root@192.168.10.201:/usr/include .
sudo rsync -e ssh -avz root@192.168.10.201:/usr/lib .
root为Nano上的用户名,ip地址为Nano的ip,这个过程时间较长;
如果遇到指令无法正确执行,确认用户名、密码及ip地址;
如遇连接被拒绝,可能需要修改Nano的ssh配置文件,自行查阅资料;
同步完毕后,进入Linux_for_Tegra文件夹 ,新建脚本文件 link.sh,复制以下内容:
#!/bin/bash if [ "$#" -ne 1 ]; then echo "usage ./cmd rootfs_path" exit -1 fi ROOTFS=$(readlink -f $1) cd $ROOTFS/usr/lib/aarch64-linux-gnu find . -maxdepth 1 -type l | while read i; do qualifies=$(readlink $i | grep '^\(/usr\)\?/lib') if [ -n "$qualifies" ]; then newPath=$(readlink $i | grep '^\(/usr\)\?/lib' | xargs echo $ROOTFS | sed -e s/\\s//g) echo $i echo $newPath; rm $i; ln -s $newPath $i; fi done
保存并退出文件,运行指令:
sudo ./link.sh ./rootfs/
修复因为同步而损坏的链接文件,注意这一步,因为软连接的失效导致编译出错耽误了不少时间;
6.准备编译配置
(1)在第三步准备好的文件系统下的/home目录创建任意一个用户文件夹,如 user, 复制一份准备好的源码到此目录作为工作目录,在自己的用户工作目录创建空文件夹,预期将交叉编译的Qt安装到此处,如:Qt-for-arm5.14.2;
(2)进入源码目录,创建 autoconfig.sh 文件,编辑内容:
./configure \
-v \
-opensource \
-confirm-license \
-device-option CROSS_COMPILE=/usr/bin/aarch64-linux-gnu- \
-device linux-jetson-tx1-g++ \
-prefix /home/你的用户目录/Qt-for-arm5.14.2 \
-extprefix /home/你的用户目录/Qt-for-arm5.14.2 \
-hostprefix /home/你的用户目录/Qt-for-arm5.14.2/tools \
-nomake examples \
-nomake tests \
-nomake tools \
-opengl es2 \
-sysroot /home/你的用户目录/jetsonNano/Linux_for_Tegra/rootfs
这个sh文件指示了执行configure指令的参数,其中有几个需要关注的参数:
//指示了交叉编译器,编译时-后会自动连接到gcc/g++, //查看自己对应目录下是否有aarch64-linux-gnu-gcc/g++ -device-option CROSS_COMPILE=/usr/bin/aarch64-linux-gnu- \ //指示了使用源码目录下 qtbase/mkspecs/devices/ 下的某个文件下的qmake.conf, //由于没有 Nano 文件夹,我们使用相似的linux-jetson-tx1-g++下的qmake.conf, //文件夹名不重要,规则是qmake.conf中的内容,我们将会修改qmake.conf; -device linux-jetson-tx1-g++ \ //编译后执行make install指令安装的目标路径,这个文件夹预先创建 -extprefix /home/你的用户目录/Qt-for-arm5.14.2 \ //存放开发机上使用交叉编译库所需的工具,如关键的 qmake -hostprefix /home/你的用户目录/Qt-for-arm5.14.2/tools \ //不编译Qt样例 -nomake examples \ //不编译 test 文件 -nomake tests \ //不编译自带工具,如creator等 -nomake tools \ //编译opengl es2 -opengl es2 \ //指示文件系统路径,在qmake.conf中我们会用到 -sysroot /home/你的用户目录/jetsonNano/Linux_for_Tegra/rootfs
(3)编辑qmake.conf
进入我们在configure阶段指定的将要使用的qmake.conf路径,编辑qmake.conf文件:
include(../common/linux_device_pre.conf) QMAKE_INCDIR_POST += \ $$[QT_SYSROOT]/usr/include \ $$[QT_SYSROOT]/usr/include/aarch64-linux-gnu QMAKE_LIBDIR_POST += \ $$[QT_SYSROOT]/usr/lib \ $$[QT_SYSROOT]/lib/aarch64-linux-gnu \ $$[QT_SYSROOT]/usr/lib/aarch64-linux-gnu QMAKE_RPATHLINKDIR_POST += \ $$[QT_SYSROOT]/usr/lib \ $$[QT_SYSROOT]/usr/lib/aarch64-linux-gnu \ $$[QT_SYSROOT]/usr/lib/aarch64-linux-gnu/tegra \ $$[QT_SYSROOT]/lib/aarch64-linux-gnu QMAKE_INCDIR_OPENGL[_ES2] += \ $$[QT_SYSROOT]/include \ $$[QT_SYSROOT]/include/EGL \ $$[QT_SYSROOT]/include/GLES2 \ $$[QT_SYSROOT]/include/GLES3 \ $$[QT_SYSROOT]/include/KHR \ $$[QT_SYSROOT]/usr/include \ $$[QT_SYSROOT]/usr/include/EGL \ $$[QT_SYSROOT]/usr/include/GLES2 \ $$[QT_SYSROOT]/usr/include/GLES3 \ $$[QT_SYSROOT]/usr/include/KHR QMAKE_LIBDIR_OPENGL[_ES2] += \ $$[QT_SYSROOT]/lib/aarch64-linux-gnu/tegra \ $$[QT_SYSROOT]/lib/aarch64-linux-gnu/tegra-egl \ $$[QT_SYSROOT]/lib/aarch64-linux-gnu \ $$[QT_SYSROOT]/usr/lib/aarch64-linux-gnu/tegra \ $$[QT_SYSROOT]/usr/lib/aarch64-linux-gnu/tegra-egl \ $$[QT_SYSROOT]/usr/lib/aarch64-linux-gnu QMAKE_LIBS_OPENGL[_ES2] += -lEGL -lGLESv2 DISTRO_OPTS += aarch64 COMPILER_FLAGS += -mtune=cortex-a57.cortex-a53 -march=armv8-a EGLFS_DEVICE_INTEGRATION = eglfs_x11 #EGLFS_DEVICE_INTEGRATION = eglfs_kms_egldevice include(../common/linux_arm_device_post.conf) load(qt_config)`
7.执行 sudo ./autoconfig
在这一步,可能会遇到各种问题,如编译套件损坏、无法使用构建套件请检查是否忘记设置环境变量等,不要遗漏细节,一个个解决问题;
(1)如遇 c++11 相关报错,进入源码目录下 qtbase/mkspecs/common/gcc-base.conf,将QMAKE_CFLAGS_ISYSTEM = -isystem
改为 QMAKE_CFLAGS_ISYSTEM = -I (capital i)
,然后在当前使用的qmake.conf中加入QMAKE_CXXFLAGS += -std=gnu++11
和 QMAKE_CFLAGS_ISYSTEM=-I
;
(2)注意qmake.conf中的注释
在拥有openg es2头文件的情况下,configure才能正确识别es2,正确加载依赖库才能编译成功;
由于直接使用了文件系统,所以不考虑注释中所提到的libm.a报错;
如果编译时仍出现奇怪的链接错误,请检查依赖库的软链接是否正常,包括但不限定于 libm.so,libz.so,libc.so,libdl.so等;
(3)执行autoconfig 出现任何错误都建议重新拷贝一份源码修改错误后执行,否则可能导致更新错误后,缓存文件导致仍然报错;
(4)预期的结果
执行 confogure 应无 error,且所关注的条目应为 yes:
8.编译源码
(1)上一步正常执行后将会生成 Makefile 文件,执行指令:
sudo make -j16
“-j16”参数指定了并行编译,按个人开发机的核心线程数适量设置可加快编译速度;
(2)“-j16” 情况下约编译二十分钟以上,期间出现错误请仔细检查链接和依赖,成功编译后执行:
sudo make install
约1-2分钟安装完毕;
(3)编译出现任何错误都建议重新拷贝一份源码修改错误后重新操作,不要寄希望于 “make clean”;
(4)以上执行编译安装无误后,将在autoconfig中指定的路径中安装交叉编译的Qt,如图:
预期大小将是 200Mb 以上,过小请考虑编译的正确性;
9.开发机搭建交叉编译套件
(1)打开QtCreator,进入 工具->选项->Kits (不同版本QtCreator可能会略有不同)
(2)选择编译器分页,点击 “添加”,在“gcc”条目中分别添加 gcc 和 g++,从交叉编译器的安装目录中分别将 aarch64-linux-gnu-gcc 和 aarch64-linux-gnu-g++ 路径添加入编译器路径,并点击 Apply 应用;如图:
(3)切换至 Qt Versions 分页,点击添加按钮,进入交叉编译库所在的目录/tools(autoconfig指定),选中qmake将其添加,修改该Versions的 Name,如:Qt-for-arm5.14.2,点击 Apply 应用修改,如图:
(4)切换至Kits分页,点击 Add 按钮,添加套件,修改一个有辨识度的套件名,如:5.14.2 arm 64,修改该套件的"Compiler"为当前条目步骤2添加的编译器,修改“Qt version”为步骤3添加的 Qt Versions,点击Apply 按钮应用,如图:
10.交叉编译套件测试
(1)创建一个空白的以QWidget为基类的Qt项目,选择条目9中添加的套件为构建套件, pro文件中引入 opengl 模块:
QT += core gui sql opengl
编译项目,如报错提示找不到opengl模块,则证明交叉编译有误,请回溯编译步骤;
(2)将测试项目修改为QOPenglWidget窗口,测试opengl功能完整性,ui中加入一个按钮设置中文文本,直接转到槽,测试Qt基础功能完备性,如图:
(3)该测试项目功能为使用opengl画出一个背景为黄色的窗口,按钮为中文文本用以测试中文字符,点击按钮弹出一个简单对话框,编译该工程,如出现opengl相关类型或函数报错,则证明编译不完整,请回溯编译步骤;
11.Qt库移植至嵌入式linux并运行测试工程
(1)使用U盘或网络将交叉编译Qt库(如Qt-for-arm5.14.2)文件夹和test测试工程生成的可执行文件复制到 Nano,将交叉编译Qt库移动到 Nano的 /usr/lib 下,测试工程放在任意位置(如Downloads或主目录);
(2)修改环境变量:
sudo gedit /etc/profile
末尾加入:
export QTDIR=/usr/lib/Qt-for-arm5.14.2
export LD_LIBRARY_PATH=/usr/lib/Qt-for-arm5.14.2/lib:$LD_LIBRARY_PATH
export QT_QPA_PLATFORM_PLUGIN_PATH=$QTDIR/plugins
export QT_QPA_PLATFORM=eglfs
保存并退出,执行 source /etc/profile
或重启Nano使其生效;
(3)运行测试样例
进入测试样例所在文件,执行./test
(test为用例文件名),默认以环境变量中的 eglfs 启动,独占整个屏幕,执行
./test -platform xcb
将以普通桌面应用方式启动,注意此处不能以 sudo 方式运行程序,由于 Nano 自带了Qt5.9.5,使用sudo会导致从 qt-default和qtchooser中链接所指向的Qt5.9.5;
以上测试功能无误则证明成功完成Qt的交叉编译配置和移植。
附:auticonfig.sh、qmake.conf、test测试用例
autoconfig.sh
qmake.conf
test.zip
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。