« CPP/CrossCompilation » : différence entre les versions
(Page créée avec « Pour générer un exécutable windows depuis linux, il faut recourir à ce qu’on appelle la cross-compilation: compiler sur un système pour un autre système. On va par... ») |
Aucun résumé des modifications |
||
| Ligne 1 : | Ligne 1 : | ||
Pour générer un exécutable windows depuis linux, il faut recourir à ce qu’on appelle la cross-compilation: compiler sur un système pour un autre système. | Pour générer un exécutable windows depuis linux, il faut recourir à ce qu’on appelle la cross-compilation: compiler sur un système pour un autre système. | ||
On va partir d’un simple “Hello world”. A ce propos, j’ai découvert une collection d’hello world sur Github. | On va partir d’un simple “Hello world”. A ce propos, j’ai découvert une [https://github.com/leachim6/hello-world collection d’hello world sur Github]. | ||
== Sources == | == Compilation d'un fichier unique == | ||
=== Sources === | |||
Le fichier source en C | Le fichier source en C | ||
| Ligne 26 : | Ligne 28 : | ||
</syntaxhighlight>}} | </syntaxhighlight>}} | ||
== Compilation == | === Compilation manuelle === | ||
=== GNU/Linux === | ==== GNU/Linux ==== | ||
Pour GNU/Linux, le fichier C serait compilé avec la commande | Pour GNU/Linux, le fichier C serait compilé avec la commande | ||
| Ligne 38 : | Ligne 40 : | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Wind@uze === | ==== Wind@uze ==== | ||
On va se servir des compilateurs mingw | On va se servir des compilateurs mingw (32 et/ou 64 bits) | ||
===== Version 32 bits ===== | |||
On installe les compilateurs C et C++ <class>mingw</class> 32 bits ainsi que la collection d'utilitaires | |||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
dnf install mingw32-gcc mingw32-gcc-c++ mingw32 | dnf install mingw32-gcc mingw32-gcc-c++ mingw32-binutils | ||
</syntaxhighlight> | </syntaxhighlight> | ||
le fichier C sera compilé avec la commande | le fichier C sera compilé avec la commande | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Ligne 55 : | Ligne 58 : | ||
i686-w64-mingw32-g++ -O0 -Wall -g -o hellocpp32.exe main.cpp | i686-w64-mingw32-g++ -O0 -Wall -g -o hellocpp32.exe main.cpp | ||
</syntaxhighlight> | </syntaxhighlight> | ||
En testant le binaire généré à partir du C++ avec wine, on se rend compte qu’il ne fonctionne pas (absence de DLLs). | En testant le binaire généré à partir du C++ avec <app>wine</app>, on se rend compte qu’il ne fonctionne pas (absence de DLLs). | ||
{{color|red|err:module:import_dll Library libstdc++-6.dll (which is needed by L"V:\\hello32.exe") not found}} | {{color|red|err:module:import_dll Library libstdc++-6.dll (which is needed by L"V:\\hello32.exe") not found}} | ||
| Ligne 65 : | Ligne 68 : | ||
cp /usr/i686-w64-mingw32/sys-root/mingw/bin/libwinpthread-1.dll . | cp /usr/i686-w64-mingw32/sys-root/mingw/bin/libwinpthread-1.dll . | ||
</syntaxhighlight> | </syntaxhighlight> | ||
{{Admon/tip|Test des exécutables avec wine|Wine affiche énormément de trace de debug (FIXME) dans la console et le message provenant de nos exécutables risque d'être noyé au milieu des traces de wine. Il est préférable de désactiver certaines traces: | |||
<syntaxhighlight lang="bash">export WINEDEBUG=fixme-all</syntaxhighlight> | |||
On peut maintenant tester un exécutable | |||
<syntaxhighlight lang="bash">wine win32/cpp-helloworld.exe</syntaxhighlight>}} | |||
==== Version 64 bits ==== | ===== Version 64 bits ===== | ||
On installe les compilateurs C et C++ <class>mingw</class> 64 bits ainsi que la collection d'utilitaires | |||
<syntaxhighlight lang="bash"> | |||
dnf install mingw64-gcc mingw64-gcc-c++ mingw64-binutils | |||
</syntaxhighlight> | |||
le fichier C sera compilé avec la commande | le fichier C sera compilé avec la commande | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Ligne 84 : | Ligne 95 : | ||
cp /usr/x86_64-w64-mingw32/sys-root/mingw/bin/libwinpthread-1.dll . | cp /usr/x86_64-w64-mingw32/sys-root/mingw/bin/libwinpthread-1.dll . | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Compilation avec Makefile === | |||
Chaque architecture aura son propre répertoire d'accueil afin d'éviter le chevauchement de DLLs (même nom mais architecture différente) et fabriquera deux binaires: un avec un fichier source en '''C''' et l'autre avec un fichier source en '''C++'''. | |||
On va définir plusieurs cibles principales: | |||
* '''linux''': ce sera la cible par défaut et elle construira les binaires ELF dans le répertoire <path>linux</path> | |||
* '''win32''': elle construira les binaires Windows 32 bits dans le répertoire <path>win32</path> | |||
* '''win64''': elle construira les binaires Windows 64 bits dans le répertoire <path>win64</path> | |||
* '''win''': elle lancera les cibles win32 et win64 | |||
* '''all''': elle lancera les cibles linux, win32 et win64 | |||
* '''clean''': elle nettoiera le projet | |||
D'autres cibles secondaires seront définies: | |||
* '''tree''': Créera si besoin le répertoire d'accueil des binaires | |||
* '''copy-dll32''': copiera si besoin les DLLs 32 bits dans le répertoire d'accueil des binaires Windows 32 bits | |||
* '''copy-dll64''': copiera si besoin les DLLs 64 bits dans le répertoire d'accueil des binaires Windows 64 bits | |||
{{Admon/tip|@ dans les commandes|Certaines commandes du fichier <path>Makefile</path> sont précédées d'un '''@''' afin de ne pas écrire la commande en cours sur la sortie standard.}} | |||
{{Admon/file|Makefile|<syntaxhighlight lang="make"> | |||
CFLAGS=-O0 -Wall -g | |||
CXXFLAGS=-O0 -Wall -g | |||
LDFLAGS= | |||
WINFLAGS= | |||
CTARGET=c-helloworld | |||
CXXTARGET=cpp-helloworld | |||
DLL32_PATH := /usr/i686-w64-mingw32/sys-root/mingw/bin | |||
DLL64_PATH := /usr/x86_64-w64-mingw32/sys-root/mingw/bin | |||
linux: tree c-linux cpp-linux | |||
win: win32 win64 | |||
win32: tree c-win32.exe cpp-win32.exe copy-dll32 | |||
win64: tree c-win64.exe cpp-win64.exe copy-dll64 | |||
all: linux win | |||
tree: | |||
@[ -d linux ] || mkdir linux | |||
@[ -d win32 ] || mkdir win32 | |||
@[ -d win64 ] || mkdir win64 | |||
cpp-linux: | |||
@g++ $(CXXFLAGS) -o linux/$(CXXTARGET) main.cpp | |||
c-linux: | |||
@gcc $(CFLAGS) -o linux/$(CTARGET) main.c | |||
c-win32.exe: | |||
@i686-w64-mingw32-gcc $(CFLAGS) -o win32/$(CTARGET).exe main.c | |||
cpp-win32.exe: | |||
@i686-w64-mingw32-g++ $(CXXFLAGS) -o win32/$(CXXTARGET).exe main.cpp | |||
c-win64.exe: | |||
@x86_64-w64-mingw32-gcc $(CFLAGS) -o win64/$(CTARGET).exe main.c | |||
cpp-win64.exe: | |||
@x86_64-w64-mingw32-g++ $(CXXFLAGS) -o win64/$(CXXTARGET).exe main.cpp | |||
copy-dll32: | |||
@[ -f libwinpthread-1.dll ] || cp $(DLL32_PATH)/libwinpthread-1.dll win32/ | |||
@[ -f libstdc++-6.dll ] || cp $(DLL32_PATH)/libstdc++-6.dll win32/ | |||
@[ -f libgcc_s_sjlj-1.dll ] || cp $(DLL32_PATH)/libgcc_s_sjlj-1.dll win32/ | |||
copy-dll64: | |||
@[ -f libwinpthread-1.dll ] || cp $(DLL64_PATH)/libwinpthread-1.dll win64/ | |||
@[ -f libstdc++-6.dll ] || cp $(DLL64_PATH)/libstdc++-6.dll win64/ | |||
@[ -f libgcc_s_seh-1.dll ] || cp $(DLL64_PATH)/libgcc_s_seh-1.dll win64/ | |||
clean: | |||
@rm -rf linux win32 win64 | |||
</syntaxhighlight>}} | |||
== Compilation de plusieurs fichiers sources == | |||
=== Sources === | |||
Afin de simplifier les explications, on va se concentrer sur le <class>C++</class> et utiliser un fichier Makefile. Cette fois les sources seront un fichier principal (<path>main.cpp</path>) ainsi qu'un fichier de définition (<path>data.h</path>) et d'implémentation (<path>data.cpp</path>) d'une classe. | |||
{{Admon/file|main.cpp|<syntaxhighlight lang="cpp"> | |||
#include <iostream> | |||
#include "data.hpp" | |||
using namespace std; | |||
int main() | |||
{ | |||
Data data; | |||
data.set( 99 ); | |||
cout << "data: " << data.get() << endl; | |||
return 0; | |||
} | |||
</syntaxhighlight>}} | |||
{{Admon/file|data.h|<syntaxhighlight lang="cpp"> | |||
#ifndef _DATA_HPP_ | |||
#define _DATA_HPP_ | |||
#include <iostream> | |||
class Data | |||
{ | |||
public: | |||
Data(); | |||
~Data(); | |||
void set( int number ); | |||
int get() const; | |||
protected: | |||
int _number; | |||
}; | |||
int get(); | |||
#endif //_DATA_HPP_ | |||
</syntaxhighlight>}} | |||
{{Admon/file|data.cpp|<syntaxhighlight lang="cpp"> | |||
#include "data.h" | |||
Data::Data() : | |||
_number( 0 ) | |||
{ | |||
} | |||
Data::~Data() | |||
{ | |||
} | |||
void Data::set( int number ) | |||
{ | |||
_number = number; | |||
} | |||
int Data::get() const | |||
{ | |||
return _number; | |||
} | |||
</syntaxhighlight>}} | |||
=== Fichier Makefile === | |||
Cette fois on va définir un modèle générique afin de compiler chaque fichier source cpp en fichier objet et lier ces fichiers objet entre eux pour obtenir un exécutable. | |||
{{Admon/note|Fichiers temporaires|Les fichiers temporaires ne sont pas isolés dans leur répertoire d'accueil respectif, mais cela ne pose pas de problème de conflit entre les architectures.}} | |||
{{Admon/file|Makefile|<syntaxhighlight lang="make"> | |||
CXXFLAGS=-O0 -Wall -g | |||
LDFLAGS= | |||
CXXTARGET=data | |||
DLL32_PATH := /usr/i686-w64-mingw32/sys-root/mingw/bin | |||
DLL64_PATH := /usr/x86_64-w64-mingw32/sys-root/mingw/bin | |||
linux: tree linux.exe | |||
win: win32 win64 | |||
win32: tree win32.exe copy-dll32 | |||
win64: tree win64.exe copy-dll64 | |||
all: linux win | |||
tree: | |||
@[ -d linux ] || mkdir linux | |||
@[ -d win32 ] || mkdir win32 | |||
@[ -d win64 ] || mkdir win64 | |||
%.o: %.cpp $(CXXDEPS) | |||
@g++ -c -o $@ $< $(CXXFLAGS) | |||
%.obj32: %.cpp $(CXXDEPS) | |||
@i686-w64-mingw32-g++ -c -o $@ $< $(CXXFLAGS) | |||
%.obj64: %.cpp $(CXXDEPS) | |||
@x86_64-w64-mingw32-g++ -c -o $@ $< $(CXXFLAGS) | |||
linux.exe: main.o data.o | |||
@g++ $(CXXFLAGS) -o linux/$(CXXTARGET) main.o data.o | |||
win32.exe: main.obj32 data.obj32 | |||
@i686-w64-mingw32-g++ $(CXXFLAGS) -o win32/$(CXXTARGET).exe main.obj32 data.obj32 | |||
win64.exe: main.obj64 data.obj64 | |||
@x86_64-w64-mingw32-g++ $(CXXFLAGS) -o win64/$(CXXTARGET).exe main.obj64 data.obj64 | |||
copy-dll32: | |||
@[ -f libwinpthread-1.dll ] || cp $(DLL32_PATH)/libwinpthread-1.dll win32/ | |||
@[ -f libstdc++-6.dll ] || cp $(DLL32_PATH)/libstdc++-6.dll win32/ | |||
@[ -f libgcc_s_sjlj-1.dll ] || cp $(DLL32_PATH)/libgcc_s_sjlj-1.dll win32/ | |||
copy-dll64: | |||
@[ -f libwinpthread-1.dll ] || cp $(DLL64_PATH)/libwinpthread-1.dll win64/ | |||
@[ -f libstdc++-6.dll ] || cp $(DLL64_PATH)/libstdc++-6.dll win64/ | |||
@[ -f libgcc_s_seh-1.dll ] || cp $(DLL64_PATH)/libgcc_s_seh-1.dll win64/ | |||
clean: | |||
@rm -rf linux win32 win64 *.o *.obj32 *.obj64 | |||
</syntaxhighlight>}} | |||
Dernière version du 5 janvier 2016 à 09:48
Pour générer un exécutable windows depuis linux, il faut recourir à ce qu’on appelle la cross-compilation: compiler sur un système pour un autre système. On va partir d’un simple “Hello world”. A ce propos, j’ai découvert une collection d’hello world sur Github.
Compilation d'un fichier unique
Sources
Le fichier source en C
Le fichier source en C++
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World !" << endl;
return 0;
}
Compilation manuelle
GNU/Linux
Pour GNU/Linux, le fichier C serait compilé avec la commande
gcc -O0 -Wall -g -o helloc main.c
et le fichier C++
g++ -O0 -Wall -g -o hellocpp main.cpp
Wind@uze
On va se servir des compilateurs mingw (32 et/ou 64 bits)
Version 32 bits
On installe les compilateurs C et C++ <class>mingw</class> 32 bits ainsi que la collection d'utilitaires
dnf install mingw32-gcc mingw32-gcc-c++ mingw32-binutils
le fichier C sera compilé avec la commande
i686-w64-mingw32-gcc -O0 -Wall -g -o helloc32.exe main.c
et le fichier C++
i686-w64-mingw32-g++ -O0 -Wall -g -o hellocpp32.exe main.cpp
En testant le binaire généré à partir du C++ avec <app>wine</app>, on se rend compte qu’il ne fonctionne pas (absence de DLLs).
err:module:import_dll Library libstdc++-6.dll (which is needed by L"V:\\hello32.exe") not found
Pour résoudre ce problème, il va falloir copié quelques DLLs dans le même répertoire que l’exécutable
cp /usr/i686-w64-mingw32/sys-root/mingw/bin/libstdc++-6.dll .
cp /usr/i686-w64-mingw32/sys-root/mingw/bin/libgcc_s_sjlj-1.dll .
cp /usr/i686-w64-mingw32/sys-root/mingw/bin/libwinpthread-1.dll .
Version 64 bits
On installe les compilateurs C et C++ <class>mingw</class> 64 bits ainsi que la collection d'utilitaires
dnf install mingw64-gcc mingw64-gcc-c++ mingw64-binutils
le fichier C sera compilé avec la commande
x86_64-w64-mingw32-gcc -O0 -Wall -g -o helloc64.exe main.c
et le fichier C++
x86_64-w64-mingw32-g++ -O0 -Wall -g -o hellocpp64.exe main.cpp
Pour résoudre le même problème que la version 32 bits, il va falloir copié quelques DLLs dans le même répertoire que l’exécutable.
cp /usr/x86_64-w64-mingw32/sys-root/mingw/bin/libstdc++-6.dll .
cp /usr/x86_64-w64-mingw32/sys-root/mingw/bin/libgcc_s_seh-1.dll .
cp /usr/x86_64-w64-mingw32/sys-root/mingw/bin/libwinpthread-1.dll .
Compilation avec Makefile
Chaque architecture aura son propre répertoire d'accueil afin d'éviter le chevauchement de DLLs (même nom mais architecture différente) et fabriquera deux binaires: un avec un fichier source en C et l'autre avec un fichier source en C++.
On va définir plusieurs cibles principales:
- linux: ce sera la cible par défaut et elle construira les binaires ELF dans le répertoire <path>linux</path>
- win32: elle construira les binaires Windows 32 bits dans le répertoire <path>win32</path>
- win64: elle construira les binaires Windows 64 bits dans le répertoire <path>win64</path>
- win: elle lancera les cibles win32 et win64
- all: elle lancera les cibles linux, win32 et win64
- clean: elle nettoiera le projet
D'autres cibles secondaires seront définies:
- tree: Créera si besoin le répertoire d'accueil des binaires
- copy-dll32: copiera si besoin les DLLs 32 bits dans le répertoire d'accueil des binaires Windows 32 bits
- copy-dll64: copiera si besoin les DLLs 64 bits dans le répertoire d'accueil des binaires Windows 64 bits
CFLAGS=-O0 -Wall -g
CXXFLAGS=-O0 -Wall -g
LDFLAGS=
WINFLAGS=
CTARGET=c-helloworld
CXXTARGET=cpp-helloworld
DLL32_PATH := /usr/i686-w64-mingw32/sys-root/mingw/bin
DLL64_PATH := /usr/x86_64-w64-mingw32/sys-root/mingw/bin
linux: tree c-linux cpp-linux
win: win32 win64
win32: tree c-win32.exe cpp-win32.exe copy-dll32
win64: tree c-win64.exe cpp-win64.exe copy-dll64
all: linux win
tree:
@[ -d linux ] || mkdir linux
@[ -d win32 ] || mkdir win32
@[ -d win64 ] || mkdir win64
cpp-linux:
@g++ $(CXXFLAGS) -o linux/$(CXXTARGET) main.cpp
c-linux:
@gcc $(CFLAGS) -o linux/$(CTARGET) main.c
c-win32.exe:
@i686-w64-mingw32-gcc $(CFLAGS) -o win32/$(CTARGET).exe main.c
cpp-win32.exe:
@i686-w64-mingw32-g++ $(CXXFLAGS) -o win32/$(CXXTARGET).exe main.cpp
c-win64.exe:
@x86_64-w64-mingw32-gcc $(CFLAGS) -o win64/$(CTARGET).exe main.c
cpp-win64.exe:
@x86_64-w64-mingw32-g++ $(CXXFLAGS) -o win64/$(CXXTARGET).exe main.cpp
copy-dll32:
@[ -f libwinpthread-1.dll ] || cp $(DLL32_PATH)/libwinpthread-1.dll win32/
@[ -f libstdc++-6.dll ] || cp $(DLL32_PATH)/libstdc++-6.dll win32/
@[ -f libgcc_s_sjlj-1.dll ] || cp $(DLL32_PATH)/libgcc_s_sjlj-1.dll win32/
copy-dll64:
@[ -f libwinpthread-1.dll ] || cp $(DLL64_PATH)/libwinpthread-1.dll win64/
@[ -f libstdc++-6.dll ] || cp $(DLL64_PATH)/libstdc++-6.dll win64/
@[ -f libgcc_s_seh-1.dll ] || cp $(DLL64_PATH)/libgcc_s_seh-1.dll win64/
clean:
@rm -rf linux win32 win64
Compilation de plusieurs fichiers sources
Sources
Afin de simplifier les explications, on va se concentrer sur le <class>C++</class> et utiliser un fichier Makefile. Cette fois les sources seront un fichier principal (<path>main.cpp</path>) ainsi qu'un fichier de définition (<path>data.h</path>) et d'implémentation (<path>data.cpp</path>) d'une classe.
#include <iostream>
#include "data.hpp"
using namespace std;
int main()
{
Data data;
data.set( 99 );
cout << "data: " << data.get() << endl;
return 0;
}
#ifndef _DATA_HPP_
#define _DATA_HPP_
#include <iostream>
class Data
{
public:
Data();
~Data();
void set( int number );
int get() const;
protected:
int _number;
};
int get();
#endif //_DATA_HPP_
#include "data.h"
Data::Data() :
_number( 0 )
{
}
Data::~Data()
{
}
void Data::set( int number )
{
_number = number;
}
int Data::get() const
{
return _number;
}
Fichier Makefile
Cette fois on va définir un modèle générique afin de compiler chaque fichier source cpp en fichier objet et lier ces fichiers objet entre eux pour obtenir un exécutable.
CXXFLAGS=-O0 -Wall -g
LDFLAGS=
CXXTARGET=data
DLL32_PATH := /usr/i686-w64-mingw32/sys-root/mingw/bin
DLL64_PATH := /usr/x86_64-w64-mingw32/sys-root/mingw/bin
linux: tree linux.exe
win: win32 win64
win32: tree win32.exe copy-dll32
win64: tree win64.exe copy-dll64
all: linux win
tree:
@[ -d linux ] || mkdir linux
@[ -d win32 ] || mkdir win32
@[ -d win64 ] || mkdir win64
%.o: %.cpp $(CXXDEPS)
@g++ -c -o $@ $< $(CXXFLAGS)
%.obj32: %.cpp $(CXXDEPS)
@i686-w64-mingw32-g++ -c -o $@ $< $(CXXFLAGS)
%.obj64: %.cpp $(CXXDEPS)
@x86_64-w64-mingw32-g++ -c -o $@ $< $(CXXFLAGS)
linux.exe: main.o data.o
@g++ $(CXXFLAGS) -o linux/$(CXXTARGET) main.o data.o
win32.exe: main.obj32 data.obj32
@i686-w64-mingw32-g++ $(CXXFLAGS) -o win32/$(CXXTARGET).exe main.obj32 data.obj32
win64.exe: main.obj64 data.obj64
@x86_64-w64-mingw32-g++ $(CXXFLAGS) -o win64/$(CXXTARGET).exe main.obj64 data.obj64
copy-dll32:
@[ -f libwinpthread-1.dll ] || cp $(DLL32_PATH)/libwinpthread-1.dll win32/
@[ -f libstdc++-6.dll ] || cp $(DLL32_PATH)/libstdc++-6.dll win32/
@[ -f libgcc_s_sjlj-1.dll ] || cp $(DLL32_PATH)/libgcc_s_sjlj-1.dll win32/
copy-dll64:
@[ -f libwinpthread-1.dll ] || cp $(DLL64_PATH)/libwinpthread-1.dll win64/
@[ -f libstdc++-6.dll ] || cp $(DLL64_PATH)/libstdc++-6.dll win64/
@[ -f libgcc_s_seh-1.dll ] || cp $(DLL64_PATH)/libgcc_s_seh-1.dll win64/
clean:
@rm -rf linux win32 win64 *.o *.obj32 *.obj64