¿Qué es la compilación cruzada? ¿Cuáles son las herramientas para construir archivos binarios para Windows en Linux ? ¿Cómo configurar el contenedor docker para todo esto? Estos son solo algunos de los temas que se discutirán a continuación.
Las herramientas
La compilación cruzada le permite obtener código ejecutable para una plataforma distinta de aquella en la que se ejecuta este proceso.
En este artículo, veremos la compilación cruzada para la plataforma Windows en Linux .
Un ejemplo de un compilador cruzado es Mingw-w64 . De hecho, proporciona solo una herramienta para compilar la aplicación, pero si necesita bibliotecas de terceros que no forman parte de la STL , tendrá que recopilarlas y sus dependencias. También puede usar archivos binarios ya preparados, como se describe en este artículo .
El proyecto mxe , que proporciona no solo herramientas, sino también bibliotecas, simplifica la configuración del ensamblaje. Su lista se puede encontrar en el sitio web oficial. Al instalar bibliotecas, se usa el control de dependencia, es decir Se instalará el paquete requerido y todo lo necesario para su funcionamiento. Las herramientas se entregan en una configuración preconfigurada, por ejemplo, para el ensamblaje estático de aplicaciones de 64 bits. Esto facilita enormemente el montaje.
El entorno mxe se implementa en la carpeta local del usuario. Para hacer esto, simplemente instale las dependencias a través del administrador de paquetes y clone el repositorio. En la raíz del repositorio hay un Makefile , que realiza la instalación de bibliotecas establecidas para ese propósito, agrega herramientas para ensamblar, etc.
Es importante tener en cuenta que el entorno de compilación está localizado dentro de su carpeta, esto le permite configurar un entorno individual para cada aplicación.
Contenerlo
Digamos que la compilación de lanzamiento para Windows está configurada en la máquina local. Las versiones salen con bastante frecuencia, en algunas versiones se agregan nuevas bibliotecas, y algunas, por ejemplo, se eliminan. Un buen día, el jefe exige deshacerse de la versión de lanzamiento para un principiante. ¿Cómo configura su entorno de construcción? ¿Qué bibliotecas deben tomarse del repositorio mxe y para qué compilación desde el origen?
En este caso, puede obtener un script bash que implementará todo el entorno en una carpeta determinada. Y después de tratar de mantener este script actualizado. Pero, al igual que la documentación del proyecto, en un momento crítico, puede quedar desactualizado.
Una buena solución sería aislar nuestro entorno de construcción dentro del contenedor acoplable . El archivo docker contendrá un conjunto de instrucciones independientes para implementar el entorno, y la presencia de un contenedor ayudará a evitar el desorden del sistema doméstico con bibliotecas innecesarias.
Poniendo todo junto
Para una demostración, tomemos un proyecto Qt simple: SimpleQtProject . Este proyecto está construido por la utilidad qmake y consta de una sola forma. Esto, por supuesto, se hace por simplicidad. También en la carpeta acoplable hay un archivo para construir el contenedor.
Considere un archivo de proyecto docker . De hecho, consta de varias partes principales:
- instalar dependencias para el sistema de compilación
- instalación y configuración del sistema de montaje
- compilar el proyecto y copiar artefactos al sistema host
A continuación, solo se consideran los comandos básicos del archivo, para una familiarización completa se recomienda consultar el repositorio .
Omitimos el primer punto y procedemos directamente a la instalación de mxe .
Clonar el repositorio:
RUN mkdir /cross \ && cd /cross \ && git clone https://github.com/mxe/mxe.git \ && cd mxe \ && git checkout build-2019-06-02
En el momento de escribir este artículo, la última versión fue build-2019-06-02 . La rama maestra no se usa aquí por una simple razón: se requiere la repetibilidad del ensamblaje. De lo contrario, al agregar nuevas confirmaciones al maestro, el ensamblado puede romperse.
A continuación, configuramos el sistema de compilación:
RUN make MXE_TARGETS=x86_64-w64-mingw32.static qtbase -j4 JOBS=4
Este comando agregará herramientas (instancias de cmake y Mingw-w64 , etc.) para el ensamblaje estático del proyecto bajo la arquitectura de 64 bits, y luego ensamblará Qt usándolas.
El siguiente paso es agregar la ruta a los archivos ejecutables mxe en PATH :
ENV PATH="/cross/mxe/usr/bin:${PATH}"
Después de configurar el entorno de compilación, puede ir directamente al último elemento:
ENTRYPOINT x86_64-w64-mingw32.static-qmake-qt5 /app/src/SimpleQtProject.pro \ && make release \ && cp release/SimpleQtProject.exe /app/res/
Aquí hay algunos puntos para aclarar. Se supone que cuando se inicia el contenedor, la carpeta de origen que contiene el archivo * .pro se montará en la carpeta / app / src / , y el lugar donde se deben agregar los resultados del ensamblaje se montará en el directorio / app / res / .
El siguiente es un ejemplo de un comando para crear docker-image , debe ejecutarse en la carpeta docker del proyecto en cuestión:
docker build -t simple-qt-build --file windows.docker .
La asamblea comienza allí:
docker run --mount type=bind,source=$(pwd)/result/,target=/app/res --mount type=bind,source=$(pwd)/../,target=/app/src simple-qt-build
Antes de ejecutar el comando, debe crear la carpeta de resultados en el directorio acoplable para copiar los resultados.
Personalización de ensamblaje
Por defecto, mxe proporciona MinGW versión 5.5.0 (al menos esto es cierto para build build-2019-06-02 ).
Si el proyecto usa nuevas características de C ++ 17 , entonces esa versión del compilador no es satisfactoria. Afortunadamente, el entorno de compilación proporciona versiones más nuevas como complementos separados. Para resolver nuestro problema, necesitamos agregar instrucciones para usar el complemento apropiado al equipo de compilación de la biblioteca:
make MXE_TARGETS=x86_64-w64-mingw32.static MXE_PLUGIN_DIRS=plugins/gcc7 qtbase -j4 JOBS=4
Este comando creará un kit para el ensamblaje estático de aplicaciones de 64 bits utilizando el compilador de la séptima versión ( 7.4.0 ). Si dicho kit ya existe , entonces no se cambiará.
Puede encontrar una lista de todos los complementos disponibles en la página .
El directorio mxe / src contiene archivos * .mk que describen los parámetros de ensamblaje de un paquete en particular. Si es necesario, puede hacer los ajustes necesarios a un paquete existente o agregar el suyo propio. La estructura del archivo se describe aquí: https://github.com/mxe/mxe/wiki/Add-a-New-Package:-Full-Version .
Para copiar dependencias, el proyecto mxe proporciona la utilidad copydlldeps.sh . Pero esta no es la única herramienta útil, sus listas completas se pueden encontrar en la página .
CMake y enlace estático Qt
Dio la casualidad de que en mi proyecto usé Qt y el sistema de compilación CMake . Cuando se decidió crear un proyecto para Windows , una excelente solución fue construir todo utilizando un enlace estático para proporcionar a los usuarios un binario, sin ninguna dependencia.
Analizando una montaña de errores de enlazador, fue posible descubrir que tal ensamblaje fuera de la caja no funciona, en ningún otro lugar. El hecho es que cuando se usa el enlace estático, qmake genera un archivo * .cpp , que contiene instrucciones para importar complementos, sobre este tipo:
// 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)
También se agregan banderas y bibliotecas para la etapa de vinculación en el Makefile .
Puede intentar experimentar con este diseño en 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()
Y luego agregue el uso de plugin_libs
. Pero para mí, este enfoque no trajo ningún resultado.
Al final, tomé la decisión de vincular dinámicamente (si es posible) todas las bibliotecas externas y copiar, junto con el archivo ejecutable, los archivos DLL necesarios usando copydlldeps.sh . En más detalle sobre la implementación bajo Qt en Windows se describe en el artículo .
En conclusión
Se mostró anteriormente cómo, en unos pocos pasos simples, puede configurar el entorno para compilar un proyecto de forma cruzada. Pero, desafortunadamente, en condiciones reales, no todo es tan color de rosa.
Aunque el proyecto mxe proporciona una lista impresionante de bibliotecas, aún puede no incluir las que necesita o incluir versiones demasiado nuevas. Sí, existe la oportunidad de crear un paquete usted mismo o, en el peor de los casos, crear una biblioteca desde la fuente. Pero no todo se puede construir con un compilador cruzado, por lo que no podría hacerlo con el proyecto cpprestsdk , porque necesita el vcpkg instalado.
Muchos problemas que pueden surgir al construir un proyecto con un compilador cruzado son típicos para el desarrollo multiplataforma en general. Por ejemplo, tuve un error extraño debido a un elemento de enumeración ERROR
. Resultó que en uno de los archivos de encabezado de Windows hay una macro con el mismo nombre. Su sustitución rompió todo el código.
Es asunto de todos usar la compilación cruzada o no. Esto me trajo una buena ganancia. Configuré el ensamblaje para varias distribuciones de Linux y Windows en contenedores acoplados separados para mi proyecto SecureDialogues , agregué un archivo MAKE para iniciar el proceso uno por uno para cada contenedor. A continuación, ejecute make y después de un tiempo obtengo los archivos binarios para el sistema operativo requerido en la carpeta local.