前言
编译出来了qtbase库和qtwebengine库,如果按照文章没有操作出来有两种情况
(1) 读者阅读不仔细
(2) 我后期记录整理时写错了
本地交叉编译qt源码的目标
(1) 能编译出来常用的qt库
(2) 使用交叉编译链工具和qt库编译出一个qt demo
(3) qt demo能在arm架构的银河麒麟V10SP1上正常运行(依赖编译出来的qt库)
本次编译qt源码主要就是研究qtbase库和webengine库如何成功编译出来,其他库没有特别在意。
有问题评论区留言
交叉编译思路
思路就是在虚拟机里搭建一个x86本地环境,然后在x86本地环境中再部署一个arm根文件系统,在x86本地环境用交叉编译链工具编译qt源码,编译过程中链接根文件系统中的库文件
虚拟机配置
虚拟机内存设置10G以上,我观察编译qtwebengine的时候,内存消耗会达到9G,所以内存要多设置一些,核心数设置四个以上比较好,磁盘空间设置60G左右就行了
glibc版本的匹配
交叉编译首先就是要确保编译出来的程序是可以在目标环境上运行的,因此要保证arm根文件系统使用的glibc版本和目标环境上版本一致,操作系统使用的glibc库的版本可以使用下面的命令获取
lzq@lzq-VirtualBox:~/forQt/debian-11-linux-rootfs/qtbuild$ ldd --version
ldd (Ubuntu GLIBC 2.39-0ubuntu8.2) 2.39
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
arm根文件系统的选择
下文会介绍arm根文件系统的部署方法,这里我先记录一下我已经使用过的arm根文件系统版本和使用结果
arm根文件系统 | 使用结果 |
ubuntu24.04 | 可以编译出qtbase库和webengine库,但是glibc版本太高,为2.39,目前嵌入式和国产化系统的版本没有这么高,所以没有普适性 |
ubuntu18.04~ubuntu22.04 | 使用包管理器无法安装某几个关键的库(例如 gperf),即使编译源码安装上也是有版本问题,依赖的库是一层套一层,太麻烦了,所以放弃 |
debian11 | glibc版本是2.31,这是目前国产化操作系统银河麒麟SP1支持的glibc版本,这就很匹配,我的目标也是要适配银河麒麟SP1 |
debian11或者ubuntu18.04一下的版本 | 按照以上规律,glibc版本逐渐下降,但这些系统我没有尝试,搭建一个环境太麻烦了,读者有兴趣可以试试 |
本地x86环境的系统版本也要和arm根文件系统一致,这样glibc版本都一样,减少奇怪的兼容问题
qt源码版本的选择
一般交叉编译都是用在嵌入式开发上,所以qt版本也不会选太高,我这是使用的是qt5.12.12版本,和公司使用的版本匹配
qt5.15.x版本也试过,其qtwebengine模块又加了一些新东西,还需要增加额外的环境配置才能编译出来(没有深入研究)
搭建根文件系统
在本地x86环境下搭建arm的根文件系统,主要就是为了获取arm架构的库文件,其实也可以从目标机上拷贝下来放到本地x86环境中的,但是后期缺什么还需要再拷贝很麻烦(但是适配度高),不如搭建跟文件系统方便,需要什么库就下什么,两种方法各有优劣,下面介绍一下搭建过程
//安装必要依赖 qemu 能安装就安装,安不了也没事
$ sudo apt-get install qemu-user-static binfmt-support debootstrap qemu
//和下载根文件系统密钥相关
$ sudo apt-get install debian-archive-keyring
//创建一个build目录
$ sudo mkdir build
$ cd build
//构建文件系统的命令 sudo debootstrap --arch [平台] [发行版本代号] [目录] [源]
$ sudo debootstrap --arch=arm64 --foreign xxxx linux-rootfs
// [-foreign] 在与主机架构不相同时需要指定此参数,仅做初始化的解包
// [发行版本代号] 用lsb_release -a获取 例如 debian11 代号是 bullsey, 注意要用小写字母
// [源] 我使用默认源,所以不填写
//==================注意此时还是在build目录下操作==================
//qemu-aarch64-static是其中的关键,能在x86_64主机系统下chroot到arm64文件系统
$ sudo cp -a /usr/bin/qemu-aarch64-static ~/build/linux-rootfs/usr/bin/qemu-aarch64-static
//执行ch-mount.sh脚本 -u 是取消挂载 -m 是挂载 ch-mount.sh脚本的内容在下面
$ ./ch-mount.sh -m linux-rootfs/
//执行脚本后,没有报错会进入文件系统,显示 I have no name ,这是正常的
I have no name!@node2:/#
//初始化文件系统,会把一个系统的基础包等全部初始化
$ debootstrap/debootstrap --second-stage
//初始化好了以后,退出文件系统
$ exit
//再次进入时,不需要执行脚本,使用chroot命令即可
$ sudo chroot linux-rootfs
//================================================================================================
//下面是 ch-mount.sh 脚本的内容
#!/bin/bash
function mnt() {
echo "MOUNTING"
sudo mount -t proc /proc ${2}proc
sudo mount -t sysfs /sys ${2}sys
sudo mount -o bind /dev ${2}dev
sudo mount -o bind /dev/pts ${2}dev/pts
sudo chroot ${2}
}
function umnt() {
echo "UNMOUNTING"
sudo umount ${2}proc
sudo umount ${2}sys
sudo umount ${2}dev/pts
sudo umount ${2}dev
}
if [ "$1" == "-m" ] && [ -n "$2" ] ;
then
mnt $1 $2
elif [ "$1" == "-u" ] && [ -n "$2" ];
then
umnt $1 $2
else
echo ""
echo "Either 1'st, 2'nd or both parameters were missing"
echo ""
echo "1'st parameter can be one of these: -m(mount) OR -u(umount)"
echo "2'nd parameter is the full path of rootfs directory(with trailing '/')"
echo ""
echo "For example: ch-mount -m /media/sdcard/"
echo ""
echo 1st parameter : ${1}
echo 2nd parameter : ${2}
fi
交叉编译工具链的准备
交叉编译链制作太复杂,我是在下面这个网站找的,只要gcc和g++支持C++14应该就可以
https://releases.linaro.org/components/toolchain/binaries/ 旧版本(我用的这个) 下面这几个没用过,不过也列出来 https://snapshots.linaro.org/gnu-toolchain/ 新版本 https://www.linaro.org/downloads/ 官方网站 https://developer.arm.com/downloads/-/gnu-a arm开发工具下载
再点击进入几层目录,直接到达这个地址,选择解压后能直接用的成品(还要注意32位、64位,大端序还是小端序,选择基于自己开发环境的系统和机器),如下
https://releases.linaro.org/components/toolchain/binaries/5.4-2017.05/aarch64-linux-gnu/ gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu.tar.xz 27-Feb-2018 00:14 86.5M open #这个是解压后就能用的
qt源码文件的配置
交叉编译链解压放在某个位置,接着就可以设置xxx/qt-everywhere-src-5.13.2/qtbase/mkspecs/linux-aarch64-gnu-g++对应的编译链路径
lzq@lzq-VirtualBox:/opt/qt-src/qt-everywhere-src-5.13.2/qtbase/mkspecs/linux-aarch64-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) # modifications to g++.conf 下面这些就是交叉编译链的路径 QMAKE_CC = /opt/arm-libc/gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc QMAKE_CXX = /opt/arm-libc/gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ QMAKE_LINK = /opt/arm-libc/gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ QMAKE_LINK_SHLIB = /opt/arm-libc/gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ # modifications to linux.conf QMAKE_AR = /opt/arm-libc/gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-ar cqs QMAKE_OBJCOPY = /opt/arm-libc/gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-objcopy QMAKE_NM = /opt/arm-libc/gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-nm -P QMAKE_STRIP = /opt/arm-libc/gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-strip load(qt_config)
configure的配置
创建一个shell脚本用于执行qt的configure,如下,出了问题再一步步完善脚本和库文件
#!/bin/bash cd ~/forQt/qt-everywhere-src-5.12.12 export LD_LIBRARY_PATH=/home/lzq/forQt/debian-11-linux-rootfs/usr/lib:${LD_LIBRARY_PATH} export LD_LIBRARY_PATH=/home/lzq/forQt/debian-11-linux-rootfs/lib/aarch64-linux-gnu:${LD_LIBRARY_PATH} export LD_LIBRARY_PATH=/home/lzq/forQt/debian-11-linux-rootfs/usr/lib/aarch64-linux-gnu:${LD_LIBRARY_PATH} export PATH=/home/lzq/forQt/debian-11-linux-rootfs/usr/lib/aarch64-linux-gnu:${PATH} export PATH=/home/lzq/forQt/debian-11-linux-rootfs/usr/lib:${PATH} export PATH=/home/lzq/forQt/debian-11-linux-rootfs/lzq:${PATH} ./configure -prefix /qtbuild \ -opensource \ -c++std c++14 \ -confirm-license \ -nomake examples \ -nomake tests \ -sysroot /home/lzq/forQt/debian-11-linux-rootfs \ -xplatform linux-aarch64-gnu-g++ \ -no-opengl \ -release \ -qpa xcb \ -xcb \ -no-strip \ 'QMAKE_LFLAGS += -L/lib/aarch64-linux-gnu -ldl -lrt -lz -lpng -lpthread -lmd -lXau -lbsd -lXdmcp'
本地环境和根文件系统需要安装的环境
这里先列出所有需要安装的库和工具,这只是我进行交叉编译时发现需要安装的,读者实际操作时遇到缺失的库安装即可,后面还会有要安装的库
本地x86环境和arm根文件系统都要安装,避免出现奇怪的依赖问题
flex gperf bison pkg-config zlib1g zlib1g-dev libgcrypt20 libgcrypt20-dev liblz4-dev liblz4-1 libzstd-dev libzstd1 liblzma-dev liblzma5 fontconfig libfontconfig1-dev dbus libdbus-1-dev libsystemd0 libsystemd-dev libnss3 libnss3-dev libgl1-mesa-dev pkg-config g++ gcc make
本地x86环境安装Python
注意要在上面的环境安装完成后再安装Python,否则后面会提示python缺少库,安装Python主要是为了编译qtwebengine,不同版本的qt对Python的版本也不一样,qt5.12.12要求的版本是2.7.5及其以后,我这里采用2.7.18
需要下载源码在本地x86环境编译安装,根文件系统不需要操作,【网络链接】Python2.7.18下载链接
目录权限的设置
到目前为止,所有的环境已经搭建完毕,还需要进行关键的一步,将qt源码所在目录和根文件系统所在目录的权限设置为777
sudo chmod -R 777 xxxx
由于没有设置这个权限,导致需要使用sudo make执行,此时会导致链接库的路径错误,编译qtwebengine的时候会出问题
执行configure脚本并编译源码
下面我记录一些执行configure脚本或者编译过程中会出现的问题,有则改之
问题 : 执行脚本遇到语法报错
/home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/include/QtCore/../../src/corelib/global/qendian.h:333:35: error: 'numeric_limits' is not a member of 'std' 333 | { return QSpecialInteger(std::numeric_limits::min()); } 【解决方法】 对应头文件或源文件增加 #include 这个问题在后续的编译中可能还会出现,目前我只会一个个改,有好的方法再更新
问题 : 缺少必要库
WARNING: Python version 2 (2.7.5 or later) is required to build QtWebEngine. 源码下载链接 https://www.python.org/downloads/release/python-2718/ WARNING: gperf is required to build QtWebEngine. 源码下载链接 http://ftp.gnu.org/gnu/gperf/ WARNING: bison is required to build QtWebEngine. WARNING: flex is required to build QtWebEngine. WARNING: host pkg-config not found 【解决方法】 在根文件系统下安装需要的库(包管理器没有就下载源码自己编译安装,注意版本), 安装完毕后,在根文件系统下创建一个单独的目录,在里面创建若干个软链接, 这些软链接分别指向这些安装好的二进制文件,注意在本地系统环境执行创建软链接命令 安完一个看一下warning有没有消失(记得删除config.cache再执行脚本) python这里有点奇怪 我在根文件系统下编译源码安装没有作用,在本地系统下(x86)编译安装反而没有warning了 注意前面提到的脚本需要增加一行export PATH=/home/lzq/forQt/linux-rootfs/lzq/:${PATH},就是存放软链接的那个路径
问题 : webengine需要的库缺少
Required system libraries:
fontconfig ........................... no
dbus ................................. no
nss .................................. no
khr .................................. no
glibc ................................ yes
【解决方法】根文件系统中上面这些库或者其对应的.pc文件,pc文件需要安装库对应的-dev包,
可以用 apt-cache search xxx 查询
例如
apt-cache search fontconfig | grep dev //会输出一个列表其中包含libfontconfig1-dev
apt install libfontconfig1-dev
接下来是khr,这个不是库是头文件,查看config.cache文件可知缺少的文件,直接执行 apt-get install libgl1-mesa-dev
还有最后一步操作 将根文件系统/usr/lib/目录下的aarch64-linux-gnu整个文件拷贝到 x86本地环境/lib目录下
然后将x86本地环境 /lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 创建一个软链接放置到/lib下
这里要注意,如果在后面的操作中arm根文件系统又安装了新库,那么还得再操作一遍,保证能链接到新的库文件和找到新的.pc文件!!!!!!!!!!!!!!!!
进行完以上操作,webengine要求的系统库就都为yes了
问题 : 编译过程中报错找不到库
/home/lzq/forQt/gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/5.4.1/../../../../aarch64-linux-gnu/bin/ld: warning: libz. so.1, needed by /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/lib/libQt5Core.so, not found (try using -rpath or -rpath-link) /home/lzq/forQt/gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/5.4.1/../../../../aarch64-linux-gnu/bin/ld: warning: libdl. so.2, needed by /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/lib/libQt5Core.so, not found (try using -rpath or -rpath-link) /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/lib/libQt5Core.so: undefined reference to `dlerror@GLIBC_2.17' /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/lib/libQt5Core.so: undefined reference to `uncompress' /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/lib/libQt5Core.so: undefined reference to `compress2' /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/lib/libQt5Core.so: undefined reference to `dlclose@GLIBC_2.17' /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/lib/libQt5Network.so.5: undefined reference to `inflate' /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/lib/libQt5Network.so.5: undefined reference to `inflateInit2_' /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/lib/libQt5Network.so.5: undefined reference to `inflateEnd' /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/lib/libQt5Core.so: undefined reference to `dlopen@GLIBC_2.17' /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/lib/libQt5Core.so: undefined reference to `dlsym@GLIBC_2.17' collect2: error: ld returned 1 exit status make[4]: *** [Makefile:99:../../../bin/canbusutil] 错误 1 make[4]: 离开目录“/home/lzq/forQt/qt-everywhere-src-5.12.12/qtserialbus/src/tools/canbusutil” 【解决方法】这里看报错可知,编译时需要连接 libz 和 libdl,还有别的库要链接,如下: 在上面的脚本后增加参数 'QMAKE_LFLAGS += -L/lib/aarch64-linux-gnu -lrt -lz -ldl -lpng -lpthread'
问题 : 没有编译qtwebengine
问题 : 没有编译出libqxcb库
arm根文件操作系统下需要安装的库,宁多勿少 libx11-xcb-dev libx11-xcb1 libdrm-dev libdrm2 libdrm-common libxcomposite1 libxcomposite-dev libxcursor1 libxcursor-dev libxi-dev libxi6 libxtst-dev libxtst6 xcb-proto libxkb* 前面的脚本后面还需要增加参数 -qpa xcb\ -xcb \ 'QMAKE_LFLAGS += -L/lib/aarch64-linux-gnu -ldl -lmd -lXau -lbsd -lXdmcp'
问题 : 编译qtwebengine遇到的问题
1.编译过一遍会发现qtwebengine并没有被编译,需要单独编译,不知道为什么很奇怪
【解决方法】
cd /home/lzq/forQt/qt-everywhere-src-5.12.12/qtwebengine
执行下列命令
export LD_LIBRARY_PATH=/home/lzq/forQt/debian-11-linux-rootfs/usr/lib:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH=/home/lzq/forQt/debian-11-linux-rootfs/lib/aarch64-linux-gnu:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH=/home/lzq/forQt/debian-11-linux-rootfs/usr/lib/aarch64-linux-gnu:${LD_LIBRARY_PATH}
export PATH=/home/lzq/forQt/debian-11-linux-rootfs/usr/lib/aarch64-linux-gnu:${PATH}
export PATH=/home/lzq/forQt/debian-11-linux-rootfs/usr/lib:${PATH}
export PATH=/home/lzq/forQt/debian-11-linux-rootfs/lzq:${PATH}
export PKG_CONFIG_PATH=/home/lzq/forQt/debian-11-linux-rootfs/usr/lib/aarch64-linux-gnu/pkgconfig
然后 /home/lzq/forQt/qt-everywhere-src-5.12.12/qtbase/bin/qmake 生成Makefile 并make
2. 编译错误 exception_handler.cc:141:65: error: no matching function for call to 'max(int, long int)' static const unsigned kSigStackSize = std::max(16384, SIGSTKSZ);
【解决方法】
修改exception_handler.cc static const unsigned kSigStackSize = std::max(16384, SIGSTKSZ);
修改为如下: long int lzq_temp = 16384; static const unsigned kSigStackSize = std::max(lzq_temp, SIGSTKSZ);
3. ../../3rdparty/chromium/ui/gfx/x/x11.h:23:26: fatal error: X11/Xlib-xcb.h: No such file or directory 会出现一些这样找不到头文件的错误
【解决方法】
apt-file search xxxxx.h //使用这个神器 通过头文件找对应的库文件,然后安装即可
根文件系统需要安装 libxrandr-dev libxdamage-dev libxdamage1
//把头文件的路径改一下,前面加上arm根文件系统路径
// Xlib.h defines base types so it must be included before the less
// central X11 headers can be included.
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/Xlib.h>
// And the rest so that nobody needs to include them manually...
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/X.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/XKBlib.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/Xatom.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/Xcursor/Xcursor.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/Xlib-xcb.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/Xregion.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/Xutil.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/cursorfont.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/XI2.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/XInput.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/XInput2.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/XIproto.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/XShm.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/XTest.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/Xfixes.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/Xrandr.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/Xrender.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/record.h>
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/shape.h>
#if defined(USE_XSCRNSAVER)
#include </home/lzq/forQt/debian-11-linux-rootfs/usr/include/X11/extensions/scrnsaver.h>
#endif
4. 应该会遇到一个编译问题,还是库链接的问题 /home/lzq/forQt/qt-everywhere-src-5.12.12/qtwebengine/src/process编译错误
【解决方法】
LIBS 后面再加上这些库的链接 -lrt -lz -ldl -lpng -lnssutil3 -lsmime3 -lplds4 -lplc4 -lnspr4 -lexpat -lfontconfig -lnss3 -lresolv -ldbus-1 -lfreetype -luuid -lsystemd
-lbrotlidec -llzma -lzstd -llz4 -lgcrypt -lbrotlicommon -lgpg-error -lX11 -lX11-xcb -lxcb -lXcomposite -lXcursor -lXdamage -lXext -lXfixes -lXi -lXrender -lXtst
注意事项再总结
1.qt源码目录设置777权限 2.安装库,尽量本地x86环境和arm根文件系统环境都安装 3.确保arm库向本地x86环境拷贝的库都是最新最全的,和arm根文件系统环境保持一致