Invocation du compilateur via un script shell

Idée de stocker la commande de compilation

Nous avons vu qu'il était en général utile de spécifier de nombreuses options de débogage ou d'optimisation, ainsi que des options qui référencent des bibliothèques. Comment alors ne pas retaper une très longue commande à chaque fois que nous avons besoin de recompiler notre programme ? Utiliser le rappel de commande dans le terminal ? Insuffisant d'une session à l'autre. Pénible de chercher dans l'historique des commandes.

Il faut donc d'une manière ou d'une autre stocker la commande de compilation. L'idée la plus simple qui vient à l'esprit est de stocker la commande telle quelle. Où ? En commentaire dans le fichier source ?

  • Pas pratique : copier-coller nécessaire à chaque session.
  • Pas logique : information spécifique à un compilateur et une machine avec le code source Fortran indépendant du compilateur.

Déjà mieux que dans le fichier source : idée de stocker la commande de compilation dans un fichier à part, c'est-à-dire un script shell.

Manipulation 2

  • Créer un fichier contenant la commande de compilation du programme trivial.
  • Ajouter les options de débogage.
  • Se servir de ce fichier pour compiler.

Programme à plusieurs fichiers

Il est intéressant de répartir le code d'un programme sur plusieurs fichiers pour : la clarté ; la gestion de versions ; le travail à plusieurs ; diminuer le temps de compilation (cf. infra).

L'idée simple de stocker la commande de compilation telle quelle se heurte à la difficulté engendrée par les programmes à plusieurs fichiers. Nous allons voir pourquoi. Mais expliquons d'abord comment compiler un programme à plusieurs fichiers.

Possibilité de compiler d'un coup tous les fichiers composant un programme :

gfortran [options] foo1.f90 foo2.f90 ...

mais ce n'est pas conseillé (cf. infra).

Décomposition de la compilation

Au lieu de compiler d'un coup tous les fichiers composant un programme comme ci-dessus, il est aussi possible de décomposer la compilation en plusieurs commandes :

gfortran [options de compilation] -c foo1.f90
gfortran [options de compilation] -c foo2.f90
gfortran [options d'édition de liens] foo1.o foo2.o

Il y a \(n\) commandes pour la compilation de \(n\) fichiers, plus une commande finale, dite "d'édition de liens". Cette décomposition ouvre la perspective de ne recompiler que le nécessaire et donc de gagner en temps de compilation. Examinons de plus près les commandes qui décomposent la compilation :

  • La compilation d'un fichier source seul, qui est une partie seulement d'un programme, produit un "fichier objet", en langage machine, non exécutable, suffixe .o.
  • La création du fichier exécutable à partir des fichiers objets et des bibliothèques s'appelle l'édition de liens.
  • Les fichiers .o sont des intermédiaires produits par la compilation. Ils ne sont pas utiles à l'exécution du programme.

  • L'option supplémentaire -c (la même pour tous les compilateurs) est requise pour la compilation d'un fichier qui est une partie seulement d'un programme (sans édition de liens).

L'idée pour réduire le temps de compilation, après modification d'un fichier source, est donc de ne re-compiler que ce fichier et refaire l'édition de liens.

Ordre de compilation

L'utilisation de modules en Fortran implique un ordre de compilation. Ordre entre les fichiers s'il y a plusieurs fichiers, ou ordre d'écriture des modules à l'intérieur d'un fichier.

S'il y a :

use bar_m

dans la procédure foo alors le module bar_m doit être compilé avant la procédure foo (avant le module contenant la procédure foo). Donc :

  • Si bar_m et foo sont dans le même fichier alors bar_m doit être avant foo dans ce fichier.
  • Sinon le fichier contenant bar_m doit être compilé d'abord.

Fichier .mod

La compilation d'un fichier contenant un module crée un fichier .mod.

module

.mod

  • bar_m.mod si le module s'appelle bar_m (le nom du fichier .mod est formé à partir du nom du module et non à partir du nom du fichier .f90)

  • bar_m.mod contient (sous forme lisible seulement par le compilateur) les informations sur l'interface du module, y compris l'interface des procédures publiques du module (function ou subroutine, type des arguments, scalaires ou tableaux, intent, etc.).

S'il y a :

use bar_m

dans la procédure foo alors :

gfortran [options de compilation] -c foo.f90

a besoin de bar_m.mod. D'où la contrainte d'ordre de compilation.

Les fichiers .mod sont, comme les fichiers .o, des intermédiaires produits par la compilation. Ils ne sont pas utiles à l'exécution du programme.

Graphe de dépendances

En regardant les fichiers sources, on peut déduire une suite de dépendances. Imaginons par exemple :

foo1.f90 → foo2.f90, bar.f90
bar.f90 → foo2.f90, plouf1.f90
plouf.f90 → foo2.f90, foo1.f90

(la flèche signifie "doit être compilé après"). La liste de dépendances définit le graphe des dépendances (un graphe directionnel acyclique) :

graphe

Mais il reste une analyse à faire pour trouver l'ordre de compilation : choisir un parcours de ce graphe.

Nécessité d'un outil

Le simple listage des commandes de compilations telles quelles dans un fichier shell est donc insuffisant :

  • il ne permet pas de recompiler uniquement le nécessaire ;
  • il ne permet pas de trouver l'ordre de compilation.

Accessoirement : nous ne voulons pas répéter l'écriture des options de compilation :

gfortran [options de compilation] -c foo1.f90
gfortran [options de compilation] -c foo2.f90

donc nous voulons stocker les options de compilation dans une variable.

make est l'outil historique qui permet de pallier à l'insuffisance d'un fichier shell contenant les commandes de compilations.