Utilisation des Imakefiles

Article pour l'Echo de Linux (Octobre 1996)

Pierre Ficheux (pierre@rd.lectra.fr)


Introduction

Le système des Imakefiles permet de générer le fichier Makefile d'une application en tenant compte des particularités des diverses plate-formes de génération de cette application.

Le concept des Imakefiles a été introduit dans les distributions de X11. En effet, X étant supporté par un grand nombre d'implémentations d'UNIX, la gestion des particularités des systèmes deviendrait vite inextricable si l'on se limitait à l'utilisation des simples Makefiles. Les Imakefiles permettent de résoudre élégamment (et simplement !) le problème en fournissant en plus une procédure standard de génération d'une application X.

Description

Principe de base

Le système des Imakefiles utilise 2 entités principales :

L'utilitaire imake

Le programme imake utilise les fichiers de configuration pour générer un fichier Makefile à partir du fichier Imakefile de l'application. Ce programme est basé sur le préprocesseur C (cpp). Les fichiers de configuration (ainsi que le fichier Imakefile) utiliseront la syntaxe du cpp (#define ,#include, commentaires = /* */, ...).

Fichiers de configuration

Les fichiers de configuration sont de 2 types :

Les fichiers généraux sont situés sur le répertoire de configuration qui pour XFree86 est /usr/X11R6/lib/X11/config. Les principaux fichiers sont :

Le fichier correspondant à la plate-forme est sélectionné lors de l'appel à imake car celui-ci définit un symbole caractéristique de l'architecture de la plate-forme (sun, lectra, sgi, ...). Le nom du fichier est associé au nom du symbole défini.

Ces fichiers permettent de surcharger les valeurs par défaut des fichiers Imake.tmpl et Imake.rules. Par exemple, dans le fichier lectra.cf, on pourra lire :

	#define HasGcc   	YES
	#define SystemV  	YES
	#define StandardDefines	-DSYSV -DUSG ...

Grace au fichier .cf, les spécificités du système seront prises en compte AUTOMATIQUEMENT lors de la génération du Makefile et ce sans AUCUNE intervention de l'utilisateur.

Le fichier Imakefile est une suite de macro-instructions dont la plupart sont définies dans le fichier Imake.rules. Il peut aussi contenir des affectations de variables ou bien des définitions #define .

Par exemple, pour générer le Makefile d'une application simple constituée d'un source C unique utilisant seulement la Xlib, on aura un Imakefile du type :

	LDLIBS = $(XLIB)
	SimpleProgramTarget(xsimple)

Imake se charge d'ajouter les particularités de la plate-forme utilisée .

Génération du Makefile, cas simple

Pour générer le Makefile, on doit effectuer les opérations suivantes :

L'écriture d'un Imakefile peut s'avérer être une opération complexe si l'on ne prends pas de précautions élémentaires :

Le fichier doit être organisé de manière ordonnée, par exemple, définition des variables en tête de fichier, appel des macros ensuite.

Le Makefile généré contient un certain nombres de buts standards que l'on invoquera par un classique make nom_d_un_but :

Pour la création d'un Imakefile type, nous allons partir d'une hypothèse d'application simple :

En premier lieu, on peut définir les librairies utilisées soit:

	LDLIBS = XawClientLibs
ou bien :
	LDLIBS = $(XAWLIB) $(XMULIB) $(XTOOLLIB) $(XLIB) $(EXTRA_LIBRARIES)

Ensuite, la liste des fichier sources puis des objets soit:

       	SRCS = xtest1.c s1.c s2.c s3.c
	OBJS = xtest1.o s1.o s2.o s3.o 

Les noms SRCS et OBJS sont imposés par la macro-instruction qui suit.

Ensuite, on utilise les macros suivantes pour créer les buts décrits ci-dessus soit:

	ComplexProgramTarget (xtest1)
	InstallAppDefaults (XTest1)

Cette denière macro génère le but d'installation du fichier de ressources.

Pour créer le Makefile, il suffit de lancer :

	xmkmf
