【Sipeed D1 Dock Pro】QT初体验
-
前提
想要完成qt交叉编译的功能,会涉及到以下几个重要的组件
- qmake和库文件
qmake对于QT来讲是一个非常重要的工具,编译qt源码时会生成qmake,它对应的平台为qt程序工作的平台(如Windows),因此configure配置时,-platform选项实际上指的是qmake工作的平台。
库文件包含了qt内置功能\函数的实现,在程序链接或运行时生效,-xplatform选项指的是库文件对应的平台(如开发板)。
- 交叉编译工具链
既用于编译目标程序需要用到的库文件,也编译实际项目中的代码文件。
主要过程如下:
- 准备工作
在windows下编译QT,需要安装active perlhttps://www.perl.org/get.html和python,安装过程不在叙述,安装完成后需要将其可执行文件所在目录(一般为bin目录)加入到环境变量中。
需要注意的是perl的目录比较绕,我的目录在用户目录下的AppData\Local\ActiveState\cache\bin,而且不是exe文件,而是bat脚本,最终执行perl.exe文件,因此添加脚本目录或者exe目录到环境变量都行。
在第二次编译时,提到需要安装msvc工具链和ruby。
msvc可以使用visual studio installer安装编译器和Windows sdk,安装完成后使用msvc的环境能执行cl指令即可。
ruby下载安装即可。
下载交叉编译工具链,开发板工具链官方整理的非常好,直接在 https://occ.t-head.cn/community/download?id=4032826170687950848 页面下载然后解压即可,为了后续的流程简单一点,也建议将其bin目录加入到环境变量。
因为是在Windows下编译源码,因此还需要安装g++,这个很多软件都会带有g++工具链(如vs、cygwin64、qt等),这里就不再详述了,g++所在目录页需要加入到环境变量中。这是我用到的g++版本。
- QT源码下载
直接在qt官网( https://www.qt.io/offline-installers ) 下载即可,Windows平台下载zip文件,下载完成之后解压。
在第二次尝试编译时得知需要将解压后的flex.exe所在目录加入到环境变量,目录为压缩包下gnuwin32\bin。
修改工具链位置由于我们的目标程序适用于开发板,因此这里的库必须使用开发板对应的交叉编译工具链来编译,qt源码中并没有预设该工具链,因此需要对现有的工具链文件做一定修改,办法如下:
修改源码目录下qtbase\mkspecs\linux-aarch64-gnu-g++\qmake.conf文件,将其工具链更改为开发板对应的工具链。
修改这个文件是因为我们等会configure配置会用到-xplatform linux-aarch64-gnu-g++选项,该选项指向了这个文件,你也可以自己创建一个文件夹,按照模板填写qmake.conf文件也可以,只需要保证-xplatform能找到新建的 文件即可。
-
Windows下编译QT源码 - 1
这部分是在51假日期间做的尝试,实际上方法不对,导致了在编译过程中出现很多问题,不过当时仅记录了一部分内容,在掉进编译的问题旋涡之后,就没有记录了。正确的编译方式请看《Windows下编译QT源码 - 2》章节。 -
执行编译
在源码文件夹顶层目录下,执行以下指令:
configure.bat -release -static -opensource -nomake tests -nomake examples -no-opengl -skip qtvirtualkeyboard -platform win32-g++ -xplatform linux-aarch64-gnu-g++ -prefix D:\Qt\Qt5.12.10\5.12.10\xuantie_900_mingw_v2.2.5
关于配置选项的说明,可以查看qtbase\config_help.txt文件,这里只简单介绍一下:
先尝试了qt 5.15.1版本(因为我本地的qt是该版本),编译过程中遇到的错误如下:
g++报错,找不到输入文件(g++: fatal error: no input files)
原因是g++在处理转义字符\时,截断了后面后面的参数,需要修改qtbase\qmake\Makefile.unix文件(为啥不是qtbase\qmake\Makefile.win32文件?),去掉QT_VERSION_STR后面的转义字符。修改完成之后,不再报这个错误。
- ::symlink未被申明
查看报错的源码,发现这个函数应该是unix下特有的,我在Windows下编译,但是实际上启用了Q_OS_UNIX的宏。从qtbase\src\corelib\global\qsystemdetection.h文件来看,如果未指定平台,则会默认Unix,而定义Q_OS_WIN宏的条件,是要定义Q_OS_WIN32、Q_OS_WIN64、Q_OS_WINRT其中至少一个宏,而这三个宏在定义条件如下:
#elif !defined(SAG_COM) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY==WINAPI_FAMILY_DESKTOP_APP) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) # define Q_OS_WIN32 # define Q_OS_WIN64 #elif !defined(SAG_COM) && (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) # if defined(WINAPI_FAMILY) # ifndef WINAPI_FAMILY_PC_APP # define WINAPI_FAMILY_PC_APP WINAPI_FAMILY_APP # endif # if defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP # define Q_OS_WINRT # elif WINAPI_FAMILY==WINAPI_FAMILY_PC_APP # define Q_OS_WINRT # else # define Q_OS_WIN32 # endif # else # define Q_OS_WIN32 # endif
Win32、win64看起来应该是在编译的时候传入的参数,最终找到qtbase\mkspecs\common\g++-win32.conf文件中定义了_WIN32宏,这个文件又被qtbase\mkspecs\win32-g++\qmake.conf文件包含,我的编译选线里面指定了-platform win32-g++,按道理这个宏应该在编译的时候已经定义了才对,为什么实际编译的时候被使能的是Q_OS_UNIX宏呢???
51期间的记录仅上述两点,实际上后来我尝试了很多办法,包括直接指定Windows的编译宏等,结局非常凄惨,就和一个谎言需要无数个谎言来掩盖类似,绕开一个编译错误的动作导致了后面出现更多的编译报错,直接钻进了牛角尖。后来还尝试过在Linux下编译,configure确实没有报错,但是编译的时候出现了其他错误,尝试解决也是以失败告终,在这里消耗了大约1天多的时间,直接清空了假期余额,因此这件事情也向后拖了很长时间。
后来在一个周末比较空闲的时间,打算继续研究一下,在查看qtbase\configure.bat文件时,发现以下代码:
if "%PLATFORM:g++=%" == "%PLATFORM%" ( if "%MAKE%" == "" ( if not "%jom.exe%" == "" ( set MAKE=jom ) else ( set MAKE=nmake ) ) set tmpl=win32 ) else ( if "%MAKE%" == "" ( set MAKE=mingw32-make ) set tmpl=unix )
大意是如果指定的平台已g++结尾,就走else的逻辑,我指定的平台为win32-g++,所以按Unix编译了?看了下还支持win32-msvc平台,我电脑上安装过vs,刚好有msvc的工具链,因此打算切换到msvc,使用-platform win32-msvc参数,依然出现上述错误,在网上收罗解决办法的时候,翻到了下面这篇文章:
里面提到要使用x64 Native Tools Command Prompt for VS 2022 Current命令行执行编译编译指令,尝试之后果然没有报错了!!(充分说明了查找资料也是初级技术人员的一项必不可少的能力),编译过程在下面的章节记录。
Windows下编译QT源码 - 2
- 重新解压qt源码
修改交叉编译工具链名称。这里要注意库编译为动态还是静态(我还不理解啥意思),摘抄文章如下:
QT静态库的编译需要注意编译选项/MD 、 /MT要和开发项目中引用的其他库相匹配。如果要修改此编译选项,可以在QT源文件根目录下的 qtbase\mkspecs\common\msvc-desktop.conf 中修改
QMAKE_CFLAGS_RELEASE = $$QMAKE_CFLAGS_OPTIMIZE -MD QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += $$QMAKE_CFLAGS_OPTIMIZE -Zi -MD QMAKE_CFLAGS_DEBUG = -Zi -MDd
或者
QMAKE_CFLAGS_RELEASE = $$QMAKE_CFLAGS_OPTIMIZE -MT QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += $$QMAKE_CFLAGS_OPTIMIZE -Zi -MT QMAKE_CFLAGS_DEBUG = -Zi -MTd
打开msvc对应的命令行(x86 Native Tools Command Prompt for VS 2022 Current,编译64位库则打开x64 Native Tools Command Prompt for VS 2022 Current),切换到qt源码解压目录下,注意可能直接cd到解压目录会失败,可以逐级切换目录。
这次使用的指令为 configure -static -release -mp -opensource -confirm-license -platform win32-msvc -xplatform linux-aarch64-gnu-g++ -prefix D:\Qt\Qt5.12.10\5.12.10\xuantie_900_mingw_v2.2.5 -nomake examples -nomake tests -no-opengl -no-iconv -no-assimp -no-qt3d-profile-jobs -no-qt3d-profile-gl -skip qtvirtualkeyboard -skip qtwebengine -skip qtlocation ,依然需要在qt解压出来的文件夹根目录下执行。configure执行的结果如下:
(上述指令红色部分是后面发现问题之后加的,到后面其实我重新解压编译了一次,用的指令如下 configure -prefix D:\Qt\Qt5.12.10\5.12.10\xuantie_900_mingw_v2.2.5 -opensource -confirm-license -release -strip -shared -xplatform linux-aarch64-gnu-g++ -platform win32-msvc -optimized-qmake -c++std c++11 --rpath=no -pch -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcanvas3d -skip qtconnectivity -skip qtdatavis3d -skip qtdoc -skip qtgamepad -skip qtlocation -skip qtmacextras -skip qtnetworkauth -skip qtpurchasing -skip qtremoteobjects -skip qtscript -skip qtscxml -skip qtsensors -skip qtspeech -skip qtsvg -skip qttools -skip qttranslations -skip qtwayland -skip qtwebengine -skip qtwebview -skip qtwinextras -skip qtx11extras -skip qtxmlpatterns -make libs -make examples -nomake tools -nomake tests -gui -widgets -dbus-runtime --glib=no --iconv=no --pcre=qt --zlib=qt -no-openssl --freetype=qt --harfbuzz=qt -no-opengl -linuxfb --xcb=no --libpng=qt --libjpeg=qt --sqlite=qt -plugin-sql-sqlite -recheck-all)- 接下来执行nmake
在编译过程中,遇到的错误以及对应的解决办法如下:
- 在编译过程中出现如下错误
看起来是在调用python时,命令行将指令中文件夹名称中的空格截断了。改动现有的文件目录比较麻烦,我直接重装了python,这次安装的目录就比较简单了,确保没有空格。继续执行nmake,不再报该错误。
- 编译过程中出现如下错误:
d:/private_toolchain/xuantie-900-gcc-linux-5.10.4-glibc-mingw-v2.2.5/bin/../lib/gcc/riscv64-unknown-linux-gnu/10.2.0/../../../../riscv64-unknown-linux-gnu/bin/ld.exe: E:/qt-everywhere-src-5.15.1/qtdeclarative/lib/libQt5Qml.a(qv4typedarray.obj): in function `unsigned long long atomicExchange<unsigned short>(char*, QV4::Value)':
qv4typedarray.cpp:(.text._Z14atomicExchangeItEyPcN3QV45ValueE[_Z14atomicExchangeItEyPcN3QV45ValueE]+0x16): undefined reference to `__atomic_exchange_2'
在网上收罗了一下资料(参考 https://twd2.me/archives/10597)
,缺少的这个函数是RV64的对8位整数和16位整数的原子操作,类似的函数还有__atomic_exchange_4(32位)和__atomic_exchange_8(64位),出现这个问题可主动指定lib文件(-latomic)解决,我报错的这个地方在qtdeclarative\tools\qml\目录下(此时已经是使用刚编译出来的qmake在编译了),需要修改该目录下的makefile文件,在LIBS变量中加入 -latomic。继续编译,不再报__atomic_exchange_2等函数的错误。在后续的编译中,还需要按这种方式修改如下文件:
qtdeclarative\tools\qmltestrunner\Makefile
- 编译过程中出现如下错误:
d:/private_toolchain/xuantie-900-gcc-linux-5.10.4-glibc-mingw-v2.2.5/bin/../lib/gcc/riscv64-unknown-linux-gnu/10.2.0/../../../../riscv64-unknown-linux-gnu/bin/ld.exe: E:/qt-everywhere-src-5.15.1/qtdeclarative/lib/libQt5Qml.a(YarrInterpreter.obj): in function `.L433': YarrInterpreter.cpp:(.text._ZN3JSC4Yarr11byteCompileERNS0_11YarrPatternEPN3WTF20BumpPointerAllocatorEPNS_6NoLockE+0x3b8): undefined reference to `JSC::Yarr::wordUnicodeIgnoreCaseCharCreate()'
收罗到如下解决办法( https://forum.qt.io/topic/32634/unresolved-external-symbol-attempting-to-build-5-2/4):
in "qt\qtdeclarative\src\qml", check the file "RegExpJitTables.h" if it's empty, delete it (it's autogenerated with python)
上述办法是针对5.2版本的qt,我现在使用的是5.15.1,这个文件的路径在qtdeclarative\src\qml.generated\RegExpJitTables.h。实际上我在删除这个文件之后依然报错,发现创建的还是一个空文件,直接进入到qtdeclarative\src\qml目录,手动执行python E:/qt-everywhere-src-5.15.1/qtdeclarative/src/3rdparty/masm/yarr/create_regex_tables > .generated\RegExpJitTables.h 命令,才成功创建这个文件(创建出来的文件是由一个超大的数组开头 static const char _wordcharData[65536])。
(文章里面还提到另一个办法是从git下载源码,但是我未尝试)。
- 在编译过程中遇到如下错误:
In file included from declarativemaps\qdeclarativepolylinemapitem.cpp:38: declarativemaps\qdeclarativepolylinemapitem_p_p.h:381:17: error: 'const char* MapPolylineShaderLineStrip::vertexShader() const' marked 'override', but does not override 381 | const char *vertexShader() const override {
这个问题,后来发现是各个模块之间有依赖导致的,我在configure的时候加了-no opengl,而这里的qtlocation模块对opengl模块有依赖,所以编译报错,重新configure一次,加上 -no qtlocation,然后重新nmake即可。
在漫长的等待之后,编译终于完成了。
执行nmake install,将编译的文件安装到 -prefix 指定的目录。
-
创建测试工程
在配置完kit(构建套件)之后,使用creator新建一个测试项目,在选择kit时勾选上我们刚刚配置好的套件,然后随便敲点代码,准备编译工程。 -
编译工程
直接点击运行或者构建,将会出现失败,对比了正常构建和失败构建的输出如下:
很明显是因为没有在工具链的目录下找到make程序导致的,但是此时已经通过qmake创建了makefile文件,我们可以直接进入到工程所在目录,然后直接执行make指令,即可完成程序的编译,我这边成功生成test可执行文件。
(后来不知道为啥就能直接找到make程序了,此时直接点击构建即可创建出test可执行程序,无需手动执行make指令了)
将编译生成的测试程序上传到开发板并执行,得到如下错误提示:
原因是因为 libstdc++.so.6库的版本不够高,因此需要更新库文件,我直接将下载的工具链中的库文件(位于工具链目录riscv64-unknown-linux-gnu\lib64v_xthead\lp64d目录下)更新到开发板上。
然后将libstdc++.so.6软连接到这个库文件即可。
重新执行测试程序,得到如下错误:
./test: error while loading shared libraries: ld-linux-riscv64v_xthead-lp64d.so.1: cannot open shared object file: No such file or directory
这看起来是库文件缺失导致的报错,将工具链中的库文件更新到开发板上
重新执行测试程序,得到如下错误:
前面两行是因为在编译qt源码时,configure没有加-no-iconv,在没加的情况下编译后,qt会使用QIconvCodec类进行编码的转换,而qt本身不再使用该类,所以不要使用该库,因此重新configure一次然后重新编译即可。
当然也可以下载去这个网址http://ftp.gnu.org/gnu/libiconv/下载libiconv源码,配置(./configure --prefix=E:\libiconv-1.17\libiconv-1.17\build --host=riscv64-unknown-linux-gnu-gcc)编译后安装,将产生的libiconv.so复制到开发板上。
编译完成后重新构建测试工程,将得到的可执行文件上传并执行,出现找不到qt库的错误,将缺失的库上传到开发板,重新执行,最终出现BUS ERROR的错误。
查询了资料,说error bus基本上可以肯定是板子上运行的qt库和编译器使用的qt库不一致造成的。那么最好的办法就是将交叉编译的qt库文件直接拷贝到开发板上,并设置其路径。
直接将整个编译出来的文件拷贝到SD卡的目录下
并设置qt的环境变量如下:
export QT_ROOT=/mnt/UDISK/qtlib export QT_QPA_PLATFORM_PLUGIN_PATH=$QT_ROOT/plugins export QT_QPA_PLATFORM=linuxfb:tty=/dev/fb0 export QT_PLUGIN_PATH=$QT_ROOT/plugins export LD_LIBRARY_PATH=$QT_ROOT/lib:$QT_ROOT/plugins/platforms export QML2_IMPORT_PATH=$QT_ROOT/qml export QT_QPA_FONTDIR=/resources/fonts
这些环境变量的设置可加入到 /etc/profile 文件中,并通过重启或者执行 source /etc/profile 的方式使其生效。
在配置完成后,重新执行测试程序(测试程序的功能为点击一个按钮,会更新一个文本框)。
界面得以显示,但是文字都没有显示出来,点击测试按钮,本应该更新文字,但是文本框依然空白,控制台输出如下信息:
Note that Qt no longer ships fonts. Deploy some (from https://dejavu-fonts.github.io/for example) or switch to fontconfig.
看这意思,是因为缺少字符库导致的,在开发板全局搜索 fonts,找到如下两个路径:
/resources/fonts /usr/share/directfb-examples/fonts
将第一个路径加入到环境变量中 ( export QT_QPA_FONTDIR=/resources/fonts/ ),重新执行程序,终于正常显示了,效果如下:
直接上一个官方炫酷一点的例程(官方例程需要编译example模块才会生成),路径在编译安装路径下"examples\widgets\animation\animatedtiles\animatedtiles",执行的效果如视频,还是非常的丝滑的。
- qmake和库文件
-
谢谢giao桑的分享
-
@yanmingjian 你好 您这边是否会出现无论运行什么窗口程序,这边都全屏显示。mes弹窗也有问题!
-
@guozhaifa everything ok!
-
@yanmingjian 你好 可以分享一下这个QT编译出来的包吗? 我编译了好几十次了 还是没有窗体。用QT4.8.5编译出来是待窗体的 但是不能有任何字体,用QT5.12编译出来能待字体 但是没有窗体。不知道咋搞。
Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号