Cross Compilation im Docker. Warum nicht?

Was ist Cross Compilation? Was sind die Tools zum Erstellen von Binärdateien für Windows unter Linux ? Wie konfiguriere ich den Docker- Container für all dies? Hier sind nur einige der Themen, die im Folgenden erörtert werden.


Die Werkzeuge


Durch Cross-Compilation können Sie ausführbaren Code für eine andere Plattform als die abrufen, auf der dieser Prozess ausgeführt wird.


In diesem Artikel befassen wir uns mit der Cross-Kompilierung für die Windows- Plattform unter Linux .


Ein Beispiel für einen Cross-Compiler ist Mingw-w64 . Tatsächlich bietet es nur ein Tool zum Erstellen der Anwendung. Wenn Sie jedoch Bibliotheken von Drittanbietern benötigen, die nicht Teil der STL sind , müssen Sie diese und Abhängigkeiten erfassen. Sie können auch vorgefertigte Binärdateien verwenden, wie in diesem Artikel beschrieben .


Das mxe- Projekt, das nicht nur Tools, sondern auch Bibliotheken bereitstellt, vereinfacht die Einrichtung von Assemblys. Ihre Liste finden Sie auf der offiziellen Website. Bei der Installation von Bibliotheken wird die Abhängigkeitssteuerung verwendet, d. H. Das erforderliche Paket und alles, was für den Betrieb erforderlich ist, wird installiert. Die Tools werden in einer vorkonfigurierten Konfiguration geliefert, z. B. für die statische Montage von 64-Bit-Anwendungen. Dies erleichtert die Montage erheblich.


Die mxe- Umgebung wird im lokalen Ordner des Benutzers bereitgestellt. Installieren Sie dazu einfach Abhängigkeiten über den Paketmanager und klonen Sie das Repository. Im Stammverzeichnis des Repositorys befindet sich ein Makefile , das die Installation der zu diesem Zweck festgelegten Bibliotheken durchführt, Tools für die Montage usw. hinzufügt.


Es ist wichtig zu beachten, dass die Build-Umgebung in ihrem Ordner lokalisiert ist. Auf diese Weise können Sie für jede Anwendung eine individuelle Umgebung konfigurieren.


Containerisieren Sie es


Angenommen, der Release-Build für Windows ist auf dem lokalen Computer konfiguriert. Releases erscheinen ziemlich oft, in einigen Versionen werden neue Bibliotheken hinzugefügt und einige werden beispielsweise gelöscht. Eines schönen Tages verlangt der Boss , den Release-Build für einen Anfänger abzulegen . Wie konfiguriert er seine Build-Umgebung? Welche Bibliotheken sollten aus dem mxe- Repository entnommen werden und für welche Builds aus dem Quellcode?


In diesem Fall können Sie ein Bash- Skript erhalten, das die gesamte Umgebung in einem bestimmten Ordner bereitstellt. Und nachdem Sie versucht haben , dieses Skript auf dem neuesten Stand zu halten. Aber wie die Dokumentation für das Projekt kann es in einem kritischen Moment veraltet sein.


Eine gute Lösung wäre, unsere Build-Umgebung innerhalb des Docker- Containers zu isolieren. Die Docker- Datei selbst enthält in sich geschlossene Anweisungen zum Bereitstellen der Umgebung, und das Vorhandensein eines Containers trägt dazu bei, dass das Heimsystem nicht mit unnötigen Bibliotheken überfüllt wird.


Alles zusammenfügen


Nehmen wir zur Demonstration ein einfaches Qt- Projekt - SimpleQtProject . Dieses Projekt wird vom Dienstprogramm qmake erstellt und besteht aus einem einzigen Formular. Dies geschieht natürlich der Einfachheit halber. Im Docker- Ordner befindet sich auch eine Datei zum Erstellen des Containers.


Betrachten Sie eine Docker- Projektdatei. In der Tat besteht es aus mehreren Hauptteilen:


  • Installieren von Abhängigkeiten für das Build-System
  • Installation und Konfiguration des Montagesystems
  • Kompilieren des Projekts und Kopieren von Artefakten auf das Hostsystem

Im Folgenden werden nur die grundlegenden Befehle aus der Datei berücksichtigt. Für eine vollständige Einarbeitung wird empfohlen, das Repository zu konsultieren.

Wir überspringen den ersten Punkt und fahren direkt mit der Installation von mxe fort .
Klonen Sie das Repository:


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

Zum Zeitpunkt des Schreibens war die neueste Version Build-2019-06-02 . Der Hauptzweig wird hier aus einem einfachen Grund nicht verwendet: Die Wiederholbarkeit der Baugruppe ist erforderlich. Andernfalls kann die Assembly beim Hinzufügen neuer Commits zum Master unterbrochen werden.


Als nächstes konfigurieren wir das Build-System:


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

Dieser Befehl fügt Tools (Instanzen von cmake und Mingw-w64 usw.) für die statische Assemblierung des Projekts unter der 64-Bit-Architektur hinzu und setzt dann Qt mit diesen zusammen.


Der nächste Schritt besteht darin, den Pfad zu den ausführbaren Dateien von mxe in PATH hinzuzufügen:


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

Nachdem die Build-Umgebung konfiguriert wurde, können Sie direkt zum letzten Element wechseln:


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

