在Docker中交叉编译 为什么不呢

什么是交叉编译? 在Linux上为Windows构建二进制文件的工具有哪些? 如何为所有这些配置Docker容器? 这只是下面将要讨论的一些问题。


工具


交叉编译允许您获取该进程运行所在平台以外的平台的可执行代码。


在本文中,我们将研究LinuxWindows平台的交叉编译。


交叉编译器的一个示例是Mingw-w64 。 实际上,它仅提供构建应用程序的工具,但是如果您需要不属于STL的第三方库,则必须收集它们和依赖项。 您还可以使用现成的二进制文件,如本文所述


mxe项目不仅提供工具,而且还提供库,从而简化了装配设置。 他们的名单可以在官方网站上找到。 安装库时,使用依赖项控制,即 所需的软件包及其操作所需的一切都将被安装。 这些工具以预配置的配置提供,例如,用于64位应用程序的静态组装。 这极大地方便了组装。


mxe环境部署到用户的本地文件夹。 为此,只需通过程序包管理器安装依赖项并克隆存储库。 在存储库的根部是一个Makefile ,该文件执行为此目的而设置的库的安装,添加用于组装的工具等。


请务必注意,构建环境位于其文件夹内,这使您可以为每个应用程序配置一个单独的环境。


容器化


假设Windows的发布版本是在本地计算机上配置的。 发行版本经常出现,在某些版本中,添加了新库,而某些版本则被删除了。 好的一天,老板要求推迟初学者的发布版本。 他如何配置自己的构建环境? 应该从mxe存储库中获取哪些库,以及从源构建哪些库?


在这种情况下,您可以获得一个bash脚本,它将在给定文件夹中部署整个环境。 并且尝试使此脚本保持最新。 但是,就像该项目的文档一样,在关键时刻,它可能会过时。


一个好的解决方案是将我们的构建环境隔离在Docker容器内。 码头工人文件本身将包含一套完整的环境部署指令,容器的存在将有助于避免不必要的库使家庭系统混乱。


全部放在一起


为了进行演示,我们来看一个简单的Qt项目-SimpleQtProject 。 该项目由qmake实用程序构建,并且由一种形式组成。 当然,这是为了简单起见。 docker文件夹中还有一个用于构建容器的文件。


考虑一个docker项目文件 。 实际上,它由几个主要部分组成:


  • 为构建系统安装依赖项
  • 组装系统的安装和配置
  • 编译项目并将工件复制到主机系统

下面仅考虑文件中的基本命令,为全面熟悉,建议查阅存储库

我们跳过了第一点,直接进行mxe的安装。
克隆存储库:


RUN mkdir /cross \ && cd /cross \ && git clone https://github.com/mxe/mxe.git \ && cd mxe \ && git checkout build-2019-06-02 

在撰写本文时,最新版本为build-2019-06-02 。 这里不使用master分支的原因很简单:要求装配可重复性。 否则,将新提交添加到母版时,程序集可能会中断。


接下来,我们配置构建系统:


 RUN make MXE_TARGETS=x86_64-w64-mingw32.static qtbase -j4 JOBS=4 

此命令将为64位架构下的项目的静态组装添加工具(例如cmakeMingw-w64等),然后使用它们组装Qt


下一步是在PATH中将路径添加到mxe可执行文件:


 ENV PATH="/cross/mxe/usr/bin:${PATH}" 

配置构建环境之后,您可以直接转到最后一项:


 ENTRYPOINT x86_64-w64-mingw32.static-qmake-qt5 /app/src/SimpleQtProject.pro \ && make release \ && cp release/SimpleQtProject.exe /app/res/ 

这里有一些要澄清的地方。 假定当容器启动时,包含* .pro文件的源文件夹将被安装在/ app / src /文件夹中,应添加汇编结果的位置将被安装在/ app / res /目录中。


以下是创建docker-image的命令的示例,它必须在相关项目的docker文件夹中运行:


 docker build -t simple-qt-build --file windows.docker . 

组装从那里开始:


 docker run --mount type=bind,source=$(pwd)/result/,target=/app/res --mount type=bind,source=$(pwd)/../,target=/app/src simple-qt-build 

