MXE (M cross environment)
Was ist MXE und wofür braucht man es?
MXE ist ein GNU-Makefile welches einen Cross-Compiler kompiliert und damit dann viele OpenSource Bibliotheken wie z.B. SLD und Qt kompiliert. Kurz gesagt: Ihr könnt mit MXE, Windows Binaries unter UNIX bzw. Linux erstellen ohne selber Windows zu benutzen.
MXE ist so ausgelegt das es auf jedem UNIX System läuft und auch angepasst werden kann. Es baut zusätzlich zum Cross-Compiler viele freie Bibliotheken, lädt die benötigten Pakete selbständig herunter und überprüft diese mit einer eindeutigen Prüfsumme. MXE kann auch die Versionsnummern automatisch aktualisieren und verwendet dafür direkt die jeweiligen Quellpakete. Somit ist Sichergestellt das der gesamte Build Prozess transparent ist.
Um wiederholte Builds zu beschleunigen lässt sich MXE gut in autotools, cmake, qmake und in Handgeschrieben Makefiles integrieren. Ich selber benutze dafür lieber ein Bash Skript da ich die Windows Version maximal zum Release erstelle.
Wer zusätzlich mehr über die Geschichte von MXE wissen möchte kann gerne meine komplette Ausarbeitung als PDF runter laden --> Download.
Einrichtung
An einem Beispiel zeige ich euch wie ihr MXE auf euer System installiert. Ich benutze dafür hier Kubuntu also Ubuntu mit KDE als Desktop.
Vorraussetzungen
Um mit MXE zu starten benötigt es folgende Komponenten die Ihr auf euer System installiert haben müsst. Und das sind folgende:
- Autoconf ≥ 2.68
- Automake ≥ 1.11.3
- Bash
- Bison
- Bzip2
- Flex ≥ 2.5.31
- GCC (gcc, g++)
- gdk-pixbuf
- Git ≥ 1.7
- GNU Coreutils
- GNU Gettext
- GNU gperf
- GNU Make ≥ 3.81
- GNU Sed
- GNU Tar
- Intltool ≥ 0.40
- LibC for 32-bit
- Libtool ≥ 2.2
- Lzip
- OpenSSL-dev ≥ 1.01
- p7zip (7-Zip)
- Patch
- Perl
- Perl XML::Parser
- Python
- Ruby
- UnZip
- Wget
- XZ Utils
- zlib
Ab jetzt muss alles in der Konsole / Terminal ausführen. Unter Debian / Ubuntu reicht dann folgender Aufruf:
sudo apt-get install autoconf automake autopoint bash bison bzip2 flex g++ g++- multilib gettext git gperf intltool libc6-dev-i386 libgdk-pixbuf2.0-dev libltdl-dev libssl-dev libtool-bin libxml-parser-perl lzip make openssl
p7zip-full patch perl python ruby sed unzip wget xz-util
Wer ein anderes Systeme benutzt kann hier nachschauen wie ihr eure Pakete installieren müsst. https://mxe.cc/#requirements
Jetzt können wir MXE von GitHub klonen. Dazu in das Verzeichnis gehen in das MXE erzeugt werden soll, es wird beim klonen ein neues Verzeichnis mit dem Name „mxe“ angelegt.
git clone https://github.com/mxe/mxe.git cd mx
Cross-Compiler und Bibliotheken erzeugen
Weg 1 -- Manuell --
Jetzt könnt ihr anfangen alles zu erstellen was ihr für eure Projekte benötigt. Wir fangen mal mit dem wichtigsten an, dem Cross-Compiler.
make cc --jobs=4 JOBS=3
cc ist das Paket was erstellt werden soll, zudem werden auch automatisch alle Abhängigkeiten erstellt. --jobs gibt an wie viele Pakete Gleichzeitig gebaut werden sollen hier sind es 4 Pakete. JOBS gibt an wie viele Compiler Prozesse für jedes Paket genutzt werden sollen. Dieser Aufruf erzeugt aber lediglich die 32-Bit Variante als Static (i686-w64-mingw32.static).
Möchten wir für ein anderes Target die Bibliotheken erzeugen können wir die Variable "MXE_TARGETS" dem makefile übergeben. Das sieht dann so aus:
make cc --jobs=4 JOBS=3 MXE_TARGETS=x86_64-w64-mingw32.static
Für mehrere gleichzeitig geht dann so was hier:
make cc --jobs=4 JOBS=3 MXE_TARGETS='i686-w64-mingw32.shared x86_64-w64-mingw32.shared'
folgende Targets stehen zur Verfügung:
- i686-w64-mingw32.static
- i686-w64-mingw32.shared
- x86_64-w64-mingw32.static
- x86_64-w64-mingw32.shared
Um jetzt eine beliebige Bibliothek zu erzeugen gehen wir geauso vor wie mit dem Cross-Compiler. In diesem Beispiel wird SLD und SFML erzeugt als 32-Bit Static. Wie man sieht kann man mehrere Pakete angeben die dann alle erzeugt werden.
make sdl smfl --jobs=4 JOBS=3
Möchte man alle Pakete erzeugen reicht dieser Aufruf:
make --jobs=4 JOBS=3
Kostet viel Zeit und ist eigtl. unnötig. Das wäre jetzt der eine Weg wie ihr euren MXE Cross-Compiler incl. Bibliotheken bauen könnt.
Weg 2 per "settings.mk"
Dieser weg benötigt eine Datei die ihr ins mxe Verzeichnis ablegen müsst. Diese muss "settings.mk" heißen. Die Datei sollte wie folgt aufgebaut sein.
JOBS := 3 MXE_TARGETS := i686-w64-mingw32.static x86_64-w64-mingw32.static LOCAL_PKG_LIST := sdl2 sdl2_gfx sdl2_image sdl2_mixer sdl2_net sdl2_ttf .DEFAULT_GOAL := local-pkg-list local-pkg-list: $(LOCAL_PKG_LIST)
Dann reicht es wenn ihr nur noch make -j4 eingebt und ihr könnt auch enspannt zurücklehen und warten bis alles fertig erstellt wurde. Dieser Weg macht es leichter wenn ihr eurer Cross Build hin und wieder neu aufsetzten wollt. Ich habe meine settings.mk an einem anderen Ort gespeichert und kann so schnell alles wieder neubauen und habe dann immer gleich alle meine Bibliotheken die ich benötige. Es ist aber euch überlassen wie ihr eure Bibliotheken bauen lasst. Man kann auch beide Wege mischen. Alles kein Problem.
Updaten
Um alle Pakete auf den neusten Stand zu bringen reicht folgender aufruf.
make update
Möchte man nur eine Liste aller Updates ruft man folgendes auf.
make update UPDATE_DRYRUN=true
Bereinigen
Wenn man alle Libaries erzeugt hat kann man alles etwas entschlacken und unötigen Ballast abwerfen. Das macht man mit
make clean-junk
Hiermit werden alle nicht nenötigten Dateien entfernt, sowie alle unbenutzten Pakete, teporäre Verzeichnisse und alle erzeugten Logs.
Vorsicht! make clean löscht auch das usr Verzeichnis und löscht damit auch alle erstellten Bibiotheken.
Benutzung
Wichtig ist noch, das vor jeder Benutzung von mxe immer der Pfad zu mxe dem System bekannt gemacht wird. Entweder manuell wie hier einmal zu beginn in der Konsole. Wird aber beim beenden dieser nicht gehalten und muss immer wieder gemacht werden.
export PATH=~/mxe/usr/bin:$PATH
Oder man bindet ihn fest ins System mit ein. Unter Debian / Ubuntu legt man dazu unter /etc/profile.d eine neue Datei mit dem Namen mxe_env.sh (könnt ihr auch nennen wie ihr wollt nur .sh muss am Ende stehen) ein und schreibt den oberen Passus darein. Nach einem Neustart könnt ihr mit echo $PATH prüfen ob der mxe Pfad jetzt global definiert ist.
Ein "cmake Projekt"
Dafür legen wir uns einfach ein kleines Projekt an um das cmake build zu testen. Dazu ertsmal ein leeres Verzeichnis anlegen:
mkdir hello_world_cmake cd hello_world_cmake nano main.cpp
Hier ein kleines „Hello World“ Programm welches als Test dienen soll:
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World!" << endl;
return 0;
}
Dazu legen wir uns noch eine CMakeList.txt Datei an ...
nano CmakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(hello_world)
set(SOURCE main.cpp)
add_executable(hello_world ${SOURCE})
Nun können wir uns als erstes mal die native Variante erstellen.
mkdir build_native cd build_nativecd cmake .. make file ./hello_world ./hello_world
Und hier jetzt die Windows Variante mit MXE
mkdir build_win cd build_win i686-w64-mingw32.static-cmake .. make file ./hello_world.exe wine hello_world.exe
Ein "qmake Projekt"
Dafür legen wir uns einfach ein kleines Projekt an um das cmake build zu testen. Dazu erstellen wir erst einmal ein leeres Verzeichnis und eine Datei main.cpp
mkdir hello_world_qmake cd hello_world_qmake nano main.cpp
In die main.cpp kommt dieses kleine „Hello World“ Programm
#include <iostream> using namespace std; int main() { cout << "Hello World!" << endl; return 0; }
Dann erstellen wir uns noch die „hello_world.pro“ unsere Qt Projektdatei.
TARGET = hello_world
TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt
SOURCES += \
main.cpp
Dann kann auch schon qmake von mxe aufgerufen werden und anschließend make:
mkdir build cd buid i686-w64-mingw32.static-qmake-qt5 .. make
Quellen