交叉编译实践-Qt应用程序交叉编译【用cmake工具编译】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Shado_walker/article/details/78594938

/****************************************************************************************/

/*           获取更多干货技能,点击 [这里-小编文章列表] 主页左侧扫码关注公众号             */ 

/***************************************************************************************/


        上篇介绍了交叉编译的基本原理,以及交叉编译一个简单的HelloWorld程序,这篇将介绍如何交叉编译Qt程序。

由于Qt程序依赖的底层以及第三方的相关库太多(比如libGL.so库为OpenGL库,libX11.so库为系统图像库等等),因此在交叉编译时会出现各种问题,下面将其主要的问题归为如下三类:
1. 编译器所依赖的库,在编译链接过程中,找到了宿主机的库了,导致文件格式不对,编译链接不成功,比如Qt编译是依赖于libGL.so库,而这个库在宿主机上的/usr/lib/目录下也存在,导致交叉编译器aarch64-linux-gnu-gcc在找库时,找到了宿主机的x86_64的库,所以肯定是链接不成功的。这里要说明一点:交叉编译所依赖的库,必须是与目标机架构一致的库,即libGL.so必须是ARM aarch64架构的库,不能是宿主机上的库,否则在目标机上运行时怎么链接呢?

2. 编译器在编译链接时,没有找到相应的头文件和库,即头文件和对应库没有在交叉编译器的寻库路径中,导致报错找不到库,如下图所示:

解决方式是:一般将所依赖的相关库,放到安装交叉编译器时生成的/usr/aarch64-linux-gnu/目录中对应bin、include、lib的lib目录中,这种方式就保证了交叉编译器能万无一失的找到库和头文件。还有一种方式是如果Qt用的是cmake编译的,则可以通过如下方式设置其所使用的交叉编译器和寻库路径:
# this is required
SET(CMAKE_SYSTEM_NAME Linux)

# specify the cross compiler
SET(CMAKE_C_COMPILER   /opt/arm/usr/bin/aarch64-linux-gnu-gcc)
SET(CMAKE_CXX_COMPILER /opt/arm/usr/bin/aarch64-linux-gnu-g++)

# where is the target environment 
SET(CMAKE_FIND_ROOT_PATH  /opt/arm/lib /home/joven/lib)

# search for programs in the build host directories (not necessary)
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

# configure Boost and Qt
SET(QT_QMAKE_EXECUTABLE /usr/local/Qt5.6/bin/qmake)
以上牵扯到相关变量的设置,简单介绍解释如下:
    1.CMAKE_SYSTEM_NAME: 即你目标机target所在的操作系统名称,比如ARM或者Linux你就需要写"Linux",如果Windows平台你就写"Windows",如果你的嵌入式平台没有相关OS你即需要写成"Generic",只有当CMAKE_SYSTEM_NAME这个变量被设置了,CMake才认为此时正在交叉编译,它会额外设置一个变量CMAKE_CROSSCOMPILING为TRUE.

    2. CMAKE_C_COMPILER: 顾名思义,即C语言编译器,这里可以将变量设置成完整路径或者文件名,设置成完整路径有一个好处就是CMake会去这个路径下去寻找编译相关的其他工具比如linker,binutils等,如果你写的文件名带有arm-elf等等前缀,CMake会识别到并且去寻找相关的交叉编译器。

    3. CMAKE_CXX_COMPILER: 同上,此时代表的是C++编译器。

    4. CMAKE_FIND_ROOT_PATH: 代表了一系列的相关文件夹路径的根路径的变更,比如你设置了/opt/arm/,所有的Find_xxx.cmake都会优先根据这个路径下的/usr/lib,/lib等进行查找,然后才会去你自己的/usr/lib和/lib进行查找,如果你有一些库是不被包含在/opt/arm里面的,你也可以显示指定多个值给CMAKE_FIND_ROOT_PATH,比如

    5.  CMAKE_FIND_ROOT_PATH_MODE_PROGRAM: 对FIND_PROGRAM()起作用,有三种取值,NEVER,ONLY,BOTH,第一个表示不在你CMAKE_FIND_ROOT_PATH下进行查找,第二个表示只在这个路径下查找,第三个表示先查找这个路径,再查找全局路径,对于这个变量来说,一般都是调用宿主机的程序,所以一般都设置成NEVER

    6. CMAKE_FIND_ROOT_PATH_MODE_LIBRARY: 对FIND_LIBRARY()起作用,表示在链接的时候的库的相关选项,因此这里需要设置成ONLY来保证我们的库是在交叉环境中找的.

    7. CMAKE_FIND_ROOT_PATH_MODE_INCLUDE: 对FIND_PATH()和FIND_FILE()起作用,一般来说也是ONLY,如果你想改变,一般也是在相关的FIND命令中增加option来改变局部设置,有NO_CMAKE_FIND_ROOT_PATH,ONLY_CMAKE_FIND_ROOT_PATH,BOTH_CMAKE_FIND_ROOT_PATH

    8. QT_QMAKE_EXECUTABLE: 指定相应的qmake路径
以上也可以将设置相关变量的代码写到一个toolChain.cmake文件中,这样在执行cmake是带上参数 -DCMAKE_TOOLCHAIN_FILE=./toolChain.cmake 即可(cmake -DCMAKE_TOOLCHAIN_FILE=./toolChain.cmake -DCMAKE_BUILD_TYPE=Release ./)。

3. 报错“/usr/aarch64-linux-gnu/lib/libGL.so.1:对‘XGetVisualInfo’未定义的引用” 等一系列未定义的引用,如下图所示:

从整体报错来看,报错归结为如下图所示:

这些未定义的引用就是上面第一种或第二种错误导致的,找到正确的引用库后,就不会报错了。

搞定以上问题后,基本上编译、链接完成,可执行文件就会生成,交叉编译Qt程序就算大功告成了!!!
编译链接成功截图如下:


但是,好多人可能都没有到编译阶段就已经报错了。。。。。。,可能原因如下:
1. 交叉编译Qt程序的原理没理清
2. Qt的库和Qt的qmake、rcc、moc等工具没有设置好
3. Qt的版本宿主机和目标机不一致
针对以上问题1:Qt应用程序的交叉编译,其原理是通过宿主机上可执行的Qt相关工具程序(比如moc、rcc等),在编译时生成中间文件和资源文件,最终打包成Qt应用程序,但是该应用程序所依赖的Qt动态库又必须是目标机的(动态库必须是ARM aarch64架构的)。
针对以上问题2:给环境变量PATH中必须设置本地Qt可执行文件的路径(也就是Qt的bin目录),这样才能找到moc、rcc等执行相关的操作,也就是说qmake、moc、rcc等在交叉编译是用的工具,必须是x86_64架构的。
针对以上问题3:由于在交叉编译Qt应用程序是,用到了本地的Qt相关工具程序,所以在链接时,最好保持本地的Qt版本和目标机的Qt版本一直(也就是编译时的Qt版本和运行时的Qt版本保持一致)。
总结以上问题:如果在cmake中设置了找库路径,则需要将目标机的Qt的lib库拷贝到设置的路径中,如果在cmake中没有设置找库路径,则有个粗暴的方法,就是将本地的Qt中lib目录换成目标机的lib,确保本地的Qt的bin目录中可执行文件都是x86_64架构,lib目录中的库文件都是ARM aarch64架构的。

这样就基本搞定了Qt应用程序的交叉编译了^_^.



猜你喜欢

转载自blog.csdn.net/Shado_walker/article/details/78594938