当我需要在嵌入式arm linux平台上运行Qt应用程序时,我了解到两种解决方案:
Qt for Device Creation + Boot to Qt,这个技术还是蛮让人着迷的,虽然只能使用商业版的Qt,但是用了这么久的开源版Qt,付费也未尝不可它; 但是当我在Qt的官方网站上了解到该公司需要留下信息来协商商业Qt的价格后,我就放弃了这个想法(其实我后来买了一些许可证...),因为这是充满了未知数,没有人知道他们要如何定价。 在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)选择使用Nvidia提供的文件系统模拟工具,选择适合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
其实只要把这两个Nvidia提供的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
在这一步中,您可能会遇到各种问题qt源码编译方法,例如编译套件损坏、构建套件无法使用等。 请检查是否忘记设置环境变量等,不要错过细节,一一解决问题;
(1)遇到c++11相关错误,在源代码目录下输入qtbase/mkspecs/common/gcc-base.conf,将QMAKE_CFLAGS_ISYSTEM = -isystem修改为QMAKE_CFLAGS_ISYSTEM = -I(大写i),然后使用当前的qmake将QMAKE_CXXFLAGS += -std=gnu++11和QMAKE_CFLAGS_ISYSTEM=-I添加到.conf中;
(2)注意qmake.conf中的注释
在openg es2头文件的情况下,configure可以正确识别es2,正确加载依赖库并编译成功;
由于直接使用文件系统,所以不考虑注释中提到的libm.a错误;
如果编译时仍然出现奇怪的链接错误,请检查依赖库的软链接是否正常,包括但不限于libm.so、libz.so、libc.so、libdl.so等;
(3)如果autoconfig执行过程中出现错误,建议复制一份源代码,改完错误后执行,否则可能会导致更新错误,缓存文件会一直报错;
(四)预期结果
执行configure时不应出现错误,相关项应为yes:
8.编译源码
(1)上一步正常执行后,会生成Makefile,执行命令:
sudo make -j16
“-j16”参数指定并行编译,根据个人开发机器的核心线程数适当设置可以提升编译速度;
(2) 在“-j16”的情况下,编译大约需要20分钟或更长时间。 如果这期间出现错误,请仔细检查链接和依赖关系。 编译成功后,执行:
sudo make install
大约1-2分钟安装完成;
(3)如果编译出现错误,建议复制一份源代码,改完错误后重新操作,不要寄希望于“make clean”;
(4)以上编译安装正确后,交叉编译的Qt就会安装在autoconfig中指定的路径下,如图:
预计大小会超过200Mb,如果太小,请考虑编译的正确性;
9. 在开发机上构建交叉编译套件
(1)打开QtCreator,进入Tools -> Options -> Kits(不同版本的QtCreator可能略有不同)
(2)选择编译器选项卡,点击“添加”qt源码编译方法,在“gcc”项中分别添加gcc和g++,并分别从交叉口的安装目录安装aarch64-linux-gnu-gcc和aarch64-linux-gnu-g++ compiler 将路径添加到编译器路径中,然后点击Apply; 如图所示:
(3)切换到Qt Versions页面,点击Add按钮,进入交叉编译库所在目录/tools(由autoconfig指定),选择qmake添加,修改Versions Name,如:Qt -for-arm5.14.2,点击Apply Changes,如图:
(4)切换到Kits页面,点击Add按钮,添加一个kit,修改一个可识别的kit名称,如:5.14.2 arm 64,修改该kit的“Compiler”为第2步添加的编译器当前条目,修改“Qt版本”为步骤3中添加的Qt版本,点击Apply按钮应用,如图:
10.交叉编译套件测试
(1)创建一个空白的Qt工程,通用类型为QWidget,选择第9项添加的套件作为创建套件,在pro文件中引入opengl模块:
QT += 核心 gui sql opengl
编译工程时,如果报错提示找不到opengl模块,则证明交叉编译错误,请回溯编译步骤;
(2)将测试项改为QOPenglWidget窗口,测试opengl功能的完整性,在ui中添加一个按钮设置英文文本,直接转入槽位,测试Qt基本功能的完整性,如如图所示:
(3)本测试项目的功能是使用opengl绘制一个蓝色背景的窗口,按钮为英文文本,测试英文字符。 点击按钮,弹出一个简单的对话框来编译项目。 如果出现opengl相关类型或函数错误报告,则证明编译不完整,请回溯编译步骤;
11.将Qt库移植到嵌入式linux并运行测试项目
(1)使用硬盘或网络将交叉编译的Qt库(如Qt-for-arm5.14.2)文件夹和测试测试项目生成的可执行文件复制到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 将导致 Qt5 从 qt-default 和 qtchooser 链接。 9.5;
如果以上测试功能正确,则证明Qt的交叉编译配置和移植已经成功完成。
附件:auticonfig.sh、qmake.conf、测试用例
自动配置文件
qmake.conf
测试.zip
发表评论