next up previous contents index
Next: Les librairies Up: La compilation Previous: La compilation

Les Makefiles

PFE... Si l'on utilise plusieurs fichiers C, ce qui est de probabilité 0 en TD et de probabilité 1 pour le projet info, il va falloir comprendre plus en détail comment on fabrique un fichier exécutable à partir de plusieurs fichiers sources. D'abord chaque fichier source est compilé séparément par l'outil approprié pour former un fichier ``objet'' qui contient le code machine généré, habituellement ou le nomme avec un .o à la fin sous unix et un .obj sous dos. Ensuite on rassemble tous ces fichiers en utilisant un ``linker'' qui va comme son nom l'indique résoudre les liens entre ces fichiers objets. Concrètement, si mon programme est contenu dans les deux fichiers C un.c et deux.c et que je veux produire un executable du nom de compte, je taperai successivement :

gcc -c un.c -o un.o
gcc -c deux.c -o deux.o
gcc un.o deux.o -o compte
(ou pour les paresseux : gcc un.c deux.c -o compte). Cela permet de ne recompiler que les fichiers qui ont été modifiés depuis la dernière compilation, par exemple, si je corrige un bug dans dans deux.c, je n'ai besoin de taper que :
gcc -c deux.c -o deux.o
gcc un.o deux.o -o compte
(voire gcc un.o deux.c -o compte) pour reconstruire l'exécutable. Là on va me dire que c'est vraiment pas pratique du tout s'il faut retaper 5 lignes à chaque fois qu'on modifie un caractère et qu'en plus c'est bien vicieux s'il faut se souvenir de tout, par exemple dans le cas suivant (nous recommandons au lecteur de bien s'accrocher au poly) : un.c et trois.c incluent graph.h mais deux.c ne l'inclut pas, donc si je modifie graph.h pour corriger un problème qui a causé une erreur de compilation dans un.c, je dois aussi recompiler trois.c, mais pas deux.c. C'est là qu'intervient la commande make. Make est un programme qui sait deviner comment reconstruire ce qu'il faut et juste ce qu'il faut quand des fichiers ont été modifiés, et en prime il devine tout seul lesquels ont été modifiés. Pour cela, il faut l'aider un minimum : make a besoin d'un fichier, le Makefile (oui comme çà avec la majuscule au début) qui indique les dépendances entre les fichiers, c'est-à-dire l'ordre logique dans lequel ils doivent êtres mis à jour et les commandes à exécuter quand la mise à jour d'un fichier s'impose d'après les règles que tu lui a données. Make permet de préciser tout ca avec de nombreuses façons, mais restons simples et donnons tout de suite le Makefile correspondant à compte :
compte: un.o deux.o trois.o
    gcc un.o deux.o trois.o -o compte
    
un.o: un.c graph.h
    gcc un.c -o un.o
    
deux.o: deux.c
    gcc deux.c -o deux.o
    
trois.o: trois.c graph.h
    gcc trois.c -o trois.o
compte: un.o deux.o trois.o
    gcc un.o deux.o trois.o -o compte
signifie : pour mettre la cible compte à jour, ce qui est nécessaire quand un des fichiers parmis un.o, deux.o ou trois.o a été modifié, c'est-à-dire que la date de sa dernière modification est supérieure à celle de compte lui-même, alors il faut exécuter la commande située en dessous : gcc un.o ... -o compte. Et comme Make est parfois têtu, c'est la commande située en dessous avec un TAB avant et pas autre chose.

