2.13. Le "make"
Vous recevez les sources du logiciel
"make". Vous copiez les fichiers dans un
répertoire initialement vide, et vous imprimez le contenu de ce
répertoire. On restera dans ce répertoire pendant tout l'exercice.
Constatant qu'il comporte un fichier de nom
Makefile, vous imprimez ce fichier. Ces
impressions sont données ci-dessous.
A- Construire le graphe de dépendances entre les
fichiers.
B- Vous ne disposez pas encore du programme
"make". Que faut-il faire pour le
créer?
C- A la suite de A, vous disposez maintenant de ce
programme. Vous en demandez l'exécution. Expliquez ce qui se
passe.
D- Vous modifiez le fichier
"basepage.h", et lancez la commande
"make install". Énoncez toutes les
commandes qui sont exécutées.
E- Vous créez le répertoire
"/usr/local/src/make", puis lancez la
commande "make backup". Que se
passe-t-il?
F- Vous lancez la commande "make
erase". Que se passe-t-il? Disposez-vous encore du programme
"make"?
Contenu du répertoire:
-rw------- 1 u 2071 Jun 15 10:01 basepage.h
-rw------- 1 u 6841 Jun 15 10:01 check.c
-rw------- 1 u 3448 Jun 15 10:01 getenv.c
-rw------- 1 u 5416 Jun 15 10:01 getopt.c
-rw------- 1 u 8068 Jun 15 10:01 h.h
-rw------- 1 u 15843 Jun 15 10:01 input.c
-rw------- 1 u 10084 Jun 15 10:01 macro.c
-rw------- 1 u 25598 Jun 15 10:01 main.c
-rw------- 1 u 14933 Jun 15 10:01 make.c
-rw------- 1 u 4238 Jun 15 10:01 make.man
-rw------- 1 u 1176 Jun 15 10:01 Makefile
-rw------- 1 u 38345 Jun 15 10:01 makshell.c
-rw------- 1 u 5792 Jun 15 10:01 reader.c
-rw------- 1 u 3467 Jun 15 10:01 readme
-rw------- 1 u 8507 Jun 15 10:01 rules.c
-rw------- 1 u 1319 Jun 15 10:01 stat.h
-rw------- 1 u 6398 Jun 15 10:01 syserr.c
Contenu
du fichier
Makefile[2]:
# Makefile for make utility.
#
CFLAGS =
LDFLAGS =
DESTDIR = /usr/bin
WORKDIR = /usr/local/sys/travail
BACKDIR = /usr/local/src/make
PROG = make
OBJS = check.o input.o macro.o main.o make.o makshell.o\
reader.o rules.o
LIBES = getenv.o getopt.o syserr.o
FILES = check.c input.c macro.c main.c make.c makshell.c\
reader.c rules.c getenv.c getopt.c syserr.c\
basepage.h h.h stat.h make.man makefile readme
$(PROG): $(OBJS) $(LIBES)
ld $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBES)
$(OBJS): h.h
getenv.o main.o: basepage.h
main.o makshell.o: stat.h
install: $(PROG)
cp $(PROG) $(DESTDIR)/$(PROG)
rm $(PROG)
clean:
rm $(OBJS) $(LIBES) $(PROG)
erase: clean
rm $(FILES)
backup:
cp $(FILES) $(BACKDIR)
restore:
cd $(BACKDIR)
cp $(FILES) $(WORKDIR)
cd $(WORKDIR)
help:
@echo Makefile options:
@echo help: Print this message
@echo default: Create $(PROG)
@echo install: Move $(PROG) to $(DESTDIR)
@echo clean: Remove $(PROG) and all .o files
@echo erase: Erase ALL files
@echo backup: Copy source files to $(BACKDIR)
@echo restore: Copy files to $(WORKDIR)
Solution
de l'exercice 2.13.
2.13.1. Question A
Le graphe de dépendances se déduit du fichier
Makefile de l'énoncé. Tous
les fichiers potentiels doivent être mentionnés dans ce graphe,
même s'ils ne sont jamais créés. Il en est ainsi, par
exemple, du fichier install qui ne sera en
fait jamais créé par le programme
make. Le graphe est donc le
suivant:
La règle de dépendance
install: $(PROG) définit le premier
arc du graphe. La règle $(PROG): $(OBJS)
$(LIBES) définit la dépendance de
make sur tous les fichiers
“*.o”. La règle
implicite sur les suffixes conjointement avec les fichiers du répertoire
définit les arc entre les
“*.o” sur les
“*.c” correspondants. Enfin les
arc vers les “*.h” se
déduisent des règles:
$(OBJS): h.h
getenv.o main.o: basepage.h
main.o makshell.o: stat.h
La
règle erase: clean définit le
dernier arc.
2.13.2. Question B
Comme on ne dispose pas encore du programme
make, il faut en simuler le comportement
à la main. Les fichiers
“*.o” n'existant pas, ils
doivent être créés par une commande identique à la
commande associée à la règle implicite des suffixes, et
appelant le compilateur. Au lieu de lancer ces commandes une à une, on
peut utiliser les structures de contrôle du langage de commandes, et donc
exécuter:
for i in *.c; do cc -c $i;
done
Lorsque ceci est terminé, on dispose de tous les
fichiers “*.o”
nécessaires, et il reste à exécuter la commande pour
créer le programme make. Ici encore,
on peut utiliser le mécanisme de substitution du langage de commandes,
puisque les fichiers “*.o” du
répertoire sont précisément ceux correspondant aux macros
$(OBJS) et
$(LIBES):
ld -o make
*.o
2.13.3. Question C
Lorsqu'on demande l'exécution du programme
make sans paramètre, il construit le
graphe de dépendances ci-dessus, et fait les vérifications de date
en partant du nœud du graphe défini par la première
règle du fichier Makefile, donc
à partir de make. Comme ce fichier
vient d'être construit, il est plus récent que tous ceux dont il
dépend, qui sont eux-mêmes plus récents que ceux dont ils
dépendent. Les dates des fichiers étant compatibles avec le graphe
de dépendances, il n'y a rien à faire.
2.13.4. Question D
Lorsqu'on lance la commande
make install, le programme
make va de nouveau construire le graphe
ci-dessus, mais en faisant le contrôle à partir de
install. Il constatera d'abord que le
fichier install n'existe pas, et doit donc
être construit. Par ailleurs, il constatera également que les
fichiers main.o et
getenv.o sont antérieurs au fichier
basepage.h, puisque ce dernier vient
d'être modifié. Il lancera donc les deux commandes:
cc -c main.c et cc -c
getenv.c
Les deux fichiers main.o
et getenv.o étant maintenant
postérieurs au fichier make,
puisqu'ils viennent d'être recréés, le programme lancera la
commande de construction du fichier make,
c'est-à-dire, après remplacement des macros:
ld -o make check.o input.o macro.o main.o make.o makshell.o\
reader.o rules.o getenv.o getopt.o syserr.o
Enfin,
il lancera les commandes de création du fichier
install, ainsi qu'elles sont
définies dans le fichier Makefile,
après remplacement des macros:
cp make /usr/bin/make
rm make
Constatons
que la première recopie le fichier
make dans le répertoire
/usr/bin, et le supprime du
répertoire courant. Notons que le fichier
install n'est toujours pas
créé, mais que le programme considère néanmoins,
pour cette exécution, qu'il existe maintenant et qu'il est à
jour.
2.13.5. Question E
Lors de la commande
make backup, le programme
make sera cette fois trouvé par les
règles de recherche dans le répertoire
/usr/bin, pourvu que ces règles de
recherche précisent bien ce répertoire. Du fait de la question
précédente, il s'agit bien de celui que l'on vient de construire.
Il constatera que le fichier backup
n'existe pas et doit donc être créé par la commande
associée définie dans le fichier
Makefile, et qui est, après
remplacement des macros:
cp check.c input.c macro.c main.c make.c makshell.c\
reader.c rules.c getenv.c getopt.c syserr.c basepage.h\
h.h stat.h make.man makefile readme /usr/local/src/make
Cette
commande recopie la totalité des fichiers fournis initialement,
éventuellement modifiés (pour
basepage.h) dans le répertoire
/usr/local/src/make qui vient d'être
créé. Notons que les fichiers
“*.o” ne sont pas
recopiés dans ce répertoire.
2.13.6. Question F
Lors de l'exécution de la commande
make erase, le programme constate
qu'il doit créer d'abord le programme
clean, puisqu'il n'existe pas. Il
exécute donc la commande:
rm check.o input.o macro.o main.o make.o makshell.o\
reader.o rules.o getenv.o getopt.o syserr.o make
Le
fichier clean n'est toujours pas
créé, mais il considère qu'il existe et est à jour.
Il passe donc à la construction du fichier
erase, en lancant la commande:
rm check.c input.c macro.c main.c make.c makshell.c\
reader.c rules.c getenv.c getopt.c syserr.c basepage.h\
h.h stat.h make.man makefile readme
Le
fichier erase n'existe toujours pas, mais
il considère le travail terminé.
Constatons que la première commande supprime tous les
fichiers que l'on a créé au cours de l'exercice. Le fichier
make n'existait déjà plus
dans le répertoire courant depuis la question D. La deuxième
commande supprime tous les fichiers qui étaient initialement dans le
répertoire, qui est donc vide maintenant. Nous disposons toujours du
programme make, puisqu'il est dans
/usr/bin depuis la question D!
[2] Le caractère
@ évite l'impression de la commande
lors de son exécution.