Compilation d'un programme à plusieurs fichiers avec make
- Informations supplémentaires à fournir à
make
(par rapport au cas d'un seul fichier) : la liste des fichiers à compiler ; les contraintes d'ordre de compilation. - Ces informations sont codées sous forme de "dépendances".
Notion de dépendance dans make
Une dépendance dans make
:
cible: prérequis1 prérequis2 ...
Ce qui signifie :
- Les prérequis doivent exister pour pouvoir créer ou mettre à jour la cible.
- La cible, si elle existe, n'est pas à jour si un prérequis a été modifié plus récemment que la cible.
- Pour créer ou mettre la cible à jour, il faut d'abord créer ou mettre à jour les prérequis, et, récursivement, les prérequis des prérequis.
Dépendance pour l'édition de liens
Écrire que l'exécutable dépend de tous les objets. Exemple :
my_program: foo.o bar.o my_program.o
- Y compris l'objet correspondant au programme principal.
- Tous les objets, même si le programme principal n'utilise pas directement tous les modules.
-
Utiliser comme cible le nom du fichier contenant le programme principal, sans suffixe.
-
Comme l'un des prérequis est
my_program.o
, c'est-à-dire le nom de la cible avec un suffixe.o
,make
comprend qu'il doit faire une édition de liens pour créer la cible à partir de tous les prérequis. -
Nota bene : nous avons ainsi implicitement donné la liste des fichiers à compiler, ce sont les fichiers obtenus en remplaçant
.o
par.f
.
La commande d'édition de liens
make
connaît la commande d'édition de liens, mais il utilise par
défaut le compilateur C. Il faut lui dire d'utiliser le compilateur
Fortran pour l'édition de liens :
LINK.o = $(FC) $(LDFLAGS) $(TARGET_ARCH)
Notion de règle dans make
Une règle est l'association d'une dépendance et d'une recette (une commande) :
cible : prérequis
recette
La recette est censée permettre de créer (ou mettre à jour) la cible. Attention : la recette doit être précédée d'une tabulation et non d'espaces.
Règles implicites
make
connaît déjà certaines règles utilisant les suffixes des fichiers :
règles implicites, ou encore pattern rules.
Les régles implicites utilisent les variables automatiques :
$^
: tous les pré-requis$<
: premier pré-requis$@
: cible
Manipulation 5 : la base de données de make
man make
Afficher la base de données de make
:
make -p -f /dev/null >make_db.txt
Regarder en particulier :
- la définition de
LINK.o
- la commande d'édition de liens, dans la règle
%: %.o
- la commande de compilation, dans la règle
%.o: %.f
Dépendances entre les objets
Indiquer les dépendances induites par l'utilisation de modules Fortran en écrivant des dépendances entre les objets. Exemple :
bar.o: foo.o plouf.o
indique bien que bar.f
doit être compilé (ou re-compilé) après
foo.f
et plouf.f
.
Lignes include
en Fortran
Si un source Fortran contient une ligne include
alors il faut tenir
compte de cette dépendance dans le makefile : on ajoute le fichier
inclus dans les prérequis de l'objet. Exemple :
foo.o: bar.h
Forme compilée d'une bibliothèque statique
La bibliothèque "statique" compilée se présente sous la forme de :
- Un fichier "archive", dont le nom commence par "lib" et dont le
suffixe est
.a
. Exemple :libjumble.a
. Plus pratique que l'ensemble de fichiers objets (.o
), contient cet ensemble. - Un ou plusieurs fichiers
.mod
. Exemple :jumble.mod
.
Compilation d'un programme utilisant une bibliothèque
Le compilateur a besoin des fichiers .mod
de la bibliothèque au
moment de la compilation des objets. Trouver le répertoire contenant
les .mod
de la bibliothèque. Puis, dans le GNUmakefile
, compléter
FFLAGS
avec l'option supplémentaire -I
suivie du répertoire
trouvé. Par exemple, si les .mod
de la bibliothèque sont dans le
répertoire ~/.local/include
, ajouter à FFLAGS
l'option
-I${HOME}/.local/include
.
Le compilateur a besoin de l'archive lib
….a
au moment de
l'édition de liens. Trouver le répertoire contenant cette
archive. Puis définir la variable LDLIBS
. make
utilise la variable
LDLIBS
à l'édition de liens. Cf. la règle : %: %.o
dans la base de
données de make. Par exemple, si libjumble.a
est dans le répertoire
~/.local/lib
ajouter dans le GNUmakefile
la ligne :
LDLIBS = -L${HOME}/.local/lib -ljumble
Utilisation de plusieurs bibliothèques
- Les archives et les
.mod
des bibliothèques peuvent se trouver dans plusieurs répertoires. On ajoute autant d'options-L
et-I
que nécessaire. - L'ordre des options
-L
et-I
n'a pas d'importance. -
Une option
-l
pour chaque bibliothèque. -
Si une bibliothèque fait appel à une autre bibliothèque alors l'ordre des options
-l
est important : les bibliothèques doivent apparaître dans l'ordre "appelant appelé". Exemple : si la bibliothèqueplouf1
appelleplouf2
alors‑lplouf1
doit être avant-lplouf2
dansLDLIBS
.
Manipulation 6
Écrire un makefile pour le programme Coriolis sachant que :
- le programme utilise les bibliothèques
Numer_Rec_95
et Jumble ; - les bibliothèques (déjà compilées) sont dans
~/.local
; Numer_Rec_95
utilise Jumble ;- L'unité de programme principale est dans le fichier
coriolis.f
.
Compiler. Puis, pour voir l'effet qu'aurait une modification du
fichier coriolis.f
, taper :
touch coriolis.f
et recompiler.
Nota bene :
- Rappel :
make
traite par défaut la première cible rencontrée. Donc il est judicieux de mettre la dépendance de l'exécutable avant les dépendances entre les objets. - La simple commande
make
produit une série de commandes : compilations des objets, édition de liens. make
a déduit un ordre de compilation à partir des dépendances entre objets.make
ne recompile que le nécessaire.
Génération automatique des dépendances
make
ne regarde pas l'intérieur des fichiers, ne connaît pas les
langages de programmation, ne peut donc pas générer tout seul les
dépendances entre objets induites par l'utilisation de modules Fortran
et les dépendances induites par les include
Fortran. Nécessité de lui
adjoindre un autre outil.
Utiliser makedepf90
. Dépôt des sources de
makedepf90
.
Installer via le gestionnaire de paquet de votre distribution Linux.
Sur Ubuntu :
apt install makedepf90
Manipulation 7
Dans un terminal, dans le dossier du programme Coriolis, sur la ligne de commande :
makedepf90 -free -Wmissing -Wconfused -nosrc -u numer_rec_95 -u jumble *.f
Nota bene : l'option -u
indique qu'un module doit être ignoré. Elle
est utilisée pour un module de bibliothèque, qui ne fait donc pas
partie des sources.
Notion de cible fictive dans make
Une cible fictive (phony target) ne désigne pas un fichier. Si
make
essaie de fabriquer cette cible, il la considère donc toujours
non à jour, et exécute la recette correspondante. make
ne perd pas
de temps à chercher une règle implicite qui permettrait de créer cette
cible. Syntaxe :
.PHONY: nom de la cible
Nous utiliserons des cibles fictives pour la combinaison de make
et
makedepf90
et pour la règle clean
.
Combinaison de make
et makedepf90
Idée : nous redirigeons la sortie standard de makedepf90
vers un
fichier, que nous appelons depend.mk
, et nous faisons référence à ce
fichier dans le makefile :
include depend.mk
Il est inutile de re-créer la liste des dépendances lorsqu'on
modifie le moindre caractère dans un fichier source. Nous voulons
donc que depend.mk
ne soit créé que lorsqu'il n'existe pas ou sur
demande explicite de l'utilisateur (dans le cas où une nouvelle
instruction use
ou une ligne include
est insérée dans le programme).
Si nous mettons les fichiers sources en prérequis de la liste des dépendances :
sources = liste des fichiers .f
depend.mk: ${sources}
makedepf90 ${sources} >depend.mk
alors le fichier depend.mk
est recréé automatiquement à chaque
modification d'un fichier source. Ce n'est pas ce que nous
voulons. Il faut donc une règle sans prérequis :
depend.mk:
makedepf90 ${sources} >depend.mk
Permet bien la création automatique de
depend.mk
s'il n'existe pas. Mais pas la mise à jour. Si depend.mk
existe, la commande :
make depend.mk
donne :
make: « depend.mk » est à jour.
Avec seulement une cible fictive :
.PHONY: depend
depend:
makedepf90 ${sources} >depend.mk
depend.mk
n'est pas créé automatiquement
lorsqu'il n'existe pas. Le makefile ne marche pas. Il affiche
simplement le message d'erreur :
GNUmakefile:ligne: depend.mk: No such file or directory
make: *** Pas de règle pour fabriquer la cible « depend.mk ».
Arrêt.
Il faut donc les deux cibles, réelle et fictive :
.PHONY: depend
depend depend.mk:
makedepf90 [options] ${sources} >depend.mk
Rappel : s'assurer que l'exécutable reste la première cible du makefile.
Création de la liste des objets à partir de la liste des sources
Nous avons besoin de la liste des objets comme prérequis de
l'exécutable et de la liste des sources comme prérequis de depend
.
Ce sont les mêmes listes au suffixe près. Ne pas les dupliquer.
Utiliser une fonctionnalité de make
qui modifie la valeur d'une
variable :
sources = liste des fichiers .f
objects := $(sources:.f=.o)
Nota bene : même s'il existe des fichiers inclus par une ligne
include
Fortran, ils n'ont pas à apparaître dans la variable
sources.
Manipulation 8
Générer automatiquement les dépendances dans le makefile du programme Coriolis. Tester la création automatique et la mise à jour.
Règle clean
Convention : la cible clean
existe dans la plupart des makefiles et
permet d'effacer les produits du makefile. clean
ne devrait effacer
que ce que le makefile est capable de regénérer. Il est donc
conseillé de ne pas utiliser de wildcard dans la recette.
.PHONY: clean
clean: rm -f my_executable ${objects}
Nota bene : make
n'a pas la liste des .mod
, il n'y a donc pas de
moyen simple de les supprimer dans clean
. Mais ce n'est pas grave :
si on veut s'assurer de tout recompiler (si par exemple on a changé
une option de compilation), la suppression des fichiers objets et de
l'exécutable répond bien à ce besoin.
Rappel : s'assurer que l'exécutable (et non la règle clean) reste la première cible du makefile.
Manipulation 9
Ajouter une règle clean
au makefile du programme Coriolis. Tester.