在执行命令之前,需要在docker目录中创建result文件夹以复制结果。


装配定制


默认情况下, mxe提供MinGW版本5.5.0 (至少对于build build-2019-06-02如此 )。


如果项目使用C ++ 17的新功能,则这样的编译器版本将不能令人满意。 幸运的是,构建环境提供了较新的版本作为单独的插件。 为了解决我们的问题,我们需要向库构建团队添加使用适当插件的说明:


 make MXE_TARGETS=x86_64-w64-mingw32.static MXE_PLUGIN_DIRS=plugins/gcc7 qtbase -j4 JOBS=4 

该命令将使用第七版( 7.4.0 )的编译器为64位应用程序的静态组装创建一个工具包。 如果已经存在这样的工具包, 则将不会更改。


页面上可以找到所有可用插件的列表。


mxe / src目录包含* .mk文件,它们描述特定程序包的程序集参数。 如有必要,您可以对现有软件包进行必要的调整或添加自己的软件包。 文件结构在此处描述-https: //github.com/mxe/mxe/wiki/Add-a-New-Package:-Full - Version


对于复制依赖项,mxe项目提供了copydlldeps.sh实用程序。 但这不是唯一有用的工具,可以在页面上找到其完整列表。


CMake和静态链接Qt


碰巧的是,在我的项目中,我使用了QtCMake构建系统。 当决定为Windows构建项目时,一种出色的解决方案是使用静态链接构建所有内容,以为用户提供一个Binar,而无需任何依赖。


通过分析大量的链接器错误,可以发现开箱即用的这种装配在其他任何地方都行不通。 事实是,当使用静态链接时, qmake会生成一个* .cpp文件,其中包含有关此类型的插件的导入说明:


 // This file is autogenerated by qmake. It imports static plugin classes for // static plugins specified using QTPLUGIN and QT_PLUGIN_CLASS.<plugin> variables. #include <QtPlugin> Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin) Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) Q_IMPORT_PLUGIN(QGifPlugin) Q_IMPORT_PLUGIN(QICOPlugin) Q_IMPORT_PLUGIN(QJpegPlugin) 

还添加了Makefile中链接阶段的标志和库。


您可以尝试在CMakeLists.txt中尝试这种设计:


 foreach(plugin ${Qt5Gui_PLUGINS}) get_target_property(_loc ${plugin} LOCATION) message("Plugin ${plugin} is at location ${_loc}") set(plugin_libs ${plugin_libs} ${_loc}) endforeach() 

然后将使用plugin_libs添加到plugin_libs 。 但是对我来说,这种方法没有带来任何结果。


最后,我决定动态链接(如果可能)所有外部库,并使用copydlldeps.sh与可执行文件一起复制必要的dll 。 有关在Windows下在Qt下进行部署的详细信息,请参见文章


总结


上面显示了如何通过几个简单步骤即可配置用于交叉编译项目的环境。 但是,不幸的是,在实际条件下,一切并不那么乐观。


尽管mxe项目提供了令人印象深刻的库列表,但它可能仍未包含您需要的库,或者包含的库版本太新。 是的,有机会自己创建一个软件包,或者最糟糕的是从源代码构建一个库。 但是并不是所有的东西都可以用交叉编译器来构建,所以我不能用cpprestsdk项目来完成它,因为它需要安装的vcpkg


通常,使用交叉编译器构建项目时可能会出现许多问题,这通常是跨平台开发的典型问题。 例如,由于ERROR枚举元素,我遇到了一个奇怪的错误。 原来,在Windows头文件之一中,有一个同名的宏。 它的替换破坏了所有代码。


是否使用交叉编译是每个人的事。 这给我带来了可观的利润。 我为SecureDialogues项目在单独的docker容器中为几个LinuxWindows发行配置了程序集,并添加了一个makefile以针对每个容器一个一个地启动该过程。 接下来,运行make ,过一会儿,我会在本地文件夹中获取所需OS的二进制文件。

Source: https://habr.com/ru/post/zh-CN456916/


All Articles