Linux环境打包Qt程序并部署到Docker容器
根据项目需要,将UKylin版本的qt应用程序打包并且部署到docker容器中
系统版本
- UKylin版本:Ubuntu18.04
- Docker镜像:Ubuntu:latest
一、打包QT应用程序
一般默认创建项目后,Qt Creator的左下角构建模式会出现三种,分别为Debug、Profile、Release,它们的区别在于Debug可以调试程序,Release不可以。程序出错时,在Debug模式下可以通过设置断点来调试程序。当程序所有错误都解决以后,在Release模式下生成程序,就可以对外发布。而Debug生成的程序因为包含了调试信息,编译器也未做优化,所以比Release模式生成的程序要大很多,而Profile则介于两者之间,用的比较少。
1. 编译可执行文件
确保程序在Debug模式下构建通过后,再选择Release模式构建项目,在生成目录查看生成的文件格式,点开CodeFrameBuilder(程序名称)属性发现当前应用程序文件为共享库文件,并非打包所需要的可执行文件。
此时打开程序源代码,在工程文件末尾添加代码:QMAKE_LFLAGS += -no-pie
重新编译后再打开生成目录,可以直观看到程序的图标变为可执行文件的图标,点开属性变为x-executable,即可执行exe。
2. 配置QT环境
打开系统终端,输入指令配置Qt环境
//配置linux环境变量
vim ~/.bashrc
//输入a/i进入编辑模式,在文件结尾添加Qt库路径,其中home/appsoft为Qt源码包的路径
export PATH=/home/appsoft/Qt5.9.5/5.9.5/gcc_64/bin:$PATH
export LD_LIBRARY_PATH=/home/appsoft/Qt5.9.5/5.9.5/gcc_64/lib:$LD_LIBRARY_PATH
export QT_PLUGIN_PATH=/home/appsoft/Qt5.9.5/5.9.5/gcc_64/plugins:$QT_PLUGIN_PATH
export QML2_IMPORT_PATH=/home/appsoft/Qt5.9.5/5.9.5/gcc_64/gml:$QML2_IMPORT_PATH
//添加完成后,点击esc退出编辑模式,输入:wq保存退出
source ~/.bashrc
//终端执行生效
3. 安装linuxdeployqt
3.1 下载linuxdeployqt
在 linuxdeployqt 下载linuxdeployqt-continuous-x86_64.AppImage , 该包是一个静态程序可以直接拿来使用。
3.2 安装linuxdeployqt
将下载好的linuxdeployqt包移动到虚拟机中,对其重命名并移动到指定路径
mv linuxdeployqt-continuous-x86_64.AppImage linuxdeployqt
chmod 777 linuxdeployqt
mv linuxdeployqt /usr/local/bin
//测试是否成功
linuxdeployqt --version
//测试结果
linuxdeployqt (commit edbf092), build 45 built on 2023-04-04 19:32:05 UTC
4. linuxdeployqt查找依赖库
将已编译生成好的Release版可执行程序(CodeFrameBuilder)复制到一个新的文件夹中,在新的文件夹(/home/appsoft/MyTools/New)中打开终端。
即在当前路径下,对应用程序进行打包;
注意:该工具只是找寻了所需的Qt依赖,但Qt环境需要的基础依赖库并没有找齐,将其放在docker下仍然跑不起来。
linuxdeployqt CodeFrameBuilder -appimage
若出现.desktop file is missing a Categories= key
问题,则根据报错提示对当前文件夹下新生成的default.desktop
结尾中添加:
vim default.desktop
//在结尾添加,退出保存
Categories=Application;
然后重新运行打包指令:linuxdeployqt CodeFrameBuilder -appimage
5. 添加libqxcb.so依赖库
5.1 创建脚本
脚本作用就是找到qt环境需要的基础依赖库即xcb库
//touch lib_copy.sh(可忽略)
//vim lib_copy.sh
//复制文本到lib_copy.sh
#!/bin/bash
if [ $# != 1 ]
then
echo "传参数顺序: ./脚本.sh <要发布的可执行文件文件>"
exit 0
fi
LibDir=$PWD/xcblib
mkdir $LibDir
Target=$1
lib_array=($(ldd $Target | grep -o "/.*" | grep -o "/.*/[^[:space:]]*"))
for Variable in ${lib_array[@]}
do
cp "$Variable" $LibDir
done
5.2 执行脚本
libqxcb.so 在qt运行环境下的plugins/platforms/ 目录,但是之前已经将路径添加到了配置环境中,可以直接运行。
bash ./lib_copy.sh plugins/platforms/libqxcb.so
完成以上步骤,程序所依赖的Qt库和Qt依赖的库都分别放在 lib/ 和 xcblib/ 目录下,它们之间有些库是重复的,可以将其合成一个目录。
二、部署到Docker容器
1. 开放docker的gui权限
此后操作都需在当前终端上执行,因为当前终端开启了x11权限,但在重启后就不会有这些顾虑了,因为该权限已经应用到了全局。
vim /etc/profile
//在/etc/profile末尾添加
if [ "$DISPLAY" != "" ]
then
xhost +
fi
//保存退出后,终端执行
source /etc/profile
2. 创建容器
使用docker run命令创建容器
//test为容器名称
docker run -itd -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$DISPLAY -e GDK_SCALE -e GDK_DPI_SCALE --name test --privileged -v ./data:/data --network host ubuntu:18.04 bash
docker exec -it test bash
在docker容器内测试x11图形界面权限,若出现下图所示即成功,若报错can't open display
则重启再进行测试。
apt update
apt install xarclock
xarclock
//若成功则退出docker容器
exit
3. 复制文件夹
因为在新的文件夹中打开的终端,并且并未对文件路径进行操作,可以在终端直接输入
//../New为打包好的文件夹相对与当前终端路径
//69de为docker容器ID, /home/则为复制到容器内的位置
docker cp ../New 69de:/home/
4. 运行qt程序
进入docker后,开始测试我们的Qt程序能否执行。
cd /home/New
//这一步是为了报错的时候,可以看到报错的地方
export QT_DEBUG_PLUGINS=1
//这一步是给出依赖的库路径,请根据实际情况调整
export LD_LIBRARY_PATH=./lib:./xcblib
./CodeFrameBulder
设置依赖库的环境变量和Qt调试环境变量
如果中间报错缺少库,设置 QT_DEBUG_PLUGINS 为 1 后可以看到缺少库的名称,可以在主机中找到对应的库并拷贝到 docker 中。
也可以通过 apt-file search libxx.so 来查找属于哪个包,然后安装对应的包。
注意事项:
运行qt应用程序出现错误的原因:宿主机的系统版本和docker容器系统版本不一致,导致libc的版本不一致。
- 解决方法删除xcblib中的libc.so.6, librt.so.1, libpthread.so.0,默认用镜像自身的;
- 将系统的库路径设置在 LD_LIBRARY_PATH 的前面,让其先从系统库路径下查找库。