使用Docker多阶段和多平台编译Qt

2020-12-24 21:29:06

这是Screenly.io首席执行官Viktor Petersson的特邀帖子。 Screenly是Raspberry Pi最受欢迎的数字标牌产品。在Twitter @vpetersson上找到Viktor。

对于不熟悉Qt的人来说,它是一个跨平台开发框架,可用于多种产品,包括汽车(Tesla),数字标牌(Screenly)和飞机(Lufthansa)。不用说,Qt非常强大。关于Qt框架,您不能说的一件事是,它易于编译-至少对于嵌入式设备而言。关于该主题的无数博客文章,论坛主题和Stack Overflow文章表明,编译Qt是一个常见的麻烦。

作为Qt的长期用户,我们在Screenly上与我们有许多应战。几年前,我们迁移到了Qt以获得商业数字标牌软件,从那时起,我们一直对它的性能和灵活性感到非常满意。最近,我们决定将我们的开源数字标牌软件(Screenly OSE)也迁移到Qt。由于这些项目没有共享代码库,因此这是一个全新的机会,使我们可以重新开始并探索构建过程中令人兴奋的新技术。

由于编译Qt(和QtWebEngine)是一项非常繁重的操作,因此我们需要预编译和分发Qt,以便Dockerfile可以简单地下载并将其包含在构建过​​程中(而不是作为安装过程的一部分进行编译)。

我们需要能够为所有受支持的Raspberry Pi板构建Qt / QtWebEngine(具有适当的Qt设备配置文件)。

我们应该在x86上使用交叉编译,以加快有意义的处理速度。

我们需要能够在CI上运行完整的过程,因此不能依赖Raspberry Pi。

我们应该将所有内容限制在Docker容器中运行,以免主机被构建包所困扰。

考虑到上述目标,我们有很大的机会尝试在Docker中提供新的多平台支持。与多阶段构建结合使用,我们能够获得两全其美的优势:

在Docker中使用多平台功能的最简单方法是从命令行调用它。使用docker buildx,我们可以利用新的Beta功能。通过运行docker buildx build --platform linux / arm / v7 -t arm-build。该命令使用ARMv7仿真在当前目录中根据Dockerfile生成docker映像。在幕后,Docker在QEMU虚拟化环境(准确地说是qemu-user-static)中运行了整个Docker构建过程。通过这样做,消除了设置自定义VM的复杂性。构建完成后,我们甚至可以使用docker run来自动以ARMv7模式启动容器。

尽管多平台功能是一项出色的独立功能,但与多阶段构建结合使用时,功能甚至更加强大。在单个Dockerfile中,我们可以混合和匹配平台并在步骤之间进行复制。该功能正是我们最终用Screenly OSE的Qt构建过程完成的。

感谢Balena的优秀人才,我们能够在第一阶段使用Raspbian基本图像。我们可以使用以下步骤调用此步骤:

完成上述步骤后,我们可以像平常一样使用Docker并执行各种RUN命令,例如安装软件包等。请注意,如果构建未在ARMv7硬件上运行,则此容器正在使用QEMU进行仿真。在我们的例子中,我们使用命令来安装Qt构建依赖关系。上面的步骤还使我们能够完全消除从磁盘映像(这是Qt Wiki建议的)或物理Raspberry Pi的rsync文件复制文件的需要。

在ARM步骤中安装依赖项后,我们可以切换到构建器的本机x86架构以避免仿真,并使用以下代码进行交叉编译:

现在,我们进入有趣的部分。切换到x86后,我们可以复制上一步中的文件。我们这样做是为了创建可用于Qt的sysroot。我们通过运行以下命令来完成此步骤:

运行mkdir -p / sysroot / usr / sysroot / opt / sysroot / libCOPY --from = builder / lib / / sysroot / lib / COPY --from = builder / usr / include / / sysroot / usr / include / COPY- from = builder / usr / lib / / sysroot / usr / lib / COPY --from = builder / opt / vc / sysroot / opt / vc /

我们现在两全其美。通过利用多步骤和多平台功能,我们生成了可用于构建Qt的sysroot。由于我们在上一步中使用了功能齐全的Raspbian映像,因此我们甚至可以让Qt选择所有现有的库。

正如我们在引言中提到的,编译Qt远非简单易行。成功编译它需要很多步骤。要了解有关确切步骤的更多信息,可以查看完整的Dockerfile和脚本build_qt5.sh。

能够模拟ARM之类的平台非常出色,并提供了很多灵活性。但是,确实要付出一定的代价。有很大的性能损失。这个问题是为什么我们实际上不使用仿真来编译Qt的原因。相反,我们使用交叉编译。如果您具有交叉编译而不是模拟的能力,请知道交叉编译将为您提供更好的性能。

Screenly是Raspberry Pi最受欢迎的数字标牌产品。如果要将物理屏幕变成可以显示仪表板,图像,视频和网页的安全的,可远程控制的设备(通过UI或数字标牌API),Screenly使设置变得轻而易举。 Screenly有两种版本:开源版本和商业版本。