Qt toolchain交叉编译流程
交叉编译工具gcc/g++安装
apt-cache search aarch64
apt-get install ggc-9-aarch64-linux-gnu
apt-get install g++-9-aarch64-linux-gnu
apt-get install ggc-aarch64-linux-gnu
apt-get install g++-aarch64-linux-gnu
Qt源码下载
建议直接在Qt官网下载,可按需查找对应版本Qt version
编译步骤
详细编译步骤可以参考该篇博客
4. Qt交叉编译(Qt5) — [野火]嵌入式Qt应用开发实战指南—基于LubanCat-RK开发板 文档
制作mkspec文件
首先,进入源码目录中,进行配置Qt,重新拷贝一份 qtbase/mkspecs/devices/
linux-arm-generic-g++的配置, 并且命名为 linux-aarch64-g++,重新配置qmake.conf,需要注意以下几个参数的使用,其中
QMAKE_INCDIR_POST +=
QMAKE_LIBDIR_POST +=
以上两个参数用于指定项目的系统库搜索路径列表
QMAKE_CFLAGS += -B xxxx
QMAKE_CXXFLAGS += -B xxxx
以上-B是一种编译器选项,用于指预处理器的搜索路径
QMAKE_RPATHLINKDIR_POST +=
用于指定项目运行时的库搜索路径
QMAKE_LFLAGS += -B xxxx
用于指定项目链接过程中的选项,包含了库搜索路径等链接器特定的选项
如果使用QMAKE_LIBDIR_POST的话,--sysroot仍然需要在QMAKE_CFLAGS、QMAKE_CXXFLAGS以及QMAKE_LFLAGS中定义
qmake.conf配置举例如下:
在配置linux-aarch64-g++/qmake.conf时,注意QMAKE_LIBDIR_POST以及QMAKE_CFLAGS的使用,遇到的情况总结如下:
#!/bin/sh
CROSS_COMPILE = aarch64-linux-gnu-
DISTRO_OPTS += aarch64
SYSROOT_FLAGS = --sysroot=$$[QT_SYSROOT] -B $$[QT_SYSROOT]/lib/ \
-B $$[QT_SYSROOT]/usr/lib/ \
-B $$[QT_SYSROOT]/lib/aarch64-linux-gnu/ \
-B $$[QT_SYSROOT]/usr/lib/aarch64-linux-gnu/
QMAKE_CFLAGS += $$SYSROOT_FLAGS
QMAKE_CXXFLAGS += $$SYSROOT_FLAGS
QMAKE_LFLAGS += $$SYSROOT_FLAGS
include(../linux-arm-generic-g++/qmake.conf)
参考上述QMAKE_RPATHLINKDIR_POST和QMAKE_CFLAGS的联系,
可将上述qmake.conf修改为:
#!/bin/sh
CROSS_COMPILE = aarch64-linux-gnu-
DISTRO_OPTS += aarch64
SYSROOT_FLAGS = --sysroot=$$[QT_SYSROOT]
QMAKE_LIBDIR_POST += $$[QT_SYSROOT]/lib/ \
$$[QT_SYSROOT]/usr/lib/ \
$$[QT_SYSROOT]/lib/aarch64-linux-gnu/ \
$$[QT_SYSROOT]/usr/lib/aarch64-linux-gnu/
QMAKE_CFLAGS += $$SYSROOT_FLAGS
QMAKE_CXXFLAGS += $$SYSROOT_FLAGS
QMAKE_LFLAGS += $$SYSROOT_FLAGS
include(../linux-arm-generic-g++/qmake.conf)
参考上述修改方法,mkspecs中的linux-aarch64-rockchip-gnu-g++也可进行如下修改,测试通过
root@ning-QiTianM620-N000:/opt/toolchain_Qt_V5.12.8/rk3588/sysroot/usr/lib/aarch64-linux-gnu/qt5/mkspecs/linux-aarch64-rockchip-gnu-g++# cat qmake.conf
#
# qmake configuration for building with aarch64-linux-gnu-g++
#
MAKEFILE_GENERATOR = UNIX
CONFIG += incremental
QMAKE_INCREMENTAL_STYLE = sublib
include(../common/linux.conf)
include(../common/gcc-base-unix.conf)
include(../common/g++-unix.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]/lib/aarch64-linux-gnu
QMAKE_CFLAGS += --sysroot=$$[QT_SYSROOT]
QMAKE_CXXFLAGS += --sysroot=$$[QT_SYSROOT]
QMAKE_LFLAGS += --sysroot=$$[QT_SYSROOT]
# modifications to g++.conf
QMAKE_CC = aarch64-linux-gnu-gcc
QMAKE_CXX = aarch64-linux-gnu-g++
QMAKE_LINK = aarch64-linux-gnu-g++
QMAKE_LINK_SHLIB = aarch64-linux-gnu-g++
# modifications to linux.conf
QMAKE_AR = aarch64-linux-gnu-ar cqs
QMAKE_OBJCOPY = aarch64-linux-gnu-objcopy
QMAKE_NM = aarch64-linux-gnu-nm -P
QMAKE_STRIP = aarch64-linux-gnu-strip
configure配置
配置Qt编译,使用 -device
指定我们的平台设备(linux-aarch64-g++)、 在 -device-option CROSS_COMPILE=
后指定交叉编译器路径 、 -sysroot
指定编译时根文件系位置,交叉编译的库和依赖到这个目录下查找、 -hostprefix
指定的目录存放主机相关文件、 -extprefix
指定其他安装的库存放路径, 实际使用指定 -prefix
和 -extprefix
就行。 配置编译的模块,个人根据自己的使用修改,使用命令 ./configure -help
查看关闭相关模块。
root@20d93c971bd1:/home/adv/qt-everywhere-src-5.12.8# cat build-qt.sh
#!/bin/sh
SYSROOT_PATH=/opt/toolchain_Qt_V5.12.8/rk3588/sysroot
DEVICE=linux-aarch64-g++
CROSS_CHAIN_PREFIX=/usr/bin/aarch64-linux-gnu-
INSTALL_COMMON_DIR=/usr/lib/aarch64-linux-gnu/qt5
INSTALL_HEADER_DIR=/usr/include/aarch64-linux-gnu/qt5
INSTALL_LIB_DIR=/usr/lib/aarch64-linux-gnu
INSTALL_BIN_DIR=/usr/lib/qt5/bin
INSTALL_DATA_DIR=/usr/share/qt5
INSTALL_DOC_DIR=/usr/share/qt5/doc
INSTALL_TRANSLATION_DIR=/usr/share/qt5/translations
HOST_DATA_DIR=/usr/lib/x86_64-linux-gnu/qt5
HOST_LIB_DIR=/usr/lib/x86_64-linux-gnu
do_configure () {
echo "\033[1;33mstart configure ...\033[0m"
./configure \
-device $DEVICE \
-device-option CROSS_COMPILE=${CROSS_CHAIN_PREFIX} \
-sysconfdir /etc/xdg \
-prefix /usr \
-hostprefix /usr \
-sysroot ${SYSROOT_PATH} \
-bindir ${INSTALL_BIN_DIR} \
-headerdir ${INSTALL_HEADER_DIR} \
-libdir ${INSTALL_LIB_DIR} \
-archdatadir ${INSTALL_COMMON_DIR} \
-qmldir ${INSTALL_COMMON_DIR}/qml \
-importdir ${INSTALL_COMMON_DIR}/imports \
-plugindir ${INSTALL_COMMON_DIR}/plugins \
-examplesdir ${INSTALL_COMMON_DIR}/examples \
-libexecdir ${INSTALL_COMMON_DIR}/libexec \
-datadir ${INSTALL_DATA_DIR} \
-hostbindir ${INSTALL_BIN_DIR} \
-hostlibdir ${HOST_LIB_DIR} \
-hostdatadir ${HOST_DATA_DIR} \
-release \
-opensource \
-confirm-license \
-no-opengl \
-no-openssl \
-no-xcb \
-no-eglfs \
-no-compile-examples \
-no-pkg-config \
-no-iconv \
-no-glib \
-skip qtscript \
-skip qtwebengine \
-no-use-gold-linker \
-v \
-recheck-all
echo "\033[1;33mdone...\033[0m"
}
do_configure
配置完成后,qt.conf内容如下:
[EffectivePaths]
Prefix=..
[DevicePaths]
Prefix=/usr
Documentation=share/qt5/doc
Headers=include/aarch64-linux-gnu/qt5
Libraries=lib/aarch64-linux-gnu
LibraryExecutables=lib/aarch64-linux-gnu/qt5/libexec
Binaries=lib/qt5/bin
Plugins=lib/aarch64-linux-gnu/qt5/plugins
Imports=lib/aarch64-linux-gnu/qt5/imports
Qml2Imports=lib/aarch64-linux-gnu/qt5/qml
ArchData=lib/aarch64-linux-gnu/qt5
Data=share/qt5
Translations=share/qt5/translations
Examples=lib/aarch64-linux-gnu/qt5/examples
[Paths]
Prefix=/usr
Documentation=share/qt5/doc
Headers=include/aarch64-linux-gnu/qt5
Libraries=lib/aarch64-linux-gnu
LibraryExecutables=lib/aarch64-linux-gnu/qt5/libexec
Binaries=lib/qt5/bin
Plugins=lib/aarch64-linux-gnu/qt5/plugins
Imports=lib/aarch64-linux-gnu/qt5/imports
Qml2Imports=lib/aarch64-linux-gnu/qt5/qml
ArchData=lib/aarch64-linux-gnu/qt5
Data=share/qt5
Translations=share/qt5/translations
Examples=lib/aarch64-linux-gnu/qt5/examples
HostPrefix=/usr
HostBinaries=lib/qt5/bin
HostLibraries=lib/x86_64-linux-gnu
HostData=lib/x86_64-linux-gnu/qt5
Sysroot=/opt/toolchain_Qt_V5.12.8/rk3588/sysroot
SysrootifyPrefix=true
TargetSpec=devices/linux-aarch64-g++
HostSpec=linux-g++
qt.conf中重要信息包括以下:
Sysroot和prefix指定了库的查找路径,最终的库文件安装路径为$Sysroot$prefix,TargetSpec指定了spec文件。
Qt SDK制作
Qt SDK制作的背景
下面这篇文章详细讲解了如何适配将QT的SDK可以放到任何目录用来编译Qt应用,主要和qt.conf文件Sysroot、Prefix以及HostData配置参数有关系。原因是由于Qt在编译的时候将安装路径写死在库文件中,当使用qmake xxxx.pro生成的Makefile会使用库文件中写死的SDK安装路径,这导致必须将SDK安装到特定的路径下不然找不到编译好的Qt库。而写死的SDK安装路径就是由Sysroot和Prefix配置参数定义的。幸运的是,在Qt4中引入了一个新的机制: qt.conf,于是,这个问题得以解决。在使用qmake 时引用参数-qtconf指定用户自定义的qt.conf文件即可。
qmake (.pro) SDK放在任意目录使用
Qt SDK制作步骤
创建sysroot目录
使用rsync同步设备上的/usr和/include目录,如果没有rsync命令的话,使用apt-get install rsync进行安装
mkdir /opt/toolchain_Qt_V5.12.8/rk3588/sysroot
cd /opt/toolchain_Qt_V5.12.8/rk3588/sysroot
mkdir -p usr/share
rsync -rl --delete-after --safe-links root@172.21.84.58:/lib ./
rsync -rl --delete-after --safe-links root@172.21.84.58:/{usr/include,usr/lib} ./usr
rsync -rl --delete-after --safe-links root@172.21.84.58:/{usr/share/qt5,usr/share/qtchooser} ./usr/share
使用rsync同步完sysroot之后,需要在usr/lib/aarch64-linux-gnu/qt5/mkspecs目录下创建linux-aarch64-g++或linux-aarch64-rockchip-gnu-g++,区别在于linux-aarch64-g++位于devices子目录下,linux-aarch64-rockchip-gnu-g++位于当前目录下,交叉编译.configure时两者的差异,前者是使用-device xx指定,后者是使用-xplatform xx指定。
在使用rsync同步sysroot目录后发现,/usr/lib/aarch64-linux-gnu/libpthread.so文件不存在,分析执行rsync -rl --delete-after --safe-links root@172.21.84.58:/{usr/include,usr/lib} ./usr时,/usr/lib/aarch64-linux-gnu/libpthread.so的源文件
/lib/aarch64-linux-gnu/libpthread.so.0为SRC目录树以外的链接指令,而--safe-links 选项忽略了指向SRC路径目录树以外的链接,因此在rsync同步的sysroot目录下,需要手动创建软链接。
意外发现
使用rsync拷贝sysroot不使用--safe-links选项时
libpthread.so -> /lib/aarch64-linux-gnu/libpthread.so.0
libpthread.so应该是存在的,只是指向的源路径需要从绝对路径修改为相对路径
那么刚好执行symlinks -rc ./sysroot可以帮忙解决该问题
其中 , -c选项将使用绝对路径的符号链接转换为相对路径
-r选项用于检查所有子目录中的符号链接
测试的话,可以使用-c选项和-t选项一起使用,会显示如何将绝对路径的符号链接转换为相对路径,但不会实际转换。
qmake环境安装
首先,需要安装x86环境的qmake指令,使用qmake -query可查看qmake是否安装成功
apt-get install qt5-qmake
apt-get install qt5-default
其次,在/opt/toolchain_Qt_V5.12.8/rk3588/sysroot目录下创建qmake以及qt.conf文件,内容如下
root@ning-QiTianM620-N000:/opt/toolchain_Qt_V5.12.8/rk3588/sysroot# cat qmake
#!/bin/sh
qmake "$@" -before -qtconf "/opt/toolchain_Qt_V5.12.8/rk3588/sysroot/qt.conf"
以上qmake的二进制脚本中调用的qmake指令,是/usr/bin/qmake,是通过apt-get install qt5-qmake安装的x86平台的qmake,其中$@代表命令行中传递的所有参数列表,-qtconf指定使用的qt.conf文件。
$@的使用比如执行/opt/toolchain_Qt_V5.12.8/rk3588/sysroot/qt5/qmake -query时,-query就在$@中传递
qmake "$@" -qtconf /opt/toolchain_Qt_V5.12.8/rk3588/sysroot/qt5/qt.conf
具体执行内容为:
/usr/bin/qmake -query -qtconf /opt/toolchain_Qt_V5.12.8/rk3588/sysroot/qt5/qt.conf
qt.conf的内容如下:
root@ning-QiTianM620-N000:/opt/toolchain_Qt_V5.12.8/rk3588/sysroot# cat qt.conf
[Paths]
# Target paths (see /usr/lib/aarch64-linux-gnu/qt5/qt.conf)
Sysroot=/opt/toolchain_Qt_V5.12.8/rk3588/sysroot
SysrootifyPrefix=true
Prefix=/usr
ArchData=lib/aarch64-linux-gnu/qt5
Binaries=lib/qt5/bin
Data=share/qt5
Documentation=share/qt5/doc
Examples=lib/aarch64-linux-gnu/qt5/examples
Headers=include/aarch64-linux-gnu/qt5
Imports=lib/aarch64-linux-gnu/qt5/imports
Libraries=lib/aarch64-linux-gnu
LibraryExecutables=lib/aarch64-linux-gnu/qt5/libexec
Plugins=lib/aarch64-linux-gnu/qt5/plugins
Qml2Imports=lib/aarch64-linux-gnu/qt5/qml
Settings=/etc/xdg
Translations=share/qt5/translations
# Host paths
HostPrefix=/usr
HostData=lib/x86_64-linux-gnu/qt5
HostBinaries=lib/qt5/bin
HostLibraries=lib/x86_64-linux-gnu
TargetSpec=devices/linux-aarch64-g++
[EffectivePaths]
# Use target mkspecs
HostData=/opt/toolchain_Qt_V5.12.8/rk3588/sysroot/usr/lib/aarch64-linux-gnu/qt5
# Dup host paths
HostPrefix=/usr
HostBinaries=lib/qt5/bin
HostLibraries=lib/x86_64-linux-gnu
# Dup target paths
Prefix=/usr
ArchData=lib/aarch64-linux-gnu/qt5
Binaries=lib/qt5/bin
Data=share/qt5
Documentation=share/qt5/doc
Examples=lib/aarch64-linux-gnu/qt5/examples
Headers=include/aarch64-linux-gnu/qt5
Imports=lib/aarch64-linux-gnu/qt5/imports
Libraries=lib/aarch64-linux-gnu
LibraryExecutables=lib/aarch64-linux-gnu/qt5/libexec
Plugins=lib/aarch64-linux-gnu/qt5/plugins
Qml2Imports=lib/aarch64-linux-gnu/qt5/qml
Settings=/etc/xdg
Translations=share/qt5/translations
在qt.conf中定义了qt库文件的安装路径以及Hostdata参数,使用Hostdata参数可找到自定义的mkspecs文件,如linux-aarch64-g++和linux-aarch64-rockchip-gnu-g++。如果修改了以上qt.conf中EffectivePaths中的HostData,会导致qmake在构建项目时报错,
提示Could not find qmake configuration file devices/linux-aarch64-g++
所以说,EffectivePaths中的HostData和Qt项目构建时查找mkspecs文件有关系。
对比分析qt.conf
分析对比此处的qt.conf文件和交叉编译Qt configure之后生成的qt.conf,差异就在于[EffectivePaths]中Hostdata的值不同,如果将交叉编译过程Qt configure生成的qt.conf,修改[EffectivePaths]中Hostdata为/opt/toolchain_Qt_V5.12.8/rk3588/sysroot/usr/lib/aarch64-linux-gnu/qt5后,使用交叉编译生成的qt.conf也可成功制作SDK。
通过上述分析,可能会有以下的疑问:Paths和EffectivePaths中定义的HostData有什么差异?
答:在Qt的配置文件qt.conf中,Paths和EffectivePaths都是用来指定一些路径信息的配置项,而其中的HostData字段通常是指主机的数据路径。
在使用qmake构建.pro项目时,Paths和EffectivePaths中的hostdata字段的差异在于:
Paths中的hostdata字段通常用于指定Qt应用程序运行时需要查找和加载的主机数据文件的路径。
这些数据文件可以是与Qt应用程序相关的配置文件、数据文件或其他资源文件。
EffectivePaths中的hostdata字段则是在构建过程中使用的路径,它通常是由Qt构建系统根据主机环境和构建选项自动确定的。
这个路径通常包含构建输出文件(例如可执行文件、库文件等)的主机数据相关的文件或目录。
总结来说,Paths中的HostData字段是在Qt应用程序运行时使用的路径,用于查找和加载主机数据文件,而EffectivePaths中的HostData字段是在Qt构建过程中使用的路径,由构建系统根据构建选项自动确定。
openGL库引用方式差异
使用/opt/toolchain_Qt_V5.12.8/rk3588/sysroot/qmake构建项目,在生成的Makefile中关于libGL.so库的引用发现存在问题,库文件的路径为/usr/lib/aarch64-linux-gnu/libGL.so,需要修改为/opt/toolchain_Qt_V5.12.8/rk3588/sysroot/usr/lib/aarch64-linux-gnu/libGL.so.。
通过与Debian系统中Qt SDK对比发现,在ubuntu20.04 Qt SDK中/usr/lib/aarch64-linux-gnu/qt5/mkspecs/modules目录下的一些.pri文件中有关于QMAKE_LIBDIR_OPENGL参数的设置,区别于Debain Qt SDK的差异在于前者赋值为usr/lib/aarch64-linux-gnu/libGL.so,后者赋值为-lGL,修改前者为-lGL后问题得以解决。
最终将ubuntu 20.04 Qt SDK中/usr/lib/aarch64-linux-gnu/qt5/mkspecs/modules中QMAKE_LIBDIR_XX=usr/lib/aarch64-linux-gnu/libx.so的部分,全部搜索进行修改,确保Makefile中对于.so的引用路径均对应到sysroot目录下。
Qt SDK发布流程
目录结构如下
root@ning-QiTianM620-N000:/opt/work/rk3568/qt5/createbin# ls -ll
total 1526624
-rwxr-xr-x 1 ning ning 419 11月 21 13:56 createbin.sh
-rwxr-xr-x 1 ning ning 407 11月 21 14:08 installbin.sh
-rwxr-xr-x 1 root root 781620700 11月 21 14:08 rk3588_aarch64_ubuntu20.04_toolchain_Qt_V5.12.8.run
-rw-r--r-- 1 root root 781620246 11月 21 13:52 rk3588_aarch64_ubuntu20.04_toolchain_Qt_V5.12.8.tar.gz
压缩sysroot
rk3588_aarch64_ubuntu20.04_toolchain_Qt_V5.12.8.tar.gz的制作,在 /opt/toolchain_Qt_V5.12.8/rk3588/目录下,压缩sysroot目录
cd /opt/toolchain_Qt_V5.12.8/rk3588
tar zcf rk3588_aarch64_ubuntu20.04_toolchain_Qt_V5.12.8.tar.gz ./sysroot/
生成安装脚本
Qt SDK生成脚本creatbin.sh内容如下:
root@ning-QiTianM620-N000:/opt/work/rk3568/qt5/createbin# cat createbin.sh
#!/bin/bash
if [ "$1" = "" ];then
echo "please input The data source"
echo "eg. ./createbin.sh rk3588_*_toolchain_Qt_V5.12.8_*.tar.gz"
exit 1;
fi
BSP=$1
INSTALLSH=installbin.sh
BASE=$(basename $BSP .tar.gz)
cat $INSTALLSH | sed -e s/OUTNAME/$BSP/ > linux_license_new.bin
dos2unix -k -q linux_license_new.bin
cat linux_license_new.bin $BSP > ${BASE}.run
rm -rf linux_license_new.bin
sudo chmod a+x ${BASE}.run
Qt SDK安装脚本installbin.sh内容如下:
root@ning-QiTianM620-N000:/opt/work/rk3568/qt5/createbin# cat installbin.sh
#!/bin/bash
localinstall=/opt/toolchain_Qt_V5.12.8/rk3588/
if [ -d "$localinstall" ] ; then
rm -rf $localinstall
fi
/bin/mkdir -p $localinstall
outname=$localinstall/OUTNAME
echo "Unpacking..."
sed -n -e '1,/^exit 0$/!p' $0 > $outname
echo "Install aarch64-ubuntu20.04 Qt V5.12.8 Cross Compiler, May be few minutes, please wait..."
tar -zxf $outname -C $localinstall
rm -rf $outname
echo "Done."
exit 0
installbin.sh脚本中OUTNAME的值在creatbin.sh中使用sed语法将其替换为rk3588_aarch64_ubuntu20.04_toolchain_Qt_V5.12.8.tar.gz,installbin.sh脚本中sed语法的使用sed -n -e '1,/^exit 0$/!p' $0 > $outname用于将creatbin.sh脚本中cat合并的两个文件installbin.sh和rk3588_aarch64_ubuntu20.04_toolchain_Qt_V5.12.8.tar.gz重新分开。具体来说,就是将.run文件中除了第一行和 exit 0 所在行中间的部分,也就是exit 0后面的内容,输出到$outname
$0 是当前脚本的名,也就是 rk3588_aarch64_ubuntu20.04_toolchain_Qt_V5.12.8.run
exit 0 在这里可以看作一个分割标志,可以使用其他字符串代替。
最终,执行rk3588_aarch64_ubuntu20.04_toolchain_Qt_V5.12.8.run即可成功安装Qt toolchain到/opt/toolchain_Qt_V5.12.8/rk3588/sysroot目录下