Les tags à la rescousse des codeurs
Par Mickaël le 20 septembre 2008, 19h14 - Scripts - Lien permanent
Etags est un tagueur de fichiers source. Il permet de créer un fichier comportant toutes les déclarations de variables, fonctions ou classes correspondant à un langage de programmation. L'intérêt est de pouvoir sauter à une déclaration dans un fichier lorsqu'on le souhaite.
Pour quoi faire ?
Lorsque l'on code et qu'il y a des dépendances dans de nombreux fichiers différents, il n'est pas aisé de se rappeler exactement à quoi correspond une fonction, une structure ou tout autre déclaration qu'on peut être amené à comprendre ou encore à modifier. Par exemple, les programmes qui utilisent des bibliothèques importante (comme un OS), ont des dépendances nombreuses.
Un outil vraiment indispensable à tout programmeur est un bon éditeur de texte. Les choix possible sont vaste. Néanmoins, pour éditer du code, parmi les meilleurs on trouve Emacs et Vim. Chacun d'eux à ses détracteurs et ses fans. Vim est très intéressant pour l'édition de fichiers en console. Pour le code complexe, je préfère Emacs, mais ça ne change pas grand chose au final.
Un éditeur c'est bien pour éditer, mais les tags c'est indispensable pour coder ! L'intérêt est de pouvoir sauter dans les fichiers, de déclaration en déclaration avec la possibilité de retour. Il existe un tagueur pour chacun de ces deux éditeurs. Il sont identique en fonctionnalités mais créent des format de fichiers différents. Pour Emacs c'est etags et pour VI c'est... ctags !
Pour les utiliser, rien de plus simple : etags *.c pour tager les fichiers C du répertoire courant. Cela vas créer un fichier TAGS (tags pour VI) contenant les relation mot clé <-> fichier et ligne.
Pour l'utiliser sous Emacs, il suffit d'aller sur le mot désiré et de presser M-.[1] pour sauter à l'endroit de la déclaration (si possible) et M-* pour revenir. On peut inclure tout les fichiers qui nous sembles nécessaires, par exemple avec find :
find . /usr/src/linux -name '*.[chSs]' -print0 | xargs -0 etags
Avec Emacs, certaines fonctionnalités intéressantes s'offrent à nous, notamment pour :
- changer de fichier d'index : (M-x) visit-tags-table ;
- voir toutes les commandes relatives aux tags : (M-h a) tags.
Amélioration pour le Ruby
Néanmoins, etags ne connais pas tout les langages, notamment le Ruby. Mais ce n'est absolument pas gênant, car il suffit de lui apprendre grâce aux regexp. Voici le petit script permettant de taguer tous les fichiers intéressants pour un projet :
CONST='[A-Z][a-zA-Z0-9_]*' VAR='[a-zA-Z0-9_]+' alias AetagsR="find . /usr/lib/ruby -name '*.rb' -print0 | xargs -0 etags -r '/^[ \t]*module[ \t]+\($CONST\)/\1/' -r '/^[ \t]*class[ \t]+\($CONST\)/\1/' -r '/^[ \t]*def[ \t]+\($CONST\.\)?\($VAR\)/\2/' -r '/^[ \t]*\($CONST\)[ \t]*=/\1/'" unset CONST VAR
Le but du jeux est de trouver tous les fichiers correspondant à un pattern (ici les fichiers Ruby : *.rb) des bibliothèques et de les parser afin d'en faire ressortir les déclarations qui peuvent nous intéresser (les modules, les classes, les fonctions et les constantes).
Si on utilise ZSH comme shell la commande peut bien entendu être raccourcis en supprimant la première partie (find) et en indiquant à etags les fichiers par : /usr/lib/ruby/**/*.rb.
Si ce code est placé dans le ~/.bashrc ou encore mieux, le ~/.zshrc, alors la nouvelle commande AetagsR crée le fichier TAGS pour les fichiers Ruby !
Amélioration pour le C
Une utilisation classique pour parcourir du code dans un grand logiciel, comme Xen par exemple, consiste simplement à exclure les architectures qui ne nous intéressent pas (64 bits et PowerPC) :
find . ~/xen-source/xen-3-3.2.0/xen/ -name '*.[chsS]' -a -not -path '*64*' -a -not -path '*powerpc*' -print0 | xargs -0 etags
Une fonctionnalité qui fonctionne mal pour l'analyse de code C, c'est le référencement de code externe. Étant donné la grande variété de déclarations possible, il n'est pas facile de toutes les prendre en compte. On peut intégrer ceci dans un Makefile pour faciliter la génération des tags :
VERSION = $(shell uname -r)
SRC = /lib/modules/$(VERSION)/build
INCLUDE = $(SRC)/include
#INCLUDE = /usr/src/linux-source-2.6.18/include
tags:
find $(INCLUDE) . -path '$(INCLUDE)asm-olpha' -prune -o -path '$(INCLUDE)asm-orm' -prune -o -path '$(INCLUDE)asm-orm26' -prune -o -path '$(INCLUDE)asm-frv' -prune -o -path '$(INCLUDE)asm-h8300' -prune -o -path '$(INCLUDE)asm-ia64' -prune -o -path '$(INCLUDE)asm-m32r' -prune -o -path '$(INCLUDE)asm-m68k' -prune -o -path '$(INCLUDE)asm-m68knommu' -prune -o -path '$(INCLUDE)asm-mips' -prune -o -path '$(INCLUDE)asm-parisc' -prune -o -path '$(INCLUDE)asm-powerpc' -prune -o -path '$(INCLUDE)asm-ppc' -prune -o -path '$(INCLUDE)asm-s390' -prune -o -path '$(INCLUDE)asm-sh' -prune -o -path '$(INCLUDE)asm-sh64'-prune -o -path '$(INCLUDE)asm-sparc' -prune -o -path '$(INCLUDE)asm-sparc64' -prune -o-path '$(INCLUDE)asm-um' -prune -o -path '$(INCLUDE)asm-v850' -prune -o -path '$(INCLUDE)asm-x86_64' -prune -o -path '$(INCLUDE)asm-xtensa' -prune -o -name "*.[chsS]" -print0 | \
xargs -0 etags \
--declarations \
-r "/#define[ \t]+\([-_A-Za-z0-9]+\)/\1/"
# --declarations -D \
# -r "/extern[ \t]+.*?[ \t(*]*\([-_A-Za-z0-9]+\)[ \t)]*[ \t]*\(?:(.*?)\)?[ \t]*;/\1/"
Ce code est un peut rebutant, mais il peut bien sûr être en partie généré par une ligne de script (ici en ZSH avec l'option EXTENDED_GLOB activée) :
for f in asm-*~asm-i386~asm-generic~asm-cris; do echo -n " -o -path '\$(INCLUDE)$f' -prune"; done
Voilà de quoi rendre plus agréable le parcours de code et surtout de gagner du temps... pour des projets libres bien sûr !
Ressources : man etags, Linux Journal
Notes
[1] Pour les non Emacsiens, M ou Meta est équivalent à la touche Alt sur un clavier de type PC.
Commentaires
Juste une remarque, de mauvais goût surement, relative à l'orthographe : « Chacun d'eux à *s*es détracteurs et *s*es fans ».
Aussi, remarque de style cette fois, en général, on note M-x (pour Meta-x) au lieu de Alt + x, les Emacsien sont moins perdus comme ça ;).
Autrement, merci pour ce billet détaillé !
Olivier;