Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
💥 Les 22 & 23 novembre : Accès 100% GRATUIT
à la Bibliothèque Numérique ENI. Je m'inscris !

Outils de compilation

Introduction

Ce chapitre présente plusieurs recettes concernant la compilation d’un programme. Nous verrons d’abord comment installer le compilateur si nécessaire, puis vérifier son bon fonctionnement. Nous traiterons ensuite de l’automatisation de la compilation (via make) et de la bonne répartition du code sur plusieurs fichiers avant d’aborder les méthodes d’optimisation du code, manuellement ou via des options du compilateur. Par ailleurs, dans ce chapitre, nous aborderons comment passer des paramètres au préprocesseur pour définir certaines options du programme au moment de sa compilation.

Compiler un programme

Problème

Vous souhaitez vérifier que vous pouvez programmer et compiler un programme dans l’environnement sur lequel vous travaillez.

Solution

Vérifiez la présence d’un compilateur, et dans la positive, compilez et exécutez le programme Bonjour (plus connu sous le nom d’Hello World).

Discussion

Sur les systèmes de type Unix, le compilateur s’appelle cc. Sur GNU/Linux, ce compilateur n’est autre que gcc, que l’on trouve aussi sur la plupart des systèmes d’exploitation. Sur certains d’entre eux, cc peut ne pas avoir été installé, mais il est possible de trouver gcc malgré tout. Comme ce dernier est porté sur de nombreuses plates-formes, son utilisation facilite la portabilité des programmes vers d’autres systèmes. En l’occurrence, ce livre est basé sur gcc car ce compilateur est à la fois libre et utilisable sur probablement le plus de plates-formes différentes.

Moins connu, clang peut également être utilisé. Il est basé sur l’infrastructure LLVM et est inclus dans la plupart des distributions GNU/Linux. Mais surtout, clang remplace gcc comme compilateur par défaut sur FreeBSD depuis le 5 novembre 2012. Ces deux compilateurs ont de nombreuses options communes pour la ligne de commande, aussi vous pouvez utiliser alternativement l’un...

Automatiser la compilation avec make

Problème

Vous souhaitez automatiser la compilation de votre programme car les commandes de compilation deviennent trop rébarbatives à taper.

Solution

Utilisez make et éditez un fichier Makefile avec vos règles de compilation.

Discussion

L’automatisation de la compilation d’un programme peut être réalisée avec un script. Mais ce serait oublier les avantages que fournit l’utilitaire make en termes de gestion de dépendances et d’écriture de règles. En effet, une règle est constituée d’une cible, de dépendances et de commandes à lancer. Lorsque vous exécutez make sans argument, il se lance sur la première cible du fichier Makefile. Sinon, la cible doit être précisée en argument sur la ligne de commande.

Lorsque make rencontre une cible, il tente d’abord de résoudre les dépendances qui sont elles-mêmes des cibles. Quand une cible correspond à un nom de fichier, que le fichier existe et que sa date de modification est antérieure à la date des dépendances lorsqu’elles sont elles-mêmes des fichiers, make ne tient pas compte de ce fichier. Sinon, make ne fait rien pour cette cible, la considérant à jour.

Enfin, lorsque make est lancé sur une cible, que toutes ses dépendances sont à jour...

Bien répartir le code sur plusieurs fichiers

Problème

Vous avez un programme dans un seul fichier et souhaitez le répartir sur plusieurs pour faciliter sa lecture. Ou le code est déjà réparti sur plusieurs fichiers mais vous ne vous y retrouvez plus et voulez revoir cette répartition.

Solution

Répartissez le code de manière à ce que le fichier contenant main() soit le plus court possible. Les autres fichiers source ne doivent être ni trop longs ni trop courts, le code contenu ne doit être que sur un seul thème et, si possible, ils ne doivent pas contenir de dépendances croisées avec les autres fichiers source.

Discussion

Le fichier contenant main() doit être le plus court possible et ne contenir que ce qui est nécessaire au lancement du programme. Nous avons parfois tendance à travailler en priorité dans ce fichier puisqu’il contient la fonction principale, et donc à accumuler le code par paresse de créer un nouveau fichier source. Dès que ce fichier devient trop long, vous pouvez déplacer toutes les fonctions autres que main() dans des fichiers annexes.

Chaque fichier annexe doit contenir du code sur un thème. De plus, le nom du fichier doit exprimer ce thème. Cela facilite ainsi la recherche d’une fonction ou d’une variable dans le code source. Vous pouvez aller jusqu’à préfixer...

Obtenir des binaires optimisés

Problème

Vous voulez optimiser votre programme afin qu’il fonctionne plus vite et prenne moins de place en mémoire.

Solution

Optimisez le code vous-même à la main et indiquez certaines options au compilateur pour le forcer à effectuer quelques optimisations automatiques.

Discussion

Il n’existe pas vraiment de méthode pour optimiser le code à la main. Cependant, restez attentif à quelques points. Le premier, un cas d’école, consiste à écrire :


if (condition) 
  { 
    while (encore) 
      { 
        /* code */ 
      } 
  }
 

au lieu de :


while (encore) 
  { 
    if (condition) 
      { 
        /* code */ 
      } 
  }
 

Le test n’est ainsi fait qu’une seule fois. Mais ceci n’est valable que lorsque la condition est indépendante des variables de la boucle. Plus généralement, restez attentif aux boucles afin d’effectuer le maximum en dehors. Surtout, les boucles à l’intérieur des boucles doivent être séparées au lieu d’être imbriquées lorsqu’elles sont indépendantes :


for (i = 0; i < n, i++) 
  { 
    /* code */ 
  } 
 
for (j = 0; j < m, j++) 
  { 
    /* code */ 
  }
 

au lieu de :


for (i = 0; i < n, i++) 
  { ...

Passer des paramètres au préprocesseur

Problème

Vous voulez passer des paramètres au préprocesseur afin que la compilation s’effectue en fonction de ces paramètres utilisés dans le code.

Solution

Utilisez l’option -D du compilateur et considérez l’option ainsi passée comme une macro.

Discussion

Lorsque le préprocesseur rencontre l’option -D, par exemple -DDEBUG, la macro DEBUG est définie dans le code et nous pouvons l’exploiter ainsi :


#ifdef DEBUG 
printf ("Mode DEBUG activé\n"); 
#endif
 

Cela permet par exemple d’avoir une compilation en fonction du système d’exploitation :


#ifdef OS_LINUX 
print ("Système : Linux\n"); 
#endif
 

Avec uname -s vous pouvez obtenir le nom du système d’exploitation, et si c’est GNU/Linux, compiler avec -DOS_LINUX.

Vous pouvez même spécifier un contenu, avec par exemple -DPREFIX=\"/usr\" qui affecte à la macro PREFIX une chaîne de caractères. Il faut faire attention aux guillemets qui sont nécessaires ici. Vous pouvez ensuite exploiter cela ainsi :


#ifdef PREFIX 
printf ("Le préfixe est %s\n", PREFIX); 
#endif
 

Voir aussi le manuel de gcc : http://gcc.gnu.org/onlinedocs/ et plus particulièrement le chapitre Options Controlling the Preprocessor.