qui correspond en fait à l'appel :
	imake -DUseInstalled -Irépertoire_de_config_X11

Le flag UseInstalled indique à imake que l'on travaille sur une arborescence X installée et non sur l'arborescence des sources. Le répertoire de config X11 contient les fichiers .cf et .rules.

L'option -I indique le chemin de recherche des fichiers de configuration.

L'appel au shell-script xmkmf (fourni dans la distribution MIT) évite à l'utilisateur de lancer imake directement.

Lorsque le Makefile est créé, vous pouvez lancer la séquence suivante :

	make depend        	pour les dépendances
	make               	pour compiler
	make install       	pour installer

Si vous vouler générer un nouveau Makefile :

	make Makefile

Cas plus complexes...

Traitement de sous-répertoires

Si votre application utilise un sous-répertoire , vous devez ajouter en tête du fichier Imakefile :

	#define IHaveSubdirs
	#define PassCDebugFlags 'CDEBUGFLAGS=$(CDEBUGFLAGS)'
Puis au niveau des appels de macros :
	MakeSubdirs(nom_répertoire)
	DependSubdirs(nom_répertoire)

La première macro crée le but Makefiles permettant la génération successive des Makefiles par:

	make Makefiles
La deuxième fait de même pour le but depend.

Règle spéciale de compilation

Vous pouvez également avoir besoin de définir une règle de compilation particulière pour un fichier .c donné. Vous utiliserez alors la règle :

	SpecialObjectRule(f.o, f.c, -DSPECIAL)

Le fichier f.o sera créé à partir du fichier f.c avec le flag SPECIAL défini à la compilation.

Application à plusieurs exécutables ou complexe

Pour le cas d'application constituée de plusieurs exécutables gérés par le même Imakefile, le fichier Imake.rules contient trois macros équivalentes à la macro ComplexProgramTarget. Il s'agit de:

	ComplexProgramTarget_1
	ComplexProgramTarget_2
	ComplexProgramTarget_3

Ces macros créent les mêmes buts dans le fichier Makefile que ComplexProgramTarget sauf que les noms des listes des fichiers sources et objets seront respectivement: SRCS1, OBJS1, SRCS2, OBJS2, SRCS3, OBJS3.

Enfin, si votre application est trop complexe pour utiliser la règle simple :

	ComplexProgramTarget (xtest1)
Vous pouvez utiliser la macro générale :
	NormalProgramTarget(program,objects,deplibs,locallibs,syslibs)
Dans ce cas la, vous devez utiliser également les macros :
	DependTarget()
	InstallProgram(program, bindir)

Respectivement pour générer les buts depend et d'installation de l'exécutable (but install) .

Macros d'installation

Comme nous l'avons vu précédemment, vous pouvez utiliser InstallProgram pour installer un exécutable sur le répertoire des binaires. Une version plus générale est :

	InstallProgramWithFlags(program,bindir,instflags)

qui permet de passer des options à l'utilitaire d'installation (en général /etc/install).

La macro:

	InstallManPage(manfile, mandir)

installe un fichier man sur le répertoire adéquat.

Enfin, vous pouvez installer un fichier quelconque (non exécutable) par la macro:

	InstallNonExec (file,instdir)

Buts directs

Vous pouvez inclure dans un Imakefile un but Makefile directement sans passer par une macro. Par exemple, si votre application utilise un programme qui n'a rien à voir avec X, vous pouvez toujours écrire dans l'Imakefile:

	prog: $(PROGOBJS)
		$(CC) -o prog $(PROGOBJS)

Les lignes seront incluses DIRECTEMENT dans le fichier Makefile. Notez bien que dans ce cas là, il convient de respecter la syntaxe du Makefile (les tabulations en particuliers).

Conclusion

L'utilisation des Imakefiles :

On peut donc regretter que ce système soit encore trop peu utilisé et que certaines distributions commerciales binaires ne le fournissent pas (ou fournissent une version érronée)...

Bibliographie

Vous pouvez également vous inspirer d'exemples comme ceux ci-dessous :