3. Qt环境搭建–交叉编译

在配置交叉编译环境之前,我们需要了解一下X86和ARM架构。

X86架构是微处理器执行的计算机语言指令集,指一个intel通用计算机系列的标准编号缩写,也标识一套通用的计算机指令集合。 我们常用的PC,笔记本就属于X86架构的计算机。这种架构的计算机上又通常运行着三种操作系统,即是Windows、Linux和macOS。

ARM架构是一个32位精简指令集(RISC)处理器架构,其广泛地使用在许多嵌入式系统中。 我们的LubanCat就是基于ARM架构的计算机。

由于x86和ARM架构最底层的指令集不同,所以两个平台的程序不能通用,需要分开编译,甚至是编译的工具链都不一样。

第一章节有介绍到GNU工具链,这套工具链就分别提供X86和ARM的编译工具。 由于我们日常的开发习惯,ARM架构的计算机资源等等因素,我们通常会在X86架构的平台上编写代码并编译然后放到ARM上运行。 所以就需要搭建交叉编译环境。

什么是交叉编译环境呢?简单点说就是在一个架构的处理器上能编译在另一个架构上运行的程序的环境。 再简单点来说,就是在X86架构下安装ARM的编译工具。

我们现在使用的工具链叫:arm-linux-gnueabihf-gcc,版本8.3.0。

3.1. 安装交叉编译工具链

首先安装交叉编译器:arm-linux-gnueabihf-gcc,版本8.3.0。 为什么要选择那么高的版本呢,因为作者测试时使用4.9.3版本编译器编译出来的Qt App在Debian系统上运行时会报一个错误:

relocation error: /home/debian/qt-app/abc: symbol _ZTVN10__cxxabiv120__si_class_type_infoE version Qt_5 not defined in file libQt5Core.so.5 with link time reference

经过Google搜索答案,大致是说编译器版本不同导致的错误,我查看了Debian系统中的 libQt5Core.so.5.11.3 库的信息,发现它的编译器版本是比较高的,所以为了一致性,我也使用了arm-linux-gnueabihf-gcc 8.3.0 版本的编译器。

# strings libQt5Core.so.5.11.3 |grep GCC

GCC_3.4
GCC_3.5
Qt 5.11.3 (arm-little_endian-ilp32-eabi-hardfloat shared (dynamic) release build; by GCC 8.3.0)
This is the QtCore library version Qt 5.11.3 (arm-little_endian-ilp32-eabi-hardfloat shared (dynamic) release build; by GCC 8.3.0)

野火提供 build-gcc.sh 脚本一键安装 arm-linux-gnueabihf-gcc 8.3.0版本编译器

build-gcc.sh 脚本内容如下:

#!/bin/sh

HOST=arm-linux-gnueabihf
SCRIPT_PATH=$(pwd)

#修改源码包解压后的名称
MAJOR_NAME=gcc-arm-linux-gnueabihf

#修改需要下载的源码版本前缀和后缀
OPENSRC_VER_PREFIX=8.3
OPENSRC_VER_SUFFIX=.0

PACKAGE_NAME=${MAJOR_NAME}-${OPENSRC_VER_PREFIX}${OPENSRC_VER_SUFFIX}

#定义压缩包名称
COMPRESS_PACKAGE=gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf.tar.xz

#定义编译后安装--生成的文件,文件夹位置路径
INSTALL_PATH=/opt/${PACKAGE_NAME}

#无需修改--下载地址
DOWNLOAD_LINK=https://developer.arm.com/-/media/Files/downloads/gnu-a/8.3-2019.03/binrel/${COMPRESS_PACKAGE}

#下载源码包
do_download_src () {
   echo "\033[1;33mstart download ${COMPRESS_PACKAGE}...\033[0m"
   if [ ! -f "${COMPRESS_PACKAGE}" ];then
      if [ ! -d "${PACKAGE_NAME}" ];then
        wget -c ${DOWNLOAD_LINK}
      fi
   fi
   echo "\033[1;33mdone...\033[0m"
}

#解压源码包
do_tar_package () {
   echo "\033[1;33mstart unpacking the ${PACKAGE_NAME} package ...\033[0m"

   mkdir -p ${INSTALL_PATH}

   if [ ! -d "${PACKAGE_NAME}" ];then
      tar -xf ${COMPRESS_PACKAGE} -C ${INSTALL_PATH} --strip-components=1
   fi
   echo "\033[1;33mdone...\033[0m"
}

#删除下载的文件
do_delete_file () {
   cd ${SCRIPT_PATH}
   if [ -f "${PACKAGE_NAME}" ];then
      sudo rm -f ${PACKAGE_NAME}
   fi
}

do_download_src
do_tar_package
# do_delete_file

