Un fichier d'archive est un fichier regroupant des sous-fichiers nommés appelés membres. Les fichiers d'archive sont gérés par le programme ar et servent principalement de bibliothèques de sous-programmes pour l'édition de liens.
Chaque membre individuel d'un fichier d'archive peut être utilisé comme cible ou comme prérequis (prerequisite) dans make. On désigne le membre nommé member contenu dans le fichier d'archive archive de la façon suivante :
archive(member)
Cette construction n'est disponible que dans les cibles et les prérequis, pas dans les recettes (recipe) ! La plupart des programmes que vous pourriez employer dans une recette ne prennent pas en charge cette syntaxe et ne peuvent pas agir directement sur les membres d'archive. Seuls ar et d'autres programmes spécialement conçus pour opérer sur les archives en sont capables. Par conséquent, une recette valide pour mettre à jour une cible membre d'archive utilisera probablement ar. Par exemple, la règle suivante indique de créer un membre hack.o dans l'archive foolib en copiant le fichier hack.o :
foolib(hack.o) : hack.o
ar cr foolib hack.o
En réalité, presque toutes les cibles membres d'archive sont mises à jour exactement de cette manière, et il existe une règle implicite qui le fait pour vous. [important] Le drapeau ‘c’ de ar est requis si le fichier d'archive n'existe pas encore.
Pour désigner plusieurs membres de la même archive, vous pouvez écrire tous les noms de membres ensemble entre les parenthèses. Par exemple :
foolib(hack.o kludge.o)
équivaut à :
foolib(hack.o) foolib(kludge.o)
Vous pouvez aussi utiliser des caractères génériques (joker) de style shell dans une référence à un membre d'archive. Voir Utiliser des caractères génériques dans les noms de fichiers. Par exemple, ‘foolib(*.o)’ se développe en tous les membres existants de l'archive foolib dont le nom se termine par ‘.o’ ; par exemple ‘foolib(hack.o) foolib(kludge.o)’.
Rappelez-vous qu'une cible de la forme a(m) représente le membre nommé m dans le fichier d'archive a.
Lorsque make recherche une règle implicite pour une telle cible, comme fonctionnalité spéciale il considère les règles implicites qui correspondent à (m), en plus de celles qui correspondent à la cible réelle a(m).
Cela fait correspondre une règle spéciale dont la cible est (%). Cette règle met à jour la cible a(m) en copiant le fichier m dans l'archive. Par exemple, elle met à jour la cible membre d'archive foo.a(bar.o) en copiant le fichier bar.o dans l'archive foo.a en tant que membre nommé bar.o.
Lorsque cette règle est enchaînée avec d'autres, le résultat est très puissant. Ainsi, ‘make "foo.a(bar.o)"’ (les guillemets sont nécessaires pour empêcher que ‘(’ et ‘)’ soient interprétés de façon spéciale par le shell), en présence d'un fichier bar.c, suffit à déclencher l'exécution de la recette suivante, même sans makefile :
cc -c bar.c -o bar.o ar r foo.a bar.o rm -f bar.o
Ici, make a envisagé le fichier bar.o comme un fichier intermédiaire. Voir Chaînes de règles implicites.
Les règles implicites de ce genre s'écrivent à l'aide de la variable automatique ‘$%’. Voir Variables automatiques.
Le nom d'un membre d'archive ne peut pas contenir de nom de répertoire, mais il peut être utile, dans un makefile, de faire comme s'il le pouvait. Si vous écrivez une cible membre d'archive foo.a(dir/file.o), make effectuera la mise à jour automatique avec cette recette :
ar r foo.a dir/file.o
ce qui a pour effet de copier le fichier dir/file.o dans un membre nommé file.o. En lien avec un tel usage, les variables automatiques %D et %F peuvent être utiles.
Un fichier d'archive utilisé comme bibliothèque contient habituellement un membre spécial nommé __.SYMDEF qui conserve un répertoire des noms de symboles externes définis par tous les autres membres. Après avoir mis à jour l'un quelconque des autres membres, vous devez mettre à jour __.SYMDEF afin qu'il récapitule correctement les autres membres. Cela se fait en exécutant le programme ranlib :
ranlib archivefile
Normalement, vous placeriez cette commande dans la règle du fichier d'archive et feriez de tous les membres du fichier d'archive des prérequis de cette règle. Par exemple :
libfoo.a: libfoo.a(x.o y.o …)
ranlib libfoo.a
L'effet est de mettre à jour les membres d'archive x.o, y.o, etc., puis de mettre à jour le membre répertoire de symboles __.SYMDEF en exécutant ranlib. Les règles pour mettre à jour les membres ne sont pas indiquées ici ; le plus souvent vous pouvez les omettre et vous appuyer sur la règle implicite qui copie les fichiers dans l'archive, décrite à la section précédente.
Cela n'est pas nécessaire si vous utilisez le programme ar de GNU, qui met à jour le membre __.SYMDEF automatiquement.
Les règles intégrées pour mettre à jour les archives sont incompatibles avec les builds parallèles. Ces règles (exigées par la norme POSIX) ajoutent chaque fichier objet dans l'archive au fur et à mesure de sa compilation. Lorsque les builds parallèles sont activés, cela permet à plusieurs commandes ar de mettre à jour la même archive simultanément, ce qui n'est pas pris en charge.
Si vous voulez utiliser des builds parallèles avec des archives, vous pouvez redéfinir les règles par défaut en ajoutant ces lignes à votre makefile :
(%) : % ; %.a : ; $(AR) $(ARFLAGS) $@ $?
La première ligne modifie la règle qui met à jour un objet individuel dans l'archive pour qu'elle ne fasse rien, et la seconde ligne modifie la règle par défaut de construction d'une archive pour qu'elle mette à jour tous les objets périmés ($?) en une seule commande.
Bien entendu, vous devrez toujours déclarer les prérequis de votre bibliothèque en utilisant la syntaxe d'archive :
libfoo.a: libfoo.a(x.o y.o …)
Si vous préférez écrire une règle explicite, vous pouvez utiliser :
libfoo.a: libfoo.a(x.o y.o …)
$(AR) $(ARFLAGS) $@ $?
Vous pouvez écrire un type particulier de règle de suffixe pour traiter les fichiers d'archive. Voir Les règles de suffixe à l'ancienne pour une explication complète des règles de suffixe. Les règles de suffixe d'archive sont obsolètes dans GNU make, car les règles de motif pour les archives constituent un mécanisme plus général (voir Règle implicite pour les cibles membres d'archive). Elles sont néanmoins conservées pour la compatibilité avec les autres make.
Pour écrire une règle de suffixe d'archive, il suffit d'écrire une règle de suffixe en utilisant le suffixe cible ‘.a’ (le suffixe habituel des fichiers d'archive). Par exemple, voici la règle de suffixe à l'ancienne pour mettre à jour une archive de bibliothèque à partir de fichiers source C :
.c.a:
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
$(AR) r $@ $*.o
$(RM) $*.o
Cela fonctionne exactement comme si vous aviez écrit la règle de motif :
(%.o): %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
$(AR) r $@ $*.o
$(RM) $*.o
En fait, c'est précisément ce que fait make lorsqu'il rencontre une règle de suffixe ayant ‘.a’ comme suffixe cible. Toute règle à double suffixe ‘.x.a’ est convertie en une règle de motif dont le motif cible est ‘(%.o)’ et le motif de prérequis ‘%.x’.
Comme vous pourriez vouloir utiliser ‘.a’ comme suffixe pour un autre type de fichier, make convertit également les règles de suffixe d'archive en règles de motif de la manière habituelle (voir Les règles de suffixe à l'ancienne). Ainsi, une règle à double suffixe ‘.x.a’ produit deux règles de motif : ‘(%.o): %.x’ et ‘%.a: %.x’.