Une compilation se fait en trois étapes :
Sous GNU/Linux, le préprocesseur/compilateur s'appelle gcc et le linker ld.
Pour compiler un programme, il suffira de taper une des séquences de commandes suivantes :
gcc -o nom_exéctable fichier1.c fichier2.c ...
gcc -c fichier1.c fichier2.c ...
gcc -o nom_exécutable *.o
Si le programme utilise une library « test », il vous faudra rajouter dans les dernières lignes « -ltest »
==Compilation avec Makefile=====Introduction===
La méthode précédente n'est pas vraiment pratique pour développer un projet ayant plusieurs dizaines de fichiers. On peut bien faire un script shell pour automatiser la compilation, mais, dans ce cas, il faut alors recompiler tous les fichiers à chaque fois que l'on fait un modification dans un seul alors que la simple recompilation du fichier suffirait. Pour cela, il existe make et les Makefile.
make est un logiciel traditionnel des systèmes UNIX. Il sert à appeler des commandes créant des fichiers. À la différence d'un simple script shell, make exécute les commandes seulement si c'est nécessaire. Le but est d'arriver à un résultat (logiciel compilé ou installé, documentation créée, etc.) sans nécessairement refaire toutes les étapes.
Make fut à l'origine développé par le docteur Stuart I. Feldman, en 1977. Ce dernier travaillait pour Bell Labs à cette époque.
Depuis, plusieurs dérivés ont été développés, les plus connus et utilisés sont ceux de BSD et celui de GNU, ce dernier étant généralement celui utilisé par défaut avec les systèmes Linux. Ils diffèrent par certaines fonctionnalités, et par exemple les scripts prévus pour GNU Make peuvent ne pas fonctionner sous BSD Make.
Il sert principalement à faciliter la compilation et l'édition des liens puisque dans ce processus le résultat final dépend d'opérations précédentes : la compilation doit réussir pour que l'édition des liens puisse se faire.
Pour ce faire, make utilise un fichier de configuration appelé makefile qui porte souvent le nom de Makefile. Ce dernier décrit des cibles (qui sont souvent des fichiers, mais pas toujours), de quelles autres cibles elles dépendent, et par quelles actions (des commandes) y parvenir.
Afin de reconstruire une cible spécifiée par l'utilisateur, Make va chercher les cibles nécessaires à la reconstruction de cette cible, et ce récursivement.
Les règles de dépendance peuvent être explicites (noms de fichiers donnés explicitement) ou implicites (via des motifs de fichiers; par exemple fichier.o dépend de fichier.c, si celui-ci existe, via une recompilation).
===Syntaxe d'un Makefile======Syntaxe générale===
Un fichier Makefile contient donc plusieurs types de lignes :
cible : dépendances
<TAB>action
cible peut être :
dépendances peut être :
Si l'action se fait en plusieurs étapes, on utilisera la syntaxe suivante :
cible : dépendances
<TAB>étape1 \
<TAB>étape2 \
...
<TAB>étapen-1 \
<TAB>étapen
Voici un exemple de makefile :
# Specify compiler
CC = gcc
# Specify compiler options
CFLAGS = -g
LDFLAGS = -L/usr/openwin/lib
LDLIBS = -lX11 -lXext
# Needed to recognize .c as a file suffix
.SUFFIXES: $(SUFFIXES) .
# Executable name
PROG = life
# List of object file needed for the final program
OBJS = main.o window.o Board.o Life.o BoundedBoard.o
all: $(PROG)
# Program compilation and linking steps
$(PROG): $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -o $(PROG) $(OBJS)
.cpp.o:
$(CC) $(CFLAGS) -c $*.c
Dans cet exemple: .ccp.o est une cible. Par défaut les cibles sont des fichiers, et c'est le cas ici. Cette cible ne dépend d'aucune autre puisqu'il n'y a rien d'autre sur la même ligne après :.
Pour parvenir à cette cible, il faut exécuter l'action, la commande $(CC) $(CFLAGS) -c $*.c
all est une autre cible qui dépend de ${PROG} (et donc de life, qui est un fichier)
$(PROG) est une cible qui dépend de $(OBJ) (et donc de main.o window.o Board.o Life.o et BoundedBoard.o). Pour y parvenir, l'action est d'exécuter la commande $(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -o $(PROG) $(OBJS)
==Compilation avec Autotools=====Pourquoi Autotools===
La méthode de makefile est déjà beaucoup plus intéressante que la compilation manuelle. Cependant, à chaque évolution de votre projet, il est nécessaire de maintenir votre Makefile. De plus, il se peut qu'il faille réaliser un Makefile spécifique pour un système Unix particulier (en plus de modification de votre code). Maintenir un fichier Makefile devient vite fastidieux. On a donc inventer les Autotools afin de simplifier la maintenance des Makefile et leur portabilité.
Ces outils ont deux objectifs principaux :
Seul les développeurs de programme doivent avoir installés les outils présentés ci-après. Les personnes qui veulent simplement installer un logiciel à partir des sources n'ont pas besoin d'outils spéciaux, excepté un shell Unix, un programme make et un compilateur C.
Voici les deux outils principaux :
Pour commencer à utiliser ces outils et construire un package près à l'installation, vous devez écrire au moins trois fichiers (configure.in, makefile.am, acconfig.h) et lancer certains outils pour générer les fichiers additionnels.
Voici le schéma des dépendances entre les fichiers et les outils (pour l'instant on ne prend pas en compte acconfig.h)
Légende :
Les deux outils possèdent la même philosophie. A partir de fichier simple écrit par le développeur, ils vont générer des fichiers "moules", qui seront complétés au dernier moment, lors de la compilation.
===Outil Autoconf======Principe===
Autoconf est un outil qui génère un script exécutable (configure) pour adapter l'installation de nos codes sources en fonction du système et de la machine d'installation. Le script configure est indépendant d'Autoconf, ainsi l'utilisateur n'a pas besoin d'avoir l'outil Autoconf.
Ce script est créé à partir d'un fichier appelé configure.in, qui contient la description de toutes les options et spécificités que l ’on veut tester.
Ensuite, c'est le script configure qui effectuera réellement les tests. Il convertira le fichier "moule" Makefile.in en véritable Makefile.
Le principal fichier pour la génération du script configure est comme vous l'avez compris le fichier configure.in.
Ce qui donne le schéma de relation suivant :
Légende :
Voici quelques règles pour écrire un fichier Configure.in :
Si vous exécuter autoscan dans le dossier de ce projet, vous obtiendrez ceci après ajout de AM_INIT_AUTOMAKE et retirer les * autour de config.h dans AC_CONFIG_HEADER :
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
#définit les informations sur le programme
AC_INIT(nom_du_programme, version_mineur_point_majeur, votre_email)
#le fichier source
AC_CONFIG_SRCDIR([main.c * )
#le fichier contenant les #define de fonctionnalités du système
AC_CONFIG_HEADER(config.h)
#initialisation d'automake pour le programme
AM_INIT_AUTOMAKE(nom_du_programme, version_mineur_point_majeur)
# Checks for programs.
#vérifier la présence d'un compilo C++
AC_PROG_CXX
#vérifier la présence d'un compilo C
AC_PROG_CC
#vérifier la présence d'un préprocesseur C
AC_PROG_CPP
#vérifier la présence du programme install (compatible BSD)
AC_PROG_INSTALL
#vérifier la présence de l'option -s de ln
AC_PROG_LN_S
#vérifier la présence de la commande make
AC_PROG_MAKE_SET
#vérifier la présence de la commande ranlib
AC_PROG_RANLIB
# Checks for libraries.
# Checks for header files.
#vérifier la présence des entêtes de la libc
AC_HEADER_STDC
#vérifier la présence de la liste de fichier en argument
AC_CHECK_HEADERS([stdlib.h string.h unistd.h * )
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
#vérifier le comportement de malloc (en particulier, taille nulle)
AC_FUNC_MALLOC
#vérifier la présence des fonctions passés en argument
AC_CHECK_FUNCS([getcwd strdup strstr * )
#fichier Makefile à générer
AC_CONFIG_FILES([Makefile * )
#générer les Makefiles
AC_OUTPUT
Vous pouvez renommer ce fichier de configure.scan en configure.in.
Une fois le fichier configure.in écrit, il faut lancer les commandes suivantes :
[user * $ aclocal
[user * $ autoconf
La première permet d'interpréter la macro pour automake (et créé le fichier aclocal.m4) et la seconde génère tout simplement le script configure.
===Outil Automake======Principe===
Automake est un outil qui permet de minimiser les tâches de maintenance des Makefile, en décrivant la structure de l'application. En fait, il génère un (ou des) fichier(s) "moule" du Makefile, nommé Makefile.in, à partir d'une série de macros contenues dans le(s) fichier(s) Makefile.am. Cet outil est souvent associé à Autcoconf.
Automake se base sur le fichier Configure.in pour savoir quels sont les Makefile.in à générer. La description de ce que doit contenir un de ces fichiers est elle-même contenue dans un fichier Makefile.am.
Ensuite le fichier Makefile.in est celui qui va être transformé par l'outil configure en véritable Makefile.
Il faut enfin noter que le « langage » utilisé par Automake n'est pas procédural mais formel.
L'outil Automake s'appuie sur deux fichiers pour générer le Makefile.in. Le principal bien sur est le fichier Makefile.am.
variable = valeur
Il peut également contenir des définitions de "cible" de Makefike (ex. : clean, install...). Toutes ces définitions vont induire les règles du Makefile.
AM_INIT_AUTOMAKE(nom_du_programme, version_mineur_point_majeur)
Les paramètres à la directive AM_INIT_AUTOMAKE permettent de définir le nom du package correspondant au programme, ainsi que son numéro de version.
Voici quelques règles pour écrire des fichiers Makefile.am et Configure.in :
SUBDIRS = sous_dossier1 sous_dossier2 ...
bin_PROGRAMS = nom_programme
nom_programme_SOURCES = fichier1 fichier2 ...
Prenons un exemple plus complet :
## fichier à passer à Automake pour générer le fichier Makefile.in
SUBDIRS = src doc
bin_PROGRAMS = exemple
exemple_SOURCES = exemple.c fonction.c exemple.h
exemple_LDADD = libexemple.a
Et enfin quelques explications :
D'autres macros non présentées dans cet exemple sont disponibles. Elles doivent être précédées du nom du fichier (les points sont remplacés par _) auquel elles s'appliquent. On peut citer :
On peut aussi citer au même titre que bin_PROGRAMS, l'équivalent pour créer une library ou inclure un fichier dans les include du système
Génération du Makefile.in
Il ne vous reste plus qu'à lancer la commande suivante pour générer le fichier Makefile.in :
[user * $ automake --add-missing
L'option passée à Automake l'informe qu'il doit installer les fichiers qui pourraient éventuellement manquer. En effet, Automake requiert certains fichiers dans certaines situations. Il est aussi possible de spécifier l'option --foreign, pour avoir un niveau de rigueur décontracté (par opposition à --gnu, option par défaut, qui requiert les fichiers INSTALL, NEWS, README, COPYING, AUTHORS, et ChangeLog dans le répertoire source). Pour les autres options admises par Automake, voir le manuel Automake, comme d'habitude...
===Gestion de la portabilité par config.h (outil Autoheader)======Principe===
Cet outil est utilisé pour la gestion des variables du pré-processeur (#define, #undef), et pour la gestion des fichiers d ’en-tête et leurs fonctions (stdlib.h, fonction free…). Le but est de savoir si tel « .h » ou fonction existe sur le système sur lequel on compile. Il créer un fichier "patron" (config.h.in) comprenant un ensemble de #define pour l'outils configure à partir de acconfig.h et Configure.in. Voici un petit schéma pour mieux comprendre l'intégration de cet outil avec les autres :
Légende :
L'outils Autoheader scanne le fichier Configure.in et recherche quels sont les symboles du pré-processeur C qu'il doit définir.
Voici les macros que l'on peut/doit trouver dans le fichier Configure.in :
Cette macro (nécessaire) spécifie le nom du fichier d'en-tête qui contiendra les définitions de macros du pré-processeur. Normalement, il s'agit de config.h. Cette macro peut optionnellement définir le nom du fichier créé par Autoheader (en entrée de configure). Par défaut, il s'agit de config.h.in. Mais ce nom n'est pas "génial" pour le système de fichier DOS. Il peut être mieux de le renommer tout simplement config.in. Ce qui donnera la macro suivante : AM_CONFIG_HEADER(config.h:config.in)
Permet de vérifier la présence ou non d'un (ou des) fichier(s) d'en-tête, listé(s) en argument (séparé par des espaces). Ainsi, dans le fichier config.h.in, on aura le code suivant :
/* Define as 1 if you have unistd.h. */
#define HAVE_UNISTD_H 1
Sur les systèmes qui ont 'unistd.h', le script configure décommentera. Sur les autres, il laissera la ligne inchangée.
Permet de vérifier la présence ou non d'un (ou des) fonction(s), listée(s) en argument (séparé par des espaces). Ainsi, dans le fichier config.h.in, on aura le code suivant :
/* Define as 1 if you have strstr. */
#define HAVE_STRSTR 1
Sur les systèmes qui ont 'strstr', le script configure décommentera. Sur les autres, il laissera la ligne inchangée.
Pour utiliser le fichier config.h, on inclura en haut de chaque fichier source :
#ifdef HAVE_CONFIG_H
#include « config.h »
#endif
L'autre fichier utilisé est le fichier acconfig.h :
Si vous générer un fichier d'en-tête portable (en utilisant la macro AM_CONFIG_HEADER dans le fichier Configure.in), alors il faut écrire le fichier acconfig.h, qui contiendra les lignes suivantes :
/* Name of package */
#undef PACKAGE
/* Version of package */
#undef VERSION
L'outil Autoheader informe toujours par un message d'erreur lorsqu'il manque un élément dans le fichier acconfig.h.
===Outil Configure======Principe===
L'outil Configure est un script shell, qui va convertir les fichiers "moules" dans leur formes définitives (Makefile.in -> Makefile). Il effectue les tests souhaités avant la compilation et remplace les paramètres de configuration par leurs valeurs appropriées. Ce script est écrit de la manière la plus portable possible.
Ce script shell est fourni régulièrement avec le package pour l'utilisateur final. Il est créé par le développeur en lançant l'outil Autoconf.
Voici le schéma représentant les fichiers qui interagissent avec Configure :
Légende :
===Quelques options===Généralités
Il est possible de choisir le répertoire d'installation du programme. Pour cela, il faut utiliser l'option --prefix lors de l'utilisation de configure. Par défaut, ce répertoire est /usr/local.
On peut aussi spécifier si certaines fonctionnalités devront être activées ou désactivées :
--disable-FEATURE ne pas inclure une fonctionnalité
--enable-FEATURE=no ne pas inclure une fonctionnalité
--enable-FEATURE=yes inclure une fonctionnalité
--with-PACKAGE=yes utiliser PACKAGE
--without-PACKAGE ne pas utiliser PACKAGE (idem à --with-PACKAGE=no)
Noms de fichiers est dossiers
--bindir=DIR exécutables utilisateur dans DIR [PREFIX/bin *
--sbindir=DIR exécutables d'admin dans DIR [PREFIX/sbin *
--libexecdir=DIR bibliothèques exec dans DIR [PREFIX/libexec *
--datadir=DIR données dans DIR [PREFIX/share *
--sysconfdir=DIR fichiers de config dans DIR [PREFIX/etc *
--localstatedir=DIR données modifiables dans DIR [PREFIX/var *
--libdir=DIR bibliothèques dans DIR [PREFIX/lib *
--includedir=DIR fichier d'entête C dans DIR [PREFIX/include *
--infodir=DIR documentation dans DIR [PREFIX/info *
--mandir=DIR pages de man dans DIR [PREFIX/man *
--srcdir=DIR trouver les sources dans DIR [configure dir ou .. *
--program-prefix=PREFIX préfixer les noms des programmes avec PREFIX
--program-suffix=SUFFIX suffixer les noms des programmes avec SUFFIX
Host Type
--build=BUILD configurer pour construire sur BUILD [BUILD=HOST *
--host=HOST configurer pour construire sur l'hôte HOST [guessed *
--target=TARGET configurer pour construire pour TARGET [TARGET=HOST *
Voici un tableau qui synthétise les différents outils à utiliser dans le développement d'un package :
Quand | Commande | Actions |
Au début du projet | autoscan | Génère un fichier moule du fichier Configure.in |
Au début du projet | Ecrire un fichier Makefile.am (structure du programme) | |
Au début du projet | aclocal | Pour installer les outils complémentaires à automake |
Au début du projet | autoheader | Pour déterminer les variables du pré-processeur à définir, et les sauver dans le fichier config.h.in |
Au début, ou si configure.in a été modifié | autoconf | Génère configure à partir de Configure.in |
Au début, ou si l'on a perdu les makefile.in | automake | Génère les fichiers Makefile.in à partir des Makefile.am |
Pour changer de configuration | configure | Génère les fichiers Makefile et le fichier config.h en fonction des options de compilation choisies |
Pour compiler les sources | make | Compile les sources ou met à jour les binaires |
GNU Libtool est un logiciel du Projet GNU qui sert à créer des bibliothèques portables sur les systèmes UNIX (voire MacOS et Windows).
Dans le passé, si un programmeur voulait profiter des avantages des bibliothèques dynamiques, il devait écrire du code spécifique à chacune des plateformes sur lesquelles la bibliothèque était compilée. Il devait aussi écrire un système de configuration permettant à l'utilisateur qui installe le logiciel, de décider quel type de bibliothèque compiler.
Libtool simplifie la tâche du programmeur en encapsulant à la fois les dépendances par rapport à chaque plateforme, ainsi que l'interface-utilisateur, dans un seul script. Cet outil est conçu de façon que toute la fonctionnalité de chaque plateforme soit accessible via une interface générique, tout en cachant les choses obscurs au programmeur.
L'interface de Libtool vise à être cohérente. Les usagers ne sont pas supposés devoir lire de la documentation de bas niveau pour réussir à faire compiler des bibliothèques dynamiques. Ils devraient n'avoir qu'à exécuter le script configure (ou un équivalent), et Libtool devrait se charger des détails.
On utilise typiquement Libtool avec Autoconf et Automake, deux autres outils du système de compilation GNU.
Pour utiliser Libtool dans un fichier Configure.in, il faut ajouter les directives suivantes :
Pour utiliser Libtool dans un fichier Makefile.am, il faudra :
Enfin, vous pouvez exécuter la commande libtoolize dans le dossier du projet.
Voici une liste non exhaustive des problèmes que l'on peut rencontrer en utilisant ces outils.
Cela signifie qu'il y a des macros dans le fichier configure.in, qui ne sont pas définies par Autoconf. Soit vous utilisez une vieille version d'Autoconf; soit le nom de la macro a été incorrectement entré. Dans le premier cas, essayer d'en installer une nouvelle version. Dans le second, vérifiez le nom des macros que vous utilisez.
Cela peut signifier que vous avez essayer d'utiliser une substitution autoconf dans le Makefile.in sans avoir ajouter un appel approprié à AC_SUBST au scrip configure. Ou cela peut tout simplement signifier que vous devez reconstruire le Makefile. Pour le reconstruire depuis le Makefile.in, lancer le script shell config.status sans arguments. Si vous devez forcer configure à être relancé, lancez d'abord config.status --recheck.
Les dépendances des fichiers sont stockées dans un répertoire appelé .deps. Si un fichier disparaît brutalement, il se peut qu'il soit encore référencé lors du make, ce qui conduit à ce message. Le plus simple est d'effacer le répertoire .deps.
Lorsque une erreur a été commise dans un Makefile.am, les mécanismes automatiques qui permettent de régénérer les Makefile peuvent ne plus fonctionner. Il faut alors les recréer soi-même grâce à Automake et Autoconf par la séquence de commande : « autoheader ; aclocal ; autoconf ; automake ; ./configure ; make »
Il est possible de compiler des sources dans plusieurs répertoires. Mais ceci n'est vrai qu'à condition de ne pas avoir compilé dans le répertoire de sources. En effet, lorsque l'on configure le répertoire de sources pour y effectuer une compilation, on créé un certain nombre de fichiers qui peuvent interférer avec ceux des autres répertoires de compilation, et engendrer des problèmes difficiles à détecter. Dans sa grande bonté, Autoconf empêche ce genre de situation.
La syntaxe des macros utilisées par Autoconf impose que les parenthèses délimitant les paramètres d'une fonction soient accolées au nom de la fonction. Donc :
AC_DEFINE(CFLAGS) juste
AC_DEFINE (CFLAGS) faux
Imaginons que vous avez réalisé un programme qui puisse fonctionner/être utile à la fois sous Unix/Linux et Windows...cependant certaines fonctions ne sont pas disponibles, portent un autre nom ou sont présentes dans un autres « .h »...il vous viendra alors à l'esprit d'utiliser les Autotools et Autoheader pour inclure ou redéfinir des fonctions suivants la plateforme...
Seulement voilà, on a deux solutions pour compiler un programme pour Windows avec les Autotools et Libtool :
Utiliser Cygnus Cygwin et leur portage des Autotools
La seconde solutions est pratique si vous souhaitez compiler le programme directement sous Windows et que vous n'êtes pas géné d'être lié (éventuellement) à la dll d'émulation cygwin1.dll.
L'avantage de la première est que si vous développez sous Linux, vous n'aurait pas à redémarrer sous Windows pour compiler votre programme sous Windows. Il vous suffira de compiler sous Linux et de tester avec Wine...en attendant le redémarrage...Enfin, MinGW permet de ne pas avoir de liaison à une quelconque autre dll que celle de Windows (et celles que le programme utilise).
Enfin la dernière solution consisterait à porter votre code dans un fichier Visual C++/Visual Studio ce qui peut souvent s'avérer long du fait que VC++ n'utilise pas forcément les mêmes options par défaut.
On peut donc donner une définition de la cross compilation : c'est le fait de compiler du code sur un système pour produire du code machine pour un autre système.
===Instalation de la plateforme sous Linux======Logiciels nécessaires===
Pour réaliser un cross-compilateur, vous aurez besoin des logiciels suivants :
De la même manière que votre système a un identifiant pour désigner le type de plateforme, il nous en faut un pour désigner la plateforme cible. Prenons quelques exemples :
sur un PC basé sur un AMD Athlon XP, sous Linux :
$ gcc -dumpmachine
i686-pc-linux-gnu
sur un PC basé sur un Intel Pentium MMX, sous FreeBSD :
$ gcc -dumpmachine
i386-unknown-freebsd
Comme vous le voyez, ce nom se divise en 2 parties : la machine, puis l'OS. Nous allons donc construire un nom sur ce même modèle :
La première chose à faire est d'installer les librairies et les headers que binutils, GCC et toutes nos cross-compilations vont réclamer (l'équivalent de la glibc sous Linux).
C'est également maintenant que vous décidez où vous voulez installer tout l'environnement, par exemple : /usr/local/cross.
On commence par créer le répertoire d'installation en tant que root :
[root * # mkdir /usr/local/cross; cd /usr/local/cross
[root cross * # mkdir i686-pc-mingw32; cd i686-pc-mingw32
Nous avons créé un répertoire portant le nom de la plateforme cible. C'est à l'intérieur que les archives vont pouvoir être décompressées :
[root i686-pc-mingw32 * # tar zxf /chemin/vers/mingw-runtime-x.x.tar.gz
[root i686-pc-mingw32 * # tar zxf /chemin/vers/w32api-x.x.tar.gz
[root i686-pc-mingw32 * # chown -R root:root .
Le premier package, mingw-runtime, correspond entre autres à tous fichiers que le compilateur et le linker auront besoin pour produire des binaires pour Windows. Le second contient, comme son nom l'indique, toute l'API Win32, c'est-à-dire les headers et les librairies de base.
Maintenant, il va s'agir de compiler les binutils, puis GCC. Pour cela vous n'avez plus besoin d'être root.
Comme pour la plupart des compilations, tout se déroule en 3 étapes :
Une fois que vous avez décompressé l'archive des binutils quelque part, vous devez créer un autre répertoire à côté, dans lequel se déroulera la compilation, parce qu'il est recommandé de ne pas travailler directement dans le répertoire décompressé :
[user * $ tar jxf /chemin/vers/binutils-2.14.tar.bz2
[user * $ mkdir binutils-build
[user * $ cd binutils-build
Maintenant, nous allons lancer le script configure, avec 2 paramètres :
l'un pour donner le prefix c'est-à-dire le répertoire d'installation, dans notre cas, /usr/local/cross
le nom de la cible (sans quoi les binutils se compileraient pour la plateforme hôte)
[user binutils-build * $ ../binutils-x.x/configure \
--prefix=/usr/local/cross \
--target=i686-pc-mingw32
Ensuite, compilation :
[user binutils-build * $ make
Une fois terminée avec succès, il nous reste à installer tout ceci, en tant que root :
[user binutils-build * # make install
C'est maintenant terminé pour les binutils. Il vous reste à mettre à jour votre $PATH si le chemin d'installation que vous avez choisi n'y est pas :
[user * $ export PATH="/usr/local/cross/bin:$PATH"
Pour tester que tout fonctionne, vous pouvez faire ceci :
build$ i686-pc-mingw32-ld -V
GNU ld version x.x XXXXXXXX
Supported emulations:
i386pe
Les binutils ont donc été compilé pour produire un format binaire i386pe, le PE étant bien le format supporté par Microsoft Windows.
Pour GCC, nous allons refaire exactement la même chose qu'avec les binutils. Commençons avec la décompression et la création du répertoire de travail :
[user * $ tar jxf /chemin/vers/gcc-core-x.x.x.tar.bz2
[user * $ tar jxf /chemin/vers/gcc-g++-x.x.x.tar.bz2
[user * $ mkdir gcc-build
[user * $ cd gcc-build
Ici, nous avons décompressé les archives du compilateur C et C++.
Ensuite, la ligne du configure est identique à celui des binutils :
[user gcc-build * $ ../gcc-3.4.0/configure \
--prefix=/usr/local/cross \
--target=i686-pc-mingw32
Cette fois-ci, il faut bien vérifier que le configure trouve les binutils installés précédemment. Il doit afficher quelque chose comme :
checking for i686-pc-mingw32-ar... i686-pc-mingw32-ar
checking for i686-pc-mingw32-as... i686-pc-mingw32-as
checking for i686-pc-mingw32-dlltool... i686-pc-mingw32-dlltool
checking for i686-pc-mingw32-ld... i686-pc-mingw32-ld
checking for i686-pc-mingw32-nm... i686-pc-mingw32-nm
checking for i686-pc-mingw32-ranlib... i686-pc-mingw32-ranlib
checking for i686-pc-mingw32-windres... i686-pc-mingw32-windres
Si au lieu de ça, il affiche des lignes comme la suivante, la compilation ne fonctionnera pas et il faut revérifier votre $PATH :
checking for i686-pc-mingw32-ar... no
Quand tout est bon pour le configure, nous passons à la compilation, qui cette fois, sera plus longue :
[user gcc-build * $ make
Nous allons conclure cette partie GCC avec l'installtion en tant que root :
[root gcc-build * # make install
Comme vous êtes passé root, il se peut que le $PATH ne soit plus à jour, et l'installation échouera peut-être. Dans ce cas, répétez la mise à jour de l'environnement, comme nous l'avons fait à la fin de l'installation des binutils. Après cette étape, vous pouvez revenir à votre compte normal (non privilégié).
Comme contrôle basique, nous pouvons demander à GCC pour quelle plateforme il est prévu :
build$ i686-pc-mingw32-gcc -dumpmachine
i686-pc-mingw32
Maintenant, vous pouvez tester, on ne sait jamais.
Mode console
Voici le traditionnel « Hello World » :
#include <stdio.h>
int main()
{
printf("Hello World !\n");
return 0;
}
Comme je pense que vous connaissez déjà par cœur ce code source, passons directement à la compilation :
build/hello$ i686-pc-mingw32-gcc -o hello.exe hello.c
build/hello$ ls
hello.c hello.exe
Comme vous le voyez, GCC a produit hello.exe. Pour le lancer, vous pouvez utiliser Wine ou une machine sous Windows, bien évidemment. Et si « wine hello.exe », vous sort un magnifique « Hello World », vous avez réussi la compilation
Mode graphique
Essayons un exemple graphique maintenant. L'objectif est d'afficher une simple boite de dialogue, ce qui nous force à utiliser le header windows.h et le point d'entrée WinMain spécifique aux applications Win32 :
#include <windows.h>
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MessageBox(NULL,
"Cette fenêtre prouve que le cross-compilateur est fonctionnel !",
"Hello World", MB_OK);
return 0;
}
Ensuite viens la compilation, identique à la précédente :
build/hello$ i686-pc-mingw32-gcc -o hello_ui.exe hello_ui.c
build/hello$ ls
hello_ui.c hello_ui.exe
Une fois compilé, nous pouvons le tester comme précédemment avec « wine hello_ui.exe » qui devrait vous afficher une magnifique boîte de message « Hello World ».
===Intégration de la cross compilation à Autotools======Appels de configure===
Pour que la cross-compilation se passe bien, il est nécessaire de passer plusieurs paramètres :
De plus, il faut définir un certain nombre de variables comme défini dans les scripts de cross-compilation de SDL (SDL site).
On aura donc le script cross-configure.sh que l'on appelera à la place de configure :
#!/bin/sh
#cross compilation base directory
XCC_INSTALL=/usr/local/cross
#where to store aclocal.m4 and co
ACLOCAL_FLAGS="-I $XCC_INSTALL/share/aclocal"
#host (linux) gcc excutable path
HOST_CC=`which gcc`
#compil for Windows
TARGET=i686-pc-mingw32
#files installation directory
PREFIX=$XCC_INSTALL/$TARGET
#include cross compilation directories to PATH
#to find the good executables
PATH="$XCC_INSTALL/bin:$XCC_INSTALL/$TARGET/bin:$PREFIX/bin:$PATH"
export PATH
#where to store cache for configure
cache=cross-config.cache
#exec configure with necessary parameters
#and optionally given by user ($*)
sh configure --cache-file="$cache" \
--target=$TARGET --host=$TARGET --build=i386-linux \
--prefix=$PREFIX \
$*
status=$?
rm -f "$cache"
exit $status
Pour ce qui est de make, le principe est le même, dans un fichier cross-make.sh :
#!/bin/sh
#cross compilation base directory
XCC_INSTALL=/usr/local/cross
#where to store aclocal.m4 and co
ACLOCAL_FLAGS="-I $XCC_INSTALL/share/aclocal"
#host (linux) gcc excutable path
HOST_CC=`which gcc`
#compil for Windows
TARGET=i686-pc-mingw32
#files installation directory
PREFIX=$XCC_INSTALL/$TARGET
#include cross compilation directories to PATH
#to find the good executables
PATH="$XCC_INSTALL/bin:$XCC_INSTALL/$TARGET/bin:$PREFIX/bin:$PATH"
export PATH
#exec make with parameters optionally given by user ($*)
exec make $*
On appelera ./cross-make.sh à la place de make, et ./cross-make.sh install à la place de make install
Pour ce qui est de configure.in :
Dans tous les cas :
Dans le cas d'un exécutable :
Dans le cas d'une « dll » :
Pour ce qui est de Makefile.am :
Dans le cas d'un exécutable : rien à faire de particulier
Dans le cas d'une « dll »
Ajouter ceci à la fin du fichier, pour fournir un règle création de « dll » :
%.dll : lib%.la
$(DLLWRAP) --output-def $*.def --driver-name=$(CXX) -o $@ `ar t .libs/lib$*.dll.a` $(LDFLAGS) $(LIBS) \
$(DLLTOOL) --dllname $@ --def $*.def --output-lib .libs/lib$*.dll.a
On peut définir des macros LINUX, WINDOWS...en fonction du système sur/pour lequel on compile afin de faire de la compilation conditionnelle (include, define, fonctions différentes en fonction de l'OS).
dnl Check the operating system
dnl
dnl Define LIBPREFIX and LIBEXT so that dynamic libraries are
dnl named libFOO.so in Linux and FOO.dll in Win32.
dnl
case "x${target_os}" in
x)
SYS=unknown
;;
xlinux*)
SYS=linux
SYSGROUP=unix
LIBEXT=".so"
LIBPREFIX="lib"
AC_DEFINE(LINUX, * ,[Compile on Linux])
;;
x*cygwin*)
CYGWIN=Yes
SYS=cygwin
SYSGROUP=win32
LIBPREFIX="lib"
LIBEXT=".so"
AC_DEFINE(WINDOWS, * ,[Compile on Windows])
;;
x*mingw*)
SYS=ming