Une fois que l'effort de conception du Makefile est consenti, et il doit être modifié à chaque fois qu'on ajoute un fichier, il suffira après n'importe quelle modification, et sans refléchir, de taper make pour voir une demi-douzaine de lignes de commandes s'exécuter sous tes yeux ébahis, toutes choisies judicieusement. Et comme évidemment taper make est beaucoup trop compliqué pour l'informaticien moyen, il te suffira de cliquer du bout de la souris sur le bouton ``compile'' de xemacs pour que tout çà se déroule sans quitter l'éditeur. Toutes les erreurs de compilations apparaitront dans une fenêtre (et autant syntax error est un must du basic, autant gcc adore dire parse error before quelque chose, ce qui est encore moins explicite, mais signifie qu'il a decroché complètement et qu'il ne comprend plus rien à l'emboitement de tes { }). En pointant cette erreur avec le bouton du milieu de la souris qui est toujours dans ta main, tu demanderas alors à xemacs d'ouvir une fenêtre sur le fichier incriminé et de placer le curseur sur la ligne concernée.

Oui mais c'est vraiment trop compliqué de faire un Makefile, va-t-on encore dire, en fait tant qu'on ne demande pas des trucs extravagants, make va être gentil avec ceux qui n'ont encore rien compris à son fonctionnement, en effet, le Makefile pour compte peut s'écrire aussi (et on ne peut que conseiller de l'ecrire comme çà, vu que tout le monde comprendra ce qu'il fait au premier coup d'oeil) :

.c.o:
     gcc -c $<

OBJS  un.o deux.o trois.o

compte: $(OBJS)
     gcc $(OBJS) -o $@

un.o: un.c graph.h
deux.o: deux.c
trois.o: trois.c graph.h
Il présente en outre l'avantage d'impressionner par la cryptique des $< et $, dont il serait bien long d'expliquer ce qu'ils font là. Cet exemple devrait servir de base à n'importe quel projet : il suffit d'indiquer dans la ligne OBJS la liste des fichiers objets (un par fichier C) et d'adapter la dernière section aux dépendances induites par les fichiers inclus (on ne met évidemment pas les fichiers inclus standards, comme stdio.h, dedans car il y a peu de chances qu'ils soient modifiés). A vrai dire, et cela concerne en particulier les TD d'info, si tu n'as pas de fichiers inclus persos, les dernières lignes sont superflux, ainsi dans notre exemple,
deux.o: deux.c
ne sert à rien, mais je rappelle encore que pour un TD consitué du seul fichier TD.c, pas besoin de Makefile, gcc TD.c -o TD suffira à produire le programme TD.

Concluons par un éclaircissement pour ceux qui ne l'auraient pas vu : l'option -o de gcc lui indique le nom du fichier à produire et l'option -c lui demande de compiler uniquement (produire un .o).

EXEMPLE CAML (B. de Singly)   voici le makefile utilisé pour mon projet informatique (parler des interactions emacs-makefile des meta...)

singlyd@poly ~/caml/red/sources > cat makefile
#makefile \`a lancer sur poly
#lissage \`a ex\'ecuter sur station (test\'e sur DEC)

CAMLC=camlc
COMPFLAGS=-W
LINK=menu.zo type.zo fichier.zo affichage.zo smooth.zo main.zo
LINKFLAGS=-custom -lgraph -lunix -lX11 -lnums unix.zo graphics.zo nums.zo

all: lissage
lissage: $(LINK)
        $(CAMLC) $(LINKFLAGS) -o lissage $(LINK)

clean:
        rm -f *.\$$\$$\$$ *~ *.zi *.zo *.zix *.log *.dvi *.aux core

.SUFFIXES: .ml .mli .zo .zi

.mli.zi:
        $(CAMLC) $(COMPFLAGS) -c $<

.ml.zo:
        $(CAMLC) $(COMPFLAGS) -c $<

#d\'ependances
main.zo:        menu.zi type.zi affichage.zi smooth.zi main.zi
smooth.zo:      type.zi fichier.zi affichage.zi smooth.zi
affichage.zo:   type.zi fichier.zi affichage.zi
fichier.zo:     type.zi fichier.zi
type.zo:        type.zi
menu.zo:        menu.zi

   


next up previous contents index
Next: Les librairies Up: La compilation Previous: La compilation




Wed Jun 25 13:24:35 MET DST 1997