Compilation séparée (4) – Le Makefile

compilation_separee-41Bon nous voici enfin arrivés à la partie la plus attendue sans doute, celle du makefile. Comme d’habitude, je rappelle que cet article vient à la suite de plusieurs autres articles traitant de la compilation séparée, à savoir:

… et qu’il peut être utile de les lire si ce n’est déjà fait pour comprendre le présent article, car ils contiennent les notions pré requises sur lesquelles je ne reviendrai donc pas. Toujours comme d’habitude, je ne vais pas chercher à vous embrouiller avec des notions compliqués, ni à faire des makefiles de tueurs, ça pourra toujours faire l’objet d’un prochain article si le besoin s’en fait sentir. Non, aujourd’hui, nous nous contenterons du cas le plus simple et le plus compréhensible.

9) Le Makefile:

Nous nous intéressons toujours au même projet que dans les articles cités ci dessus. La structure en est rappelée pour mémoire dans l’illustration d’en tête. Nous avons vu au cours de l’article précédent, que pour compiler ce projet, il était nécessaire d’exécuter 4 commandes. C’est pas encore trop fastidieux comme s’il y avait une dizaine de fichiers objet à générer, mais ça commence tout de même à être un petit peu lourd, d’autant que chaque fois que vous allez faire une modification, il va falloir exécuter deux commandes au moins, une pour re-générer le fichier objet, et une pour re-générer le fichier exécutable. D’où l’idée de se simplifier la tâche en simplifiant tout ça.

Je ne sais pas si vous connaissez les scripts shell? Ce sont en gros des fichiers textes dans lesquels on place les suites de commandes répétitives que l’on taperai sinon les unes après les autres dans un terminal. Eh bien le principe du makefile est un peu similaire. Mais trêve de blabla, regardons le makefile que je viens de concocter pour le projet, et les commentaires viendront après 😉

makefile-2

Comme vous pouvez le voir, il y a un certain nombre de commentaires, reconnaissables au caractère ‘#’ qui débute les lignes, puis quatre groupes de deux lignes. Les sauts de lignes, comme les commentaires comptent pour rien, mais sont vivement conseillés dans un souci de clarification. Au niveau contraintes, la principale est que le fichier doit impérativement se nommer makefile. La tabulation que vous pouvez observer au début de chaque deuxième ligne est elle aussi impérative. Ne vous souciez pas des couleurs si chez vous elles sont différentes, en effet elles sont ajoutées par l’éditeur à partir du moment où il reconnait que vous faites un makefile (c’est à dire en général la première fois que vous enregistrez le fichier) dans un souci d’améliorer la lisibilité. Mais les conventions de couleurs peuvent varier selon la manière dont votre éditeur est configuré, et en tout état de cause, ce n’est pas à vous de les ajouter ou de vous en préoccupper.

Interprétation du makefile par le système:

Lors du lancement de la compilation par makefile via la commande ‘make‘, le système parcourt le makefile jusqu’à la première cible (une cible est le mot en vert suivit d’un ‘:’ qui débute chaque groupe de deux lignes). Donc dans le cas présent, il va aller examiner la cible ‘Programme:’, puisque c’est elle la plus haute dans le fichier. Il lit ensuite les noms de fichiers sur la même ligne que la cible, qui indiquent les dépendances à satisfaire pour procéder à la compilation. Ensuite il regarde parmis ces noms de fichier si certains correspondent à d’autres cibles. Si tel est le cas, il va sur toutes ces cibles l’une après l’autre, avant de poursuivre. Donc dans le cas présent, il va d’abord aller sur la cible ‘humain.o‘.

Au niveau de la cible ‘humain.o‘, il va lire la suite de la ligne, et constater que les dépendances du fichier ‘humain.o’ sont ‘humain.c‘ et ‘humain.h‘. Il va alors procéder à des vérifications sur les dates de dernière modification des fichiers. Si l’un des deux fichiers de dépendance s’avère être plus récent que le fichier ‘humain.o’, il va en déduire que le fichier ‘humain.o’ a besoin d’être recompilé, et exécutera donc la commande de compilation située à la ligne du dessous. Dans le cas contraire, il en concluera que cette cible est à jour et retournera vers la cible dont il vient. Donc dans le cas présent il va retourner vers la cible ‘Programme‘, continuer de vérifier les fichiers listés qui sont aussi des cibles, et vérifier de même voiture.o et conduire.o

Ceci fait, deux cas peuvent se présenter: s’il existe au moins une dépendance qui est plus récente que l’exécutable ou si l’exécutable n’existe pas ou plus, il sera régénéré grâce à la commande de compilation de la ligne du dessous. Sinon, il ne se passera rien car l’exécutable sera considéré comme étant à jour.

Généralisation:

Le cas traité ici est simple, mais vous pouvez tomber sur des make plus compliqués. Mais l’algorithme utilisé est toujours le même:

  1. Le système lit le makefile de haut en bas à la recherche de la première cible.
  2. Il regarde les dépendances de la cible, qui sont explicitées sur la même ligne.
  3. Si certaines de ces dépendances s’avèrent être elles même des cibles, il va les traiter l’une après l’autre avant de poursuivre le traitement de la cible en cours.
  4. Notez qu’il est tout à fait possible que parmi ces dépendances, il y en ait qui aient elles même des dépendances qui sont aussi des cibles, auquel cas, le système fera de même jusqu’à ce qu’il tombe sur une cible dont aucune dépendance n’est une cible.
  5. Il vérifie que si fichier.o concerné est à jour en comparant les dates de dernière modification des fichiers.
  6. Si ce n’est pas le cas, il procède à une recompilation suivant les instructions données à la ligne du dessous (après une tabulation à ne pas oublier surtout).
  7. Ensuite il remonte sur la cible d’où il vient, et continue de parcourir les cibles jusqu’à ce que tous les fichiers objets nécessaires pour la compilation finale de l’exécutables aient été mis à jour dans le cas où c’était nécessaire.
  8. Enfin, il procède si nécessaire à la (re)compilation du programme exécutable.

Conclusion:

En procédant de cette manière, il est certain de perdre le moins de temps système possible. Et pour l’utilisateur c’est beaucoup plus simple, vu qu’à chaque fois qu’il change quelque chose dans l’un de ses fichiers C, il a juste à taper ‘make’ dans le terminal (en se plaçant quand même dans le répertoire où sont placés les fichiers C et le makefile), et tout ce qu’il y a à recompiler est recompilé automatiquement.

10) Conclusion générale:

Vous disposez donc dorénavant de toutes les connaissances nécessaires pour profiter des joies de la compilation séparée. Vous verrez qu’au début, c’est toujours un petit peu pénible, parce qu’il faut faire l’effort d’apprendre, mais une fois qu’on a maitrisé l’outil, on utiliserai un makefile pour se faire des oeufs sur le plat si c’était possible ;).

Je vais maintenant m’arrêter quelques jours de parler du C (ça fait quand même quatre longs articles quatre jours de suite tout de même!), même si je pense que d’autres articles pourraient s’avérer utiles éventuellement, peut être un complément sur le makefile pour des usages plus avancés, ou encore un article sur l’utilisation de gdb pour débugger un programme, on verra ça :).

Une réflexion au sujet de « Compilation séparée (4) – Le Makefile »

  1. Ping : Compilation séparée (3) - Fichiers objets et compilation manuelle. « Aldian’s Blog

Laisser un commentaire