exit $?

整个脚本的核心就是使用wget命令将arm-linux-gnueabihf-gcc v8.3.0的文件下载到本地, 然后通过tar解压到指定的安装目录(/opt/${PACKAGE_NAME}, 实际上就是/opt/gcc-arm-linux-gnueabihf-8.3.0目录下)。

我们直接运行脚本即可下载并安装arm-linux-gnueabihf-gcc 8.3.0 版本的交叉编译器,后续的编译都是要该编译器进行。

执行脚本的过程:

# sudo ./build-gcc.sh

start download gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf.tar.xz...
--2020-03-18 11:04:11--  https://developer.arm.com/-/media/Files/downloads/gnu-a/8.3-2019.03/binrel/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf.tar.xz
正在解析主机 developer.arm.com (developer.arm.com)... 23.41.45.203
正在连接 developer.arm.com (developer.arm.com)|23.41.45.203|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 302 Moved Temporarily
位置:https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-a/8.3-2019.03/binrel/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf.tar.xz [跟随至新的 URL]
--2020-03-18 11:04:12--  https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-a/8.3-2019.03/binrel/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf.tar.xz
正在解析主机 armkeil.blob.core.windows.net (armkeil.blob.core.windows.net)... 52.239.137.100
正在连接 armkeil.blob.core.windows.net (armkeil.blob.core.windows.net)|52.239.137.100|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度: 256094408 (244M) [application/octet-stream]
正在保存至: “gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf.tar.xz”

gcc-arm-8.3-2019.0   0%[                    ] 167.51K  22.5KB/s    剩余 3h 5m ^C

start unpacking the arm-linux-gnueabihf-8.3.0 package ...
done...

安装完成之后,/opt/目录下就会存在gcc-arm-linux-gnueabihf-8.3.0

# ls /opt
gcc-arm-linux-gnueabihf-8.3.0

3.2. 导出环境变量

如果你的系统本身存在多个gcc-arm-linux-gnueabihf编译器的话,没有关系, 因为gcc-arm-linux-gnueabihf-8.3.0只是用来编译Qt,默认也没有启用。

如果想要使用gcc-arm-linux-gnueabihf-8.3.0,需要先导出环境变量,具体操作如下:

export PATH=/opt/gcc-arm-linux-gnueabihf-8.3.0/bin:$PATH

3.3. 输入命令验证版本

arm-linux-gnueabihf-gcc -v

若环境变量设置正确,则会出现以下信息

➜  ~ arm-linux-gnueabihf-gcc -v
使用内建 specs。
COLLECT_GCC=arm-linux-gnueabihf-gcc
COLLECT_LTO_WRAPPER=/opt/gcc-arm-linux-gnueabihf-8.3.0/bin/../libexec/gcc/arm-linux-gnueabihf/8.3.0/lto-wrapper
目标:arm-linux-gnueabihf
配置为:/tmp/dgboter/bbs/rhev-vm8--rhe6x86_64/buildbot/rhe6x86_64--arm-linux-gnueabihf/build/src/gcc/configure --target=arm-linux-gnueabihf --prefix= --with-sysroot=/arm-linux-gnueabihf/libc --with-build-sysroot=/tmp/dgboter/bbs/rhev-vm8--rhe6x86_64/buildbot/rhe6x86_64--arm-linux-gnueabihf/build/build-arm-linux-gnueabihf/install//arm-linux-gnueabihf/libc --with-bugurl=https://bugs.linaro.org/ --enable-gnu-indirect-function --enable-shared --disable-libssp --disable-libmudflap --enable-checking=release --enable-languages=c,c++,fortran --with-gmp=/tmp/dgboter/bbs/rhev-vm8--rhe6x86_64/buildbot/rhe6x86_64--arm-linux-gnueabihf/build/build-arm-linux-gnueabihf/host-tools --with-mpfr=/tmp/dgboter/bbs/rhev-vm8--rhe6x86_64/buildbot/rhe6x86_64--arm-linux-gnueabihf/build/build-arm-linux-gnueabihf/host-tools --with-mpc=/tmp/dgboter/bbs/rhev-vm8--rhe6x86_64/buildbot/rhe6x86_64--arm-linux-gnueabihf/build/build-arm-linux-gnueabihf/host-tools --with-isl=/tmp/dgboter/bbs/rhev-vm8--rhe6x86_64/buildbot/rhe6x86_64--arm-linux-gnueabihf/build/build-arm-linux-gnueabihf/host-tools --with-arch=armv7-a --with-fpu=neon --with-float=hard --with-arch=armv7-a --with-pkgversion='GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36)'
线程模型:posix
gcc 版本 8.3.0 (GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36))

到这里交叉编译工具链就已经配置好了。