Hier sind einige Punkte zu klären. Es wird davon ausgegangen, dass beim Starten des Containers der Quellordner mit der Datei * .pro im Ordner / app / src / und der Ort, an dem die Assembly-Ergebnisse hinzugefügt werden sollen, im Verzeichnis / app / res / bereitgestellt werden.


Das Folgende ist ein Beispiel für einen Befehl zum Erstellen eines Docker-Images . Er muss im Docker- Ordner des betreffenden Projekts ausgeführt werden:


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

Die Montage beginnt dort:


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

Bevor Sie den Befehl ausführen, müssen Sie den Ergebnisordner im Docker- Verzeichnis erstellen, um die Ergebnisse zu kopieren.


Anpassung der Baugruppe


Standardmäßig stellt mxe MinGW Version 5.5.0 zur Verfügung (zumindest gilt dies für Build Build-2019-06-02 ).


Wenn das Projekt neue Funktionen von C ++ 17 verwendet , ist eine solche Version des Compilers unbefriedigend. Glücklicherweise bietet die Build-Umgebung neuere Versionen als separate Plugins. Um unser Problem zu lösen, müssen wir dem Team zum Erstellen von Bibliotheken Anweisungen zur Verwendung des entsprechenden Plugins hinzufügen:


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

Dieser Befehl erstellt ein Kit für die statische Assemblierung von 64-Bit-Anwendungen mit dem Compiler der siebten Version ( 7.4.0 ). Wenn ein solches Kit bereits vorhanden ist , wird es nicht geändert.


Eine Liste aller verfügbaren Plugins finden Sie auf der Seite .


Das Verzeichnis mxe / src enthält * .mk- Dateien, die die Assemblyparameter eines bestimmten Pakets beschreiben. Bei Bedarf können Sie die erforderlichen Anpassungen an einem vorhandenen Paket vornehmen oder ein eigenes hinzufügen. Die Dateistruktur wird hier beschrieben - https://github.com/mxe/mxe/wiki/Add-a-New-Package:-Full-Version .


Zum Kopieren von Abhängigkeiten stellt das mxe-Projekt das Dienstprogramm copydlldeps.sh bereit. Dies ist jedoch nicht das einzige nützliche Tool. Die vollständigen Listen finden Sie auf der Seite .


CMake und statische Verbindung Qt


So kam es, dass ich in meinem Projekt Qt und das CMake- Build-System verwendete. Als beschlossen wurde, ein Projekt für Windows zu erstellen, bestand eine hervorragende Lösung darin, alles mithilfe eines statischen Links zu erstellen, um Benutzern eine Binärdatei ohne Abhängigkeiten bereitzustellen.


Bei der Analyse eines Berges von Linkerfehlern konnte festgestellt werden, dass eine solche sofort einsatzbereite Baugruppe nirgendwo anders funktioniert. Tatsache ist, dass qmake bei Verwendung der statischen Verknüpfung eine * .cpp- Datei generiert, die Anweisungen zum Importieren von Plugins zu diesem Typ enthält:


 // 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) 

Flags und Bibliotheken für die Verknüpfungsphase im Makefile werden ebenfalls hinzugefügt.


Sie können versuchen, mit diesem Design in CMakeLists.txt zu experimentieren:


 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 dann die Verwendung von plugin_libs . Für mich brachte dieser Ansatz jedoch keine Ergebnisse.


Am Ende kam ich zu der Entscheidung, alle externen Bibliotheken (wenn möglich) dynamisch zu verknüpfen und zusammen mit der ausführbaren Datei die erforderlichen DLLs mit copydlldeps.sh zu kopieren . Ausführlichere Informationen zur Bereitstellung unter Qt unter Windows finden Sie im Artikel .


Abschließend


Oben wurde gezeigt, wie Sie in wenigen einfachen Schritten die Umgebung für das Cross-Compilieren eines Projekts konfigurieren können. Aber unter realen Bedingungen ist leider nicht alles so rosig.


Obwohl das mxe- Projekt eine beeindruckende Liste von Bibliotheken enthält, enthält es möglicherweise immer noch nicht die von Ihnen benötigten oder zu neue Versionen. Ja, es besteht die Möglichkeit, ein Paket selbst zu erstellen oder im schlimmsten Fall eine Bibliothek aus dem Quellcode zu erstellen. Aber nicht alles kann mit einem Cross-Compiler erstellt werden, daher konnte ich es mit dem cpprestsdk- Projekt nicht tun, da es das installierte vcpkg benötigt .


Viele Probleme, die beim Erstellen eines Projekts mit einem Cross-Compiler auftreten können, sind typisch für die plattformübergreifende Entwicklung im Allgemeinen. Zum Beispiel hatte ich einen seltsamen Fehler aufgrund eines ERROR Aufzählungselements. Es stellte sich heraus, dass in einer der Windows- Header-Dateien ein Makro mit demselben Namen vorhanden ist. Seine Ersetzung hat den gesamten Code gebrochen.


Es ist jedermanns Sache, Cross-Compilation zu verwenden oder nicht. Das brachte mir einen guten Gewinn. Ich habe die Assembly für mehrere Linux- und Windows- Distributionen in separaten Docker- Containern für mein SecureDialogues- Projekt konfiguriert und ein Makefile hinzugefügt, um den Prozess für jeden Container einzeln zu starten. Führen Sie als Nächstes make aus und nach einer Weile erhalte ich die Binärdateien für das erforderliche Betriebssystem im lokalen Ordner.

Source: https://habr.com/ru/post/de456916/


All Articles