ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

银河麒麟ARM版离线搭建Qt5开发环境完整实战指南

银河麒麟ARM版离线搭建Qt5开发环境完整实战指南 最近在给客户交付一套基于Qt5的界面程序目标平台是银河麒麟V10的ARM版而且是完全的物理离线环境。折腾了整整两天光是gcc和Qt5的依赖问题就来回撞了好几次墙。一边翻系统源一边手动补包最后总算是把从编译器到Qt Creator的整条链路理顺了。这里把完整流程和踩过的坑一次性整理出来给同样需要在银河麒麟ARM版上离线做Qt5开发的朋友做个参考。这篇内容主要面向三类人刚拿到麒麟ARM设备、准备从零搭建Qt环境的嵌入式开发者需要在信创/隔离网环境下交付项目的工程团队以及被“Qt5无法拖拽文件”“gcc升级后还是旧版本”这类问题折磨过、正在到处搜索解决方案的人。你不需要提前准备太多只要有一台银河麒麟V10 ARM设备、一个可用的离线安装包来源系统ISO镜像或另一台联网机器按照下面的步骤就能把环境完整搭起来。1. 开始之前先搞清楚你拿到的到底是个什么环境1.1 系统版本与CPU架构自查命令很多人在第一步就走错了方向。拿到机器不问架构、不看系统版本直接用一个x86世界里的安装包往ARM机器上怼结果自然是各种not found和cannot execute binary file报错。银河麒麟V10有x86版也有ARM版同样是“V10”两者软件生态的安装方式差别非常大。动手装任何东西之前先执行下面三组命令把系统的家底盘清楚# 查看操作系统具体版本和代号 cat /etc/os-release # 查看内核版本和架构 uname -a # 查看CPU架构详细信息 lscpu | grep Architecture我在实际项目里拿到的是麒麟V10的ARM版uname -m输出为aarch64这说明系统跑的是64位ARM指令集。注意aarch64和armv7l虽然都叫ARM但前者是64位、后者是32位软件包完全不通用。如果你是armv7l下面讲到的arm64版deb包也用不了需要单独找armhf版本。另外用cat /etc/os-release看清楚系统基于哪个上游版本。银河麒麟V10有的基于Ubuntu有的基于Debian虽然包管理工具都是apt/dpkg但软件源配置和依赖版本会有差异。我看到VERSION_CODENAME是focal就知道它基本兼容Ubuntu 20.04的软件包这为后面从联网机器拉取deb包提供了重要依据。1.2 判断离线程度三种网络形态决定安装策略“离线安装”这个词其实很笼统不同离线程度对应完全不同的操作路线。我先根据现场情况把离线环境分为三类你对照自己手头的机器来判断完全物理隔离机器不出网没有内网源只有一个U盘或光盘可用。这种情况最麻烦所有软件包都得提前在外面准备好。有内网软件源机器能访问公司内部的apt源只是不能上外网。这种情况最简单直接改一下sources.list指向内网源apt install基本畅通无阻。无源但有系统ISO镜像机器不能联网但是你有银河麒麟的安装光盘或ISO文件。这中等情况可以通过挂载ISO搭建一个本地源装gcc、基础Qt5库都没问题。我这次的现场属于第一种机器连内网源都没有只有一个清明上河图一样的ISO镜像。所以下面的方案会以“有ISO”和“无ISO”两条线来讲一条是挂载ISO搭本地源另一条是借助联网机器打包deb再拷贝进场。先说一个直觉判断如果只靠U盘随便拷贝几个.run安装包进去就想搞定Qt5开发环境基本不可行。Qt5开发依赖庞大gcc需要配套的libc6-dev、binutilsQt5需要libxcb、libGL、fontconfig等一堆运行时库缺一个都能让你编译失败或者程序起不来。正确思路是“准备一个完整可用的软件包集合”而不是“拷贝一个安装程序进去”。2. 离线环境下的gcc先从依赖地狱里爬出来2.1 招式一挂载ISO搭本地源能救80%的场银河麒麟V10的ISO镜像里带了一部分常用软件包虽然不一定有完整的Qt5开发套件但gcc、g、make、binutils这些基础编译工具大概率齐。如果你手头有ISO文件或光盘我建议优先用这个方案因为它最省事、最不容易出依赖遗漏。先把ISO文件拷进系统或用光驱挂载然后执行sudo mkdir -p /mnt/kylin sudo mount -o loop /path/to/Kylin-Desktop-V10-ARM.iso /mnt/kylin挂载完以后在/etc/apt/sources.list.d/下新建一个本地源文件echo deb [trustedyes] file:///mnt/kylin focal main restricted universe multiverse | sudo tee /etc/apt/sources.list.d/kylin-local.list sudo apt update这里有个坑要提醒[trustedyes]参数一定要加因为本地源没有Release签名不加的话apt update会直接拒绝使用报NO_PUBKEY或InRelease相关错误。另外如果ISO解压出来的目录结构不是标准的Ubuntu源结构比如只有/pool和/dists你需要确认dists目录下确实存在focal这个代号如果不是就把sources.list里的focal改成实际的目录名。搞定本地源之后安装编译器就变成一条命令的事sudo apt install gcc g make装完验证一下gcc --version g --version这个方法能覆盖绝大多数“只需要gcc能用来编译”的场景。如果你做的是纯Qt Widgets项目后面再补Qt5的包也能通过这个源装上一部分。但如果ISO里没有你要的包就得用第二种方法。2.2 招式二同架构联网机器打包deb一次拉齐依赖树ISO源覆盖不了的情况太常见了。麒麟V10的ISO镜像体积有限里面不可能把所有Qt5开发包都塞进去这个时候就需要借助另一台“同架构、同系统版本”的联网机器把目标包和它的整个依赖树全部下载成deb再拷贝到离线机上安装。先找一台能联网的aarch64机器如果实在没有用虚拟机跑一个Ubuntu ARM版也可以但系统版本尽量贴近配置好麒麟源或Ubuntu源然后执行# 先安装一个递归查询依赖的工具 sudo apt install apt-rdepends # 查看目标包会引入多少依赖心里有个数 apt-cache depends gcc g make # 使用--print-uris把所有deb包的下载地址打出来 apt-get install --reinstall --print-uris -y gcc g make libc6-dev--print-uris会把每个包的实际下载链接列出来你可以按这个清单逐个下载。但更省事的办法是直接用apt-get download配合递归依赖列表一条命令拉全部sudo apt-get install -y --print-uris gcc g make | grep ^\http | awk {print $1} | tr -d /tmp/deb_urls.txt然后借助wget逐个下载或者直接用下面这个更实用的命令把deb包统一放到一个目录mkdir /tmp/offline_debs cd /tmp/offline_debs # 方式A如果你能联网且使用apt直接下载指定包 apt-get download $(apt-rdepends gcc g make libc6-dev 2/dev/null | grep -v ^ ) # 方式B如果依赖关系太乱就直接手工把关键包下载下来再逐个试 apt-get download gcc g make libc6-dev binutils linux-libc-dev cpp注意apt-rdepends输出里那些带Depends:缩进的部分才是依赖包名直接取第一列即可。下载完把整个offline_debs目录拷贝到U盘拿到离线机上。离线机上安装deb包时我最开始直接sudo dpkg -i *.deb结果一团糟。因为dpkg按照文件名字母顺序安装不会主动解析依赖顺序经常装到一半报某个依赖还没有配置。正确的做法是分两步先忽略依赖顺序套一层再用apt-get -f install来自动修正。cd /tmp/offline_debs sudo dpkg -i --force-depends *.deb sudo apt-get -f install -y--force-depends可以跳过依赖检查先把所有包装上最后apt-get -f install会自动把缺失的依赖关系补齐。实测这个方法比手工按依赖顺序不停dpkg -i要靠谱得多。如果最后还报缺包那说明你在打包阶段漏了依赖回到联网机器上用apt-cache depends逐个对照补一次就好。2.3 gcc装完还是旧版本PATH和软链的坑gcc装完以后很多人的第一反应是gcc --version验证结果发现版本号和预期不符甚至还是系统自带的旧版本。这个问题我见过太多次了一般来说有三个原因第一系统里本来就带了一个旧版gcc新装的gcc被装到了不同路径。用which gcc看一下实际调用的路径如果输出的是/usr/bin/gcc而新版本装到了/usr/local/bin/gcc说明是PATH顺序问题。执行echo $PATH看一下/usr/local/bin和/usr/bin哪个靠前。第二安装后系统存在多个gcc版本可以通过update-alternatives手动管理sudo update-alternatives --config gcc sudo update-alternatives --config g这个命令会把系统里所有gcc版本列出来你选择默认要用的那个即可。第三环境变量没有重新加载。source /etc/profile或者重新登录一次终端不要用旧的bash窗口去验证很多时候版本没变就是这个低级原因。还有一个更隐蔽的坑ARM机器上如果libc6-dev没有正确安装gcc即使能运行编译任何C程序都会报fatal error: stdio.h: No such file or directory。这是因为gcc本身正常但标准头文件和glibc开发文件缺失。遇到这种报错不用怀疑gcc装坏了第一反应应该是补libc6-dev。3. ARM版Qt5离线安装的思路和实操3.1 为什么在ARM上不要执着于Qt官网的.run包很多第一次接触ARM Linux Qt开发的人第一反应就是去Qt官网下载安装包。这里我必须泼一盆冷水Qt官方的Linux离线安装包绝大多数只有x86_64版本官网那个qt-opensource-linux-x64-5.14.2.run在ARM机器上根本运行不了。Qt官方提供的是在线安装器确实支持aarch64但离线环境用不了在线安装器。官方离线.run安装包又只面向桌面x86架构ARM版几乎没有对应物。所以“在ARM上找Qt官方离线包”这件事本身就是一个死胡同不要在这个方向上浪费时间。在银河麒麟ARM版上装Qt5最靠谱的路线还是走系统包管理器。麒麟是基于Debian系的系统软件源里有现成的Qt5开发套件。虽然版本可能不是最新的比如5.12、5.15之类但对大多数工业界面、嵌入式应用来说已经完全够用而且和系统库的兼容性最好不需要你手动去配置运行时库路径。3.2 用apt/离线deb装好qtbase5-dev和全套工具链如果你之前搭好了本地源或者能访问内网源直接一条命令装齐sudo apt install qtbase5-dev qtbase5-dev-tools qttools5-dev-tools qtcreator libqt5svg5-dev libgl1-mesa-dev libxkbcommon-dev这里几个包的作用我分别说明一下qtbase5-devQt5的核心开发包包含了QtCore、QtGui、QtWidgets这几个主力模块的头文件和链接库没有它你连#include QWidget这关都过不去。qtbase5-dev-tools提供moc、uic、rcc这些Qt专用工具。Qt的元对象系统依赖moc生成代码缺了它编译任何带Q_OBJECT的类都会报错。qttools5-dev-tools提供designer、linguist、assistant等辅助工具如果你想在可视化界面里拖控件这个包必须装。qtcreatorQt开发IDE。libqt5svg5-devSVG格式支持做界面设计经常用到。libgl1-mesa-dev和libxkbcommon-dev这两个是很多Qt5程序编译时的隐性依赖。Qt的GUI底层依赖OpenGL库和键盘输入处理库缺了它们编译时可能出现找不到GL/gl.h或者链接失败虽然报错信息不直接指向Qt但就是这个原因。如果你是完全离线、只能用deb包迁移的环境那就在联网机器上用前面第2.2节的思路把上面这一串包连同依赖一起拉下来mkdir /tmp/qt5_debs cd /tmp/qt5_debs apt-get download $(apt-rdepends qtbase5-dev qtbase5-dev-tools qttools5-dev-tools qtcreator libqt5svg5-dev libgl1-mesa-dev libxkbcommon-dev 2/dev/null | grep -v ^ )这条命令拉下来的包数量会非常多因为Qt5的运行时库依赖一堆xcb、freetype、fontconfig等图形栈组件。打包时耐心等它下载完然后整个目录带进离线机用同样的dpkg -i --force-depends *.deb加apt-get -f install -y处理。安装完成后可以用一个简单的命令验证Qt5的核心组件是否就位dpkg -l | grep qt5 qmake --version如果qmake --version能输出类似QMake version 3.1 Using Qt version 5.15.2 in /usr/lib/aarch64-linux-gnu/qt5的内容说明Qt5核心已经安装成功。3.3 小范围内的选型建议哪些包必须装哪些踩过才知道首次搭建Qt环境的人很容易陷入两个极端要么只装一个qtbase5-dev就开干等到具体模块缺了再手忙脚乱补包要么一口气把qt5-*全部装掉结果系统里塞满用不上的东西。结合我在麒麟ARM上跑Qt5的实际经验给你一个参考清单必要组件不装基本跑不起来qtbase5-dev核心开发包qtbase5-dev-toolsmoc等编译工具qtcreatorIDE强烈建议安装业务开发高频用到qttools5-dev-toolsdesigner和linguistlibqt5svg5-devSVG图标和图片libqt5printsupport5和对应的开发包如果需要做打印功能libqt5serialport5-dev如果做串口调试工具libqt5networkauth5-dev如果需要OAuth之类认证流程按需安装用到再补libqt5charts5-dev图表绘制libqt5webenginewidgets5嵌入式浏览器组件体积巨大对资源占用也高非必要别装libqt5multimedia5多媒体播放有一点你可能会踩坑Qt5的版本和系统桌面环境UKUI之间的配合。麒麟的UKUI桌面本身基于Qt开发系统里通常自带了某种版本的Qt5运行库但开发头文件很可能没装。你通过apt额外安装qtbase5-dev时如果版本比系统自带的运行时库版本略新或略旧一般不会冲突APT会把依赖关系自动处理好。怕的是你手动下载一个非系统源的Qt编译产物去覆盖那就容易把桌面环境搞崩。所以在这个平台上的第一原则能用系统源装就用系统源装不要自己下载编译包覆盖。4. Qt Creator装配与构建套件Kit配置4.1 安装Qt Creator版本别盲目追新sudo apt install qtcreator装好的版本可能不是Qt官方最新版这反而不是坏事。Qt Creator和Qt库的版本对应关系比较灵活新版Creator通常可以兼容多个Qt5小版本但反向不一定成立——特别老的Creator可能不识别新版本qmake生成的工程格式。在麒麟这种比较克制的系统源里Creator版本往往比官方最新版落后一两个大版本但对开发Qt5 Widgets程序毫无影响。我甚至建议不要自己去Qt官网下载一个很新的Creator二进制包因为新版Creator对GLIBC版本有要求ARM版银河麒麟的glibc如果不够新直接起不来。安装完以后启动一下命令行输入qtcreator如果启动过程中遇到缺少libxcb-xinerama0之类的报错这个报错在离线环境很常见因为Qt Creator依赖xcb的扩展库先去补装sudo apt install libxcb-xinerama0 libxcb-cursor04.2 Kit配置qmake、gcc、gdb三件套Qt Creator装上以后真正的关口是构建套件Kit配置。打开菜单“工具” → “选项” → “Kits”部分中文版叫“构建套件”你会看到左侧有“Qt版本”、“编译器”、“调试器”等页签。这三个页签必须都配置正确Kit前面的状态图标才会变成绿色否则编译会报一堆莫名其妙的问题。先看“Qt版本”页签正常情况Qt Creator会自动扫描到系统安装的qmake路径通常为/usr/lib/aarch64-linux-gnu/qt5/bin/qmake。如果列表为空点击“添加”手动选择qmake路径。这里要特别嘱咐一句看清楚路径里的aarch64-linux-gnu如果你不小心把x86架构机器的qmake路径配到这里Creator会直接报“qmake无法执行”。再看“编译器”页签点击“添加” → “GCC” → “C”在“编译器路径”里填/usr/bin/g名称随便写比如GCC-ARM64。C编译器同理填/usr/bin/gcc。如果Creator自动检测到了你也可以直接用它检测出来的结果但要确认架构列显示的是aarch64不是x86_64。最后在“调试器”页签添加/usr/bin/gdb。这个步骤很多人会忽略导致能编译能运行但一打断点就报“No debugger”。如果系统里没有gdb先sudo apt install gdb装上。三个组件都就位以后回到“Kits”页签新建一个Kit名称建议写成Kylin-ARM64-Qt5Qt版本选刚才添加的那个编译器C/C都选GCC-ARM64调试器选gdbCMake工具如果有需要也可以指定。保存之后看到Kit前面的状态是绿色对勾说明它已经可用了。如果显示红色警告先在下方“错误/警告”信息栏查看具体原因最常见的就是“没有设置Qt版本”和“编译器中C和C没有匹配到同一套工具”。4.3 第一个程序跑起来之前先过一遍这几个细节新环境上跑第一个Qt程序最容易在下面四个细节上翻车。我在麒麟ARM上第一次建工程时就因为在模板选择上多犹豫了一下。第一工程模板选择。新建工程时如果选择“Qt Widgets Application”cmake和qmake两套构建系统都能用。我建议在麒麟上用qmake原因很简单qmake和apt安装的Qt5集成度更好路径配置更省心几乎不需要额外指定任何参数。CMake虽然也能用但要确保Kit里CMake路径配置正确而且CMake在解析Qt5的Qt5_DIR时容易因为路径不对报错。第二编译器基线和-fPIC问题。ARM Linux上编译共享库或可执行文件时qmake会自动加上-fPIC一般不需要你手工干预。但如果你用纯命令行gcc去编译Qt程序而不是通过qmake或Creator很容易漏掉编译选项导致链接错误。新手阶段建议全程走Creator的构建按钮不要自己手搓命令行。第三程序运行时提示找不到libQt5Widgets.so.5的解决方案。这种情况一般不是安装有问题而是运行时库路径没被找到。用apt安装的Qt5库都在/usr/lib/aarch64-linux-gnu/目录下系统的ldconfig默认是能搜到的。如果你手动把编译好的程序拷贝到别的机器上运行才需要考虑LD_LIBRARY_PATH或rpath问题。在本机调试阶段遇到这个报错先执行sudo ldconfig刷新缓存。第四检查~/.bashrc里是否有人为设置过QTDIR或LD_LIBRARY_PATH环境变量如果指向了不存在的目录会影响Qmake和Creator的自动检测。我在一台机器上就遇到有人配了个过时的QTDIR/opt/Qt5.9.2导致一切自动检测都失效。检查方法echo $QTDIR echo $LD_LIBRARY_PATH有任何残留配置先注释掉再重启Creator。5. 现场常见故障排查与实用技能补充5.1 Qt5无法拖拽文件到界面的原因和对策“Qt5无法拖拽文件”这个问题在银河麒麟ARM平台上非常典型而且网上答案碎片化严重这里我把排查路径完整捋一遍。首先要明确拖拽分为“从系统文件管理器拖文件到你的程序窗口”和“在你的程序内部拖控件”两种。前者依赖系统级拖拽协议后者依赖Qt内部的事件系统。用户通常抱怨的是前者。在麒麟V10的UKUI桌面上Qt5程序无法接收系统文件拖拽我排查下来主要是这四个原因第一程序没有调用setAcceptDrops(true)。这是最常见的新手错误。QWidget默认不接受drop事件你必须在构造函数或初始化函数里显式开启setAcceptDrops(true);然后在类里重写dragEnterEvent和dropEvent两个函数并正确调用event-acceptProposedAction()否则文件拖进来不会有任何响应。第二平台插件没有走X11而走了Wayland。Qt5在Linux下默认会根据当前桌面环境选择平台插件如果系统的图形会话是Wayland而你的程序没有使用Wayland适配层拖拽功能就很容易失效。麒麟V10默认的登录会话基本是X11但如果你通过某些远程软件或切换过显示服务器就可能在Wayland环境下运行。遇到这种问题强制指定XCB插件启动export QT_QPA_PLATFORMxcb ./your_app第三以root身份运行导致拖拽受限。在麒麟上很多用户喜欢sudo ./your_app但某些桌面组件的拖拽在root权限下会被窗口管理器限制。如果程序非要用root权限可以先试试不用root运行看拖拽是否恢复从而确认是不是权限问题。第四事件过滤器拦截了拖拽事件。如果程序里给控件安装了eventFilter并且在过滤器里吞掉了QEvent::DragEnter或Drop那无论如何设置setAcceptDrops都不生效。排查方法是暂时关闭事件过滤器逐一确认。从我的经验看第三和第四个原因在真实项目里出现频率很高尤其是root运行的问题很多测试工程师会在root环境下得出“拖拽功能坏了”的结论实际上切回普通用户一切正常。5.2 编译报错与运行时缺库的定位方法Qt5开发时遇到编译错误先别急着重新安装整个Qt大部分问题定位一下就能解决。编译期最常报的错是fatal error: QWidget: No such file or directory。这个报错出现只能说明头文件搜索路径没配上。用qmake构建时检查.pro文件里是否确实包含了QT widgets。如果漏写qmake生成的头文件路径里就不会包含QtWidgets模块编译才会找不到头文件。如果报的是undefined reference to vtable之类的链接错误基本可以断定Q_OBJECT宏所在的类的头文件没有跑过moc。这在qmake工程里通常是因为把含Q_OBJECT的类写在了.cpp文件里虽然现在moc也能处理这种情况但需要额外配置QMAKE_MOC或者忘了在.pro文件里声明对应的头文件。Qt的规则是头文件里有Q_OBJECT这个头文件必须出现在HEADERS变量里moc才会处理它。运行期缺库的报错长这样error while loading shared libraries: libQt5Widgets.so.5: cannot open shared object file: No such file or directory第一反应不应该是重装Qt而是先看这个库是否存在ldconfig -p | grep Qt5Widgets find /usr/lib/aarch64-linux-gnu -name libQt5Widgets*如果库存在但程序仍然报“找不到”多半是程序里设置了LD_LIBRARY_PATH把系统库路径覆盖了或者程序是用rpath固定了错误的库目录。执行一下ldd ./your_app | grep Qt5能看到实际链接到的路径。如果库真的不存在说明开发包装了但运行时库没装补装对应的libqt5widgets5以及libqt5gui5即可。5.3 顺手解决几个高频小问题中文字体、信号槽结构体、GDB数据断点开发过程中还有几个高频问题几乎每台新机器上都会被问一遍我在这里一起处理掉。第一个是中文乱码和方块字。在麒麟ARM上跑Qt5程序界面显示中文全部变成方块这是典型的字体缺失。系统可能带了基本的中文字体但Qt5默认字体回退没有选择到它们。解决办法是安装开源中文字体sudo apt install fonts-wqy-microhei fonts-wqy-zenhei fonts-noto-cjk安装完成后重启程序如果还不行在代码里显式设置字体QApplication::setFont(QFont(Noto Sans CJK SC, 10));第二是信号槽传递结构体的问题。很多人在Qt5里自定义了一个结构体然后用信号槽传递编译能过但槽函数收到的数据是空的甚至连接不生效。原因很简单跨线程队列连接时结构体需要注册到Qt的元类型系统。在结构体定义下面加一行Q_DECLARE_METATYPE(MyStruct)然后在连接前注册类型qRegisterMetaTypeMyStruct(MyStruct);如果是跨线程信号槽连接时最好显式使用Qt::QueuedConnection避免因为连接类型自动判断错误而丢失信号。第三是Qt Creator里怎么下数据断点。这个问题问的人也很多。在Qt Creator的调试模式下右键左侧断点边栏选择“添加数据断点”在弹出的编辑框里输入要监视的内存地址。更常用的做法是在断点命中后在“调试器” → “监视” 表达式列表里右键变量选择“在内存地址处设置数据断点”。数据断点在排查栈溢出、数组越界和指针被意外改写时非常有用。注意数据断点只在GDB后端下可用而且ARM Linux上硬件数据断点数量有限一般同时只能设4个设太多会提示资源不足。写在最后的小经验这套环境我在两台不同的麒麟ARM设备上各装了一遍第二次明显比第一次快了很多。最大的体会是离线安装的成功率基本取决于你在“打包阶段”是否把依赖树一次理清。与其在目标机器上反复试错、逐个补包不如在联网机器上把所有依赖一次性用apt-rdepends拉干净哪怕打包体积大一点也比现场缺库强十倍。另外还想分享一个小习惯每次装完一个阶段比如gcc装完、Qt5装完都随手做一个dpkg -l /tmp/pkg_manifest.txt的快照。如果后续环境被改坏了对照快照排查比从头重装快得多。这个习惯在隔离环境交付项目时尤其有用客户现场的运维看到你能直接告诉他要补哪个包会省掉很多沟通成本。
返回列表