« CPP/CrossCompilation » : différence entre les versions

De TartareFR
Aller à la navigation Aller à la recherche
(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-binutils mingw64-gcc mingw64-gcc-c++ mingw64-binutils
dnf install mingw32-gcc mingw32-gcc-c++ mingw32-binutils
</syntaxhighlight>
</syntaxhighlight>
==== Version 32 bits ====
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

TextFileIcon16.png main.c
#include<stdio.h>
int main()
{
  printf("Hello World !\n");
  return 0;
}

Le fichier source en C++

TextFileIcon16.png main.cpp
#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 .
Idea.png
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:
export WINEDEBUG=fixme-all

On peut maintenant tester un exécutable

wine win32/cpp-helloworld.exe
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
Idea.png
@ 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.
TextFileIcon16.png Makefile
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.

TextFileIcon16.png main.cpp
#include <iostream>
#include "data.hpp"

using namespace std;

int main()
{
	Data data;
	data.set( 99 );
	cout << "data: " << data.get() << endl;
	return 0;
}
TextFileIcon16.png data.h
#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_
TextFileIcon16.png data.cpp
#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.

Note.png
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.
TextFileIcon16.png Makefile
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