最近项目需要,qt源码需要交叉编译,以前对这块不是很熟悉,从开始的一脸懵逼到最终成功编译出qt源码库,还是有了很多收获,分享给大家,希望对大家有所帮助。
交叉编译详解
还是先说交叉编译的思想。这里我以为我的项目经历为例给大家讲解。
说到交叉编译,这里要给大家普及两个概念。
构建机平台,这个意思是说要在那台机器进行交叉编译,我自己的项目中的构建平台是x86_64+银河麒麟v10。目标平台,这个更好理解了,就是编译出来的动态库要运行在哪个平台上,我自己项目中的目标平台是aarch64+银河麒麟v10。
通过上面的介绍,大家应该基本理解了这两个概念,下面就开始说说交叉交叉编译的思想,交叉编译顾名思义就是在构建机平台上编译出目标机平台的应用程序或者动态库/静态库。很多朋友会问为什么会存在交叉编译呢?因为有这样的场景,很多嵌入式平台因性能不足无法进行大规模软件编译的情况,也存在很多其它的情况,大家可以百度一下能够得到很多理由。
好了上面对交叉编译做了基本的介绍,下面先从本机编译开始说起,本机编译,顾名思义就是本机编译的程序或者动态在本机上运行(也可以在同平台的机器上运行,这里不做过多赘述),这个很好理解,要编译的程序或者动态库依赖的一些头文件或者库文件就在本机系统上,或在默认目录(以linux为例,/usr/include /usr/lib等),另外也可以通过人工指定的方式(例如qt中可以通过LIBS关键字或者INCLUDEPATH等指定库文件路径和头文件路径等)指明依赖的头文件和库文件位置。这个很好理解,对于程序源来说这是很基本的概念。
上面已经详细介绍了本机编译的情况,本机介绍交叉编译,交叉编译的思想与本机编译的思想类似,但是有一些区别,下面一一列出。
必须选择交叉编译器,以arm为例,可以到arm官网:Arm GNU Toolchain Downloads – Arm Developer下载包含符合自己构建平台和目标平台的交交叉编译器,这里是提供的是版本比较新的交叉编译器。如果需要查找历史版本则可以从这两个路径去查找:GNU toolchain releases for Embedded processors (discontinued)和GNU toolchain releases for A-profile processors (discontinued),这二者的区别是一个是嵌入式处理器的交叉编译工具,另外一个是移动设备或者计算能力强的处理的交叉编译工具,如有感兴趣的朋友可以自己查找资料,这里仅简单描述。用户需要根据自己的需求下载对应的交叉编译工具,上面的连接中提供了多种不同的构建和目标平台的交叉编译版本,例如x86_64 Linux hosted cross toolchains表示该编译器是运行在x86_64架构上的;AArch64 GNU/Linux target表示是编译出的目标程序是运行在aarch64架构上且依赖的是linux操作系统的。也有一部分是与操作系统无关,我自己的理解是编译器中的没有依赖操作系统相关的接口。交叉编译过程中依赖的头文件和库需要从目标机上安装,下载到构建机使用,不能使用本机的头文件和库文件,因为构建机的库文件与目标机的库文件由于架构不同,往往不能通用。头文件在不同的平台下接口名称可能也不一样,为了保险起见,最好编译程序所依赖的头文件和库文件最好从平台拉取到本地,然后手动指定头文件和库文件的路径进行编译。这里我在交叉编译qt5.15.2的源码时,是通过sysroot关键字来制出依赖的头文件和库文件路径,下面会详细介绍,这里仅介绍方法。需要注意的是交叉编译过程中依赖的头文件和库文件需要在目标机上安装,然后下载到本地,直接在构建机上安装是不对的。
上面涉及的两个步骤都做完后就可以开始交叉编译了,下面以qt5.15.2为例,详细介绍下是如何完成交叉编译的。
Qt 5.15.2交叉编译实例
构建平台: x86_64+银河麒麟v10
目标机平台:aarch64+银河麒麟v10
按照第一节讲解的步骤下载了9.2版本(gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu)的交叉编译器,同时将目标机的头文件和库文件下载到构建机。
步骤一
qt源码安装依赖很多库,需要提前在目标机(aarch64+银河麒麟V10)上安装,依赖库如下:
sudo apt install build-essential cmake unzip pkg-config gfortran sudo apt build-dep qt5-qmake libqt5gui5 libqt5webengine-data libqt5webkit5 libudev-dev libinput-dev libts-dev libxcb-xinerama0-dev libxcb-xinerama0 gdbserver sudo apt install libxcb-randr0-dev libxcb-xtest0-dev libxcb-shape0-dev libxcb-xkb-dev
附加包如下所示,根据需要安装即可。
# additional (multimedia) packages sudo apt install libjpeg-dev libpng-dev libtiff-dev sudo apt install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev sudo apt install libxvidcore-dev libx264-dev openjdk-8-jre-headless # audio packages sudo apt install libopenal-data libsndio7.0 libopenal1 libopenal-dev pulseaudio # bluetooth packages sudo apt install bluez-tools sudo apt install libbluetooth-dev # gstreamer (multimedia) packages sudo apt install libgstreamer1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
步骤二
设置符号连接,这里我自己的理解是交叉编译工具需要一些特定的符号链接用于在编译过程中使用。需要下载SSymlinkerbash 脚本,这个脚本我从一[参考链接]中看到,但是按照他的方法没有下载下来,这里我把这个脚本上传到这里,供大家下载。脚本下载地址请在文章顶部提供的链接下载。下载脚本后执行如下操作,通过这些操作你能大概明白他的含义:
sudo chmod +x SSymlinker ./SSymlinker -s /usr/include/arm-linux-gnueabihf/asm -d /usr/include ./SSymlinker -s /usr/include/arm-linux-gnueabihf/gnu -d /usr/include ./SSymlinker -s /usr/include/arm-linux-gnueabihf/bits -d /usr/include ./SSymlinker -s /usr/include/arm-linux-gnueabihf/sys -d /usr/include ./SSymlinker -s /usr/include/arm-linux-gnueabihf/openssl -d /usr/include ./SSymlinker -s /usr/lib/arm-linux-gnueabihf/crtn.o -d /usr/lib/crtn.o ./SSymlinker -s /usr/lib/arm-linux-gnueabihf/crt1.o -d /usr/lib/crt1.o ./SSymlinker -s /usr/lib/arm-linux-gnueabihf/crti.o -d /usr/lib/crti.o
至此目标机(aarch64+银河麒麟V10)已经设置完成。
步骤三(配置构建机)
构建机也需要安装一些基础软件包,安装的软件包如下所示:
sudo apt update sudo apt dist-upgrade sudo apt install build-essential cmake unzip gfortran sudo apt install gcc git bison python gperf pkg-config gdb-multiarch wget sudo apt-get -y install gcc g++ gperf flex texinfo gawk bison openssl pigz libncurses-dev autoconf automake tar figlet
设置目录结构,这个目录结构用来存放从目标机下载的库文件和头文件,供交叉编译器使用。
命令如下:
sudo mkdir ~/rk-qt sudo mkdir ~/rk-qt/build sudo mkdir ~/rk-qt/tools sudo mkdir ~/rk-qt/sysroot sudo mkdir ~/rk-qt/sysroot/usr sudo mkdir ~/rk-qt/sysroot/opt sudo chown -R consys:consys ~/rk-qt cd ~/rk-qt
接下来下载qt5.15.2源码到构建机,并解压缩到本地,目录自己指定即可。另外从arm官网下载的交叉编译工具也解压缩到本地。上述步骤完成,将交叉编译工具的bin目录添加到环境变量PATH中,方法如下:
vim .bashrc
在文件末尾添加export PATH=交叉编译工具bin目录:$PATH(以我的项目为例:/home/consys/tool/gcc-linaro-11.3.1-2022.06-x86_64_aarch64-linux-gnu/bin:$PATH)
source .bashrc 或者直接关闭该终端,再重新打开终端执行export命令查看PATH环境变量是否添加成功。
需要注意的是交叉编译工具bin目录下存放的是aarch64-linux-gnu-gcc。xxxxxxx-g++等工具,别找错目录了
修改/home/consys/tool/qt-everywhere-src-5.15.2/qtbase/mkspecs目录下的qmake配置文件,我因为是要交叉编译aarhch+银河麒麟v10的程序,所以我选择的是修改linux-aarch64-gnu-g++目录下的qmake.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
修改为:
QMAKE_CC = aarch64-none-linux-gnu-gcc //注意这里的名字与交叉编译工具bin目录下的名字一致 QMAKE_CXX = aarch64-none-linux-gnu-g++ //注意这里的名字与交叉编译工具bin目录下的名字一致 QMAKE_LINK = aarch64-none-linux-gnu-g++ //注意这里的名字与交叉编译工具bin目录下的名字一致 QMAKE_LINK_SHLIB = aarch64-none-linux-gnu-g++ //注意这里的名字与交叉编译工具bin目录下的名字一致
# modifications to linux.conf
QMAKE_AR = aarch64-none-linux-gnu-ar cqs //注意这里的名字与交叉编译工具bin目录下的名字一致 QMAKE_OBJCOPY = aarch64-none-linux-gnu-objcopy //注意这里的名字与交叉编译工具bin目录下的名字一致 QMAKE_NM = aarch64-none-linux-gnu-nm -P //注意这里的名字与交叉编译工具bin目录下的名字一致 QMAKE_STRIP =aarch64-none-linux-gnu-strip //注意这里的名字与交叉编译工具bin目录下的名字一致
从目标平台下载依赖的头文件、库文件和配置文件等。命令如下所示:
cd ~/rk-qt
rsync -avz --rsync-path="sudo rsync" --delete consys@192.168.0.174:/lib sysroot rsync -avz --rsync-path="sudo rsync" --delete consys@192.168.0.174:/usr/include sysroot/usr rsync -avz --rsync-path="sudo rsync" --delete consys@192.168.0.174:/usr/lib sysroot/usr rsync -avz --rsync-path="sudo rsync" --delete consys@192.168.0.174:/opt/vc sysroot/opt
注意:rsync命令研究一下,只要从目标机下载到本机即可
将交叉编译依赖的文件下载到本地后执行下面命令,恢复链接
sudo chmod +x sysroot-relativelinks.py ./sysroot-relativelinks.py sysroot
编译qt源码需要先执行configure脚本,然后执行make 和make install命令,但是在交叉编译过程中有一项千万要在执行configure脚本之前执行,即配置PKG_CONFIG_PATH环境变量,这个环境变量指明一些后缀为pc的文件,这些pc文件指令了依赖库的路径,是非常重要的一个步骤,我很长时间编译出错就是因为在执行configure后,才添加的这个环境变量,但是configure执行过程中如果不配置该环境变量,qt就认为一些依赖库不存在,但是其实依赖库是存在的,因为configure执行过程中会根据该环境变量,影响一些生成的文件中的内容,造成报错或者编译跳过问题。我遇到的问题是编译qwebegine模块时一直提示nns模块不存在,但是从目标机下载头文件和库文件中nss已经存在,被折腾了好久。到这里已经基本配置完成了,但是还是要啰嗦一下,就是最好写一个autoconfigre.sh脚本放到qt5.15.2的目录下,与configure脚本同级即可,这个就自己研究参数把,我把自己使用的autoconfigure.sh脚本放在下面,供参考使用。
#!/bin/sh
./configure -release -qt-libjpeg -qt-libpng -qt-zlib -qt-pcre -xplatform linux-aarch64-gnu-g++ -sysroot ~/rk-qt/sysroot -prefix /home/consys/tool/aarch64-qt-output -opensource -confirm-license -skip qtscript -skip qtwayland -nomake tests -skip qtlocation -skip qt3d -no-opengl -skip qtcanvas3d -skip qtpurchasing -make libs -pkg-config -v -recheck -L$HOME/rk-qt/sysroot/usr/lib/aarch64-linux-gnu -I$HOME/rk-qt/sysroot/usr/include/aarch64-linux-gnu
最后赋予autoconfigure.sh脚本执行权限,执行该脚本就可以了
sudo chmod +x autoconfigure.sh
./autoconfiure.sh
make
make install
注意1:交叉编译qt5.15.2时会消耗大量的内存,我开始使用的笔记本交叉编译(配置为8G内存+9G交换空间),在上面的配置中依然内存不够,造成编译不通过,最后找了一台内存超大的机器后菜编译通过。
注意2:autoconfigure.sh中指定了prefix(/home/consys/tool/aarch64-qt-output),这个目录需要注意并不是绝对路径,如果指定了sysroot,那么生成路径就是在sysroot指定的路径下创建prefix路径,即最终的动态库生成路径是(/home/consys/rk-qt/sysroot/home/consys/tool/aarch64-qt-output),这一点不要搞混了。sysroot的作用是提供一个逻辑地址,configure指定该参数后,编译依赖的头文件和库文件会以sysroot指定的路径作为基地址,即交叉编译原本依赖的头文件路径是/usr/include,指定sysroot后,则变成/home/consys/sysroot/usr/include。
交叉编译过程中遇到的问题
按照上面的步骤操作能够避免绝大多数问题,但是还是不可避免的遇到了一些问题,下面将他们一一列出,希望能够给朋友们一些帮助。
问题1
错误信息:编译的过程中提示缺少libdl.so 和librt.so等动态库,提示这些动态库的下符号找不到,例如dlOpen等。
修改方法:打开报错目录的makefile文件,手动在LIBS关键字中添加对应的so,缺少哪个就添加哪个,例如-ldl -lrt 如果名字不标准的,直接添加路径+so名称即可。
问题2
错误信息:jidctfst-neon.c:88:29: 错误: 用'int64x1_t'初始化'int_64' {或称‘long int’}时类型不兼容
int64_t left_ac_bitmap = vreinterpret_s64_s16(bitmap))
修改方法:此处是代码问题,需要修改代码,=号量表类型不一致,问题代码为:
int64_t left_ac_bitmap = vreinterpret_s64_s16(vget_low_s16(bitmap));
修改为
int64_t left_ac_bitmap = vget_lane_s64(vreinterpret_s64_s16(vget_low_s16(bitmap)),0);
其它报相同的问题按照相同方式修改即可。
问题3
错误信息:错误: 'vaddq_s8'的第2个实参类型不兼容
修改方法:代码问题,需要修改代码,实参类型与虚参类型不一致,问题代码为:
vaddq_s8(cols_01_s8, vdupq_n_u8(CENTERJSAMPLE)));
修改为
vaddq_s8(cols_01_s8, vreinterpretq_s8_u8(vdupq_n_u8(CENTERJSAMPLE))));
使用方法
交叉编译完成后,到生成的目录中将文件上传到目标平台,然后在目标平台执行如下命令:
echo /usr/local/qt5.15/lib | sudo tee /etc/ld.so.conf.d/qt5.15.conf sudo ldconfig
问题4
一定要重视config.log的作用,在执行configure过程中,config.log脚本会记录过程中出现中的错误,要沉下心下来仔仔细细的研究config.log,根据configure的检测结果通过搜索关键字的方式找到对应日志的区域,一行一行的研究,这里举两个例子:
configure检测qtwenengine依赖模块,模块缺失,造成qtwebengine无法编译,
xcomposite...................................no
但是该库的头文件和库文件肯定是存在的,然后打开config.log文件,搜索关键字“xcomposite”,然后找到如下记录。
looking for library webengine-xcomposite Trying source 0 (type pkgConfig) of library webengine-xcomposite ... + PKG_CONFIG_SYSROOT_DIR=/home/consys/rk-qt/sysroot PKG_CONFIG_LIBDIR=/home/consys/rk-qt/sysroot/usr/lib/aarch64-linux-gnu/pkgconfig:/home/consys/rk-qt/sysroot/usr/lib/pkgconfig:/home/consys/rk-qt/sysroot/usr/share/pkgconfig /usr/bin/pkg-config --exists --silence-errors xcomposite (1)pkg-config did not find package. => source produced no result. test config.qtwebengine_buildtools.libraries.webengine-xcomposite FAILED
通过上面的记录日志发现是xcomposite包未找到,然后原因是命令(1)执行失败,把该命令拷贝下来,先通过export命令添加临时环境变量,然后执行该命令,发现没有任何输出,然后修改该命令的参数选项为pkg-config --exists --print-errors,再次执行该命令,出现了错误,错误原因是找不到xproto.pc,这时候在平板上搜索该pc文件,发现其在/usr/share目录下,但是构建机从平板拉取的目录不包含该目录,至此问题原因找到了,需要从平板拉取/usr/share目录。
问题5
这是一个很重要的问题,编译出qt动态库后,移植到平板上运行测试程序,程序报错“qt.qpa.plugin:could not load the qt platform plugin "xcb" in ""”,经定位是编译出的动态库目录下/plugins/platforms不存在linuxqxcb.so,这个so是一个关键库,要正常运行qt界面程序一定是需要这个关键库的。
解决方法:在执行configure脚本中添加参数-xcb 只有这样才会编译出libqxcb.so。
但是添加该选项后的,再执行configure脚本过程中出现如下错误:
xcb was enabled,but the pre-condition'feature.thread && libs.xcb && test.xcb_syslibs && features.xkbcommon-x11' failed
要解决这个问题,还是要参照问题4提供的手段 仔细研究config.log,这里同样把报错的列出来
............................./bin/ld:waring:libphread.so.0 needed by...................
...............................................................下面也是类似的报错,大意就是动态库找不到,,造成的链接时符号未定义。
经过确认这些动态库都是存在的,找不到的原因最后也没有找到,即使通过添加QMAKE_LFLAG指定动态库搜索路径,依然不能解决问题。没有办法的情况下,在qmake.conf中通过添加LIBS关键字,将所有找不到的so一一列出,这样才最终解决该问题。
xcb编译依赖的库如下:
libfontconfig1-dev
libfreetype6-dev
libx11-dev
libx11-xcb-dev
libxext-dev
libxfixes-dev
libxi-dev
libxrender-dev
libxcb1-dev
libxcb-glx0-dev
libxcb-keysyms1-dev
libxcb-image0-dev
libxcb-shm0-dev
libxcb-icccm4-dev
libxcb-sync0-dev
libxcb-xfixes0-dev
libxcb-shape0-dev
libxcb-randr0-dev
libxcb-render-util0-dev
libxcb-xinerama-dev
libxkbcommon-dev
libxkbcommon-x11-dev
或者sudo apt-get install libxcb*
问题6
将动态库拷贝到平板上后,起初是通过ftp工具传递的,不知道什么原因到导致的某些目录下文件会丢失,这些丢失的问题目测都是快捷方式。造成启动的过程中报错,
解决方法:采用rysn命令上传整个动态库目录
问题7
将动态库步骤好后,运行程序,程序可以正常启动,但是所有字符均不显示,图片可以正常显示。
解决方法:
export QT_QPA_FONTDIR=/usr/share/fonts/kyfonts(注意这里要填写自己系统的字体文件所在路径),然后重新启动程序文字可以正常显示。
问题8
很多人对交叉编译好的qt动态库,拷贝到新平台后如何部署有疑问,我相信大家花点时间都可以解决,主要是解决动态库绑定问题,这里列出我的启动脚本,供大家参考:
QT_INSTALL_DIR=/path/aarch64-qt-output
export LD_LIBRARY_PATH=$QT_INSTALL_DIR/lib:$LD_LIBRARY_PATH
export PATH=$QT_INSTALL_DIR/bin:$PATH
export QT_PLUGIN_PATH=$QT_INSTALL_DIR/plugins
export QT_QPA_FONTDIR=/usr/share/fonts/kyfonts
./app
最后运行程序即可。
参考链接:
树莓派4B——ubuntu20.04交叉编译QT5.15.2,_交叉编译qt 5.15_Li丶Chong的博客-CSDN博客
好文链接
发表评论