J'ai découvert le soft global disponible dans les dépôt ubuntu et debian. Cet outil permet de tagger du code écrit en C, C++, Yacc, Java et PHP4, ce qui vous servira si vous voulez rechercher un pattern et par exemple modifier directement le fichier concerné.
Récupérons les sources d'un projet (nous allons avoir besoin de git dans l'exemple)
Pour indexer les fichiers nous utiliserons la commande gtags qui crée les fichiers GTAGS GPATH GRTAGS GSYMS utilisés par global pour nos futures recherches.
Etant donné que le répertoire contient des fichiers autres que des fichiers sources c++ (.cpp et .h), on utilise la commande find pour les filtrer :
find . -name "*.cpp" -o -name "*.h"|gtags -v -f -
[Mon Aug 22 09:39:52 CEST 2010] Gtags started.
Using default configuration.
[Mon Aug 22 09:39:52 CEST 2010] Creating 'GTAGS'.
[1] extracting tags of tools/git_id/git_id.cpp
[2] extracting tags of tools/map_extractor/wdt.cpp
[3] extracting tags of tools/map_extractor/adt.cpp
[4] extracting tags of tools/map_extractor/loadlib.cpp
[5] extracting tags of tools/map_extractor/mpq_libmpq.cpp
[6] extracting tags of tools/map_extractor/wdt.h
[7] extracting tags of tools/map_extractor/adt.h
[8] extracting tags of tools/map_extractor/dbcfile.cpp
[9] extracting tags of tools/map_extractor/System.cpp
..
[1096/1099] extracting tags of server/shared/Threading/Threading.cpp
[1097/1099] extracting tags of server/shared/Threading/LockedQueue.h
[1098/1099] extracting tags of server/shared/Threading/Threading.h
[1099/1099] extracting tags of server/shared/Threading/DelayExecutor.cpp
[Mon Aug 22 09:40:18 CEST 2010] Done.
Tous les fichiers sont à présent indexés. On peut vérifier la taille des fichiers générés par gtags :
du -sh G*
280K GPATH
3,2M GRTAGS
6,6M GSYMS
2,1M GTAGS
Vous pouvez aussi choisir de les placer ailleurs, mais je vous laisse le faire en exercice. Ce qui est intéressant dans cet outil c'est qu'il est possible de rechercher la définition d'une fonction aussi simplement que :
global -x EndQuery
EndQuery 60 server/shared/Database/QueryResult.cpp void QueryResult::EndQuery()
On peut aussi vouloir rechercher les références à cette fonction dans le code :
global -rx EndQuery
EndQuery 37 server/shared/Database/QueryResult.cpp EndQuery();
EndQuery 50 server/shared/Database/QueryResult.cpp EndQuery();
EndQuery 59 server/shared/Database/QueryResult.h void EndQuery();
Comme vous le voyez il est possible de faire des recherches intéressantes. Il est aussi possible d'utiliser des expressions régulières et de rechercher des motifs diverses :
global -gx mCurrentRow
mCurrentRow 28 server/shared/Database/QueryResult.cpp mCurrentRow = new Field[mFieldCount];
mCurrentRow 29 server/shared/Database/QueryResult.cpp ASSERT(mCurrentRow);
mCurrentRow 32 server/shared/Database/QueryResult.cpp mCurrentRow[i].SetType(ConvertNativeType(fields[i].type));
mCurrentRow 55 server/shared/Database/QueryResult.cpp mCurrentRow[i].SetValue(row[i]);
mCurrentRow 62 server/shared/Database/QueryResult.cpp if (mCurrentRow)
mCurrentRow 64 server/shared/Database/QueryResult.cpp delete [] mCurrentRow;
mCurrentRow 65 server/shared/Database/QueryResult.cpp mCurrentRow = 0;
mCurrentRow 45 server/shared/Database/QueryResult.h Field *Fetch() const { return mCurrentRow; }
mCurrentRow 47 server/shared/Database/QueryResult.h const Field & operator [] (int index) const { return mCurrentRow[index]; }
mCurrentRow 53 server/shared/Database/QueryResult.h Field *mCurrentRow;
Reportez vous à la documentation pour en savoir plus. Dernier outil que je trouve très intéressant est le wrapper globash qui permet en plus de se promener directement dans les fichiers à partir des résultats obtenus. Pour cela, lancez la commande globash et acceptez de créer le répertoire .globash lors du premier lancement :
globash
GloBash --- Global facility for Bash
GloBash needs working directory.
Create '/home/cyril/.globash'? ([y]/n) y
Created.
Welcome to Globash! When you need help, please type 'ghelp'.
Vous pouvez ensuite utiliser les mêmes commandes mais sans l'option x qui est activée par défaut :
[/home/cyril/src-repo/git-repo/wow/src] g mCurrentRow
> 1 mCurrentRow 28 server/shared/Database/QueryResult.cpp mCurrentRow = new Field[mFieldCount];
2 mCurrentRow 29 server/shared/Database/QueryResult.cpp ASSERT(mCurrentRow);
3 mCurrentRow 32 server/shared/Database/QueryResult.cpp mCurrentRow[i].SetType(ConvertNativeType(fields[i].type));
4 mCurrentRow 55 server/shared/Database/QueryResult.cpp mCurrentRow[i].SetValue(row[i]);
5 mCurrentRow 62 server/shared/Database/QueryResult.cpp if (mCurrentRow)
6 mCurrentRow 64 server/shared/Database/QueryResult.cpp delete [] mCurrentRow;
7 mCurrentRow 65 server/shared/Database/QueryResult.cpp mCurrentRow = 0;
8 mCurrentRow 45 server/shared/Database/QueryResult.h Field *Fetch() const { return mCurrentRow; }
9 mCurrentRow 47 server/shared/Database/QueryResult.h const Field & operator [] (int index) const { return mCurrentRow[index]; }
10 mCurrentRow 53 server/shared/Database/QueryResult.h Field *mCurrentRow;
Vous pouvez lister à nouveau les résultats obtenus :
[/home/cyril/src-repo/git-repo/wow/src] list
> 1 mCurrentRow 28 server/shared/Database/QueryResult.cpp mCurrentRow = new Field[mFieldCount];
2 mCurrentRow 29 server/shared/Database/QueryResult.cpp ASSERT(mCurrentRow);
3 mCurrentRow 32 server/shared/Database/QueryResult.cpp mCurrentRow[i].SetType(ConvertNativeType(fields[i].type));
4 mCurrentRow 55 server/shared/Database/QueryResult.cpp mCurrentRow[i].SetValue(row[i]);
5 mCurrentRow 62 server/shared/Database/QueryResult.cpp if (mCurrentRow)
6 mCurrentRow 64 server/shared/Database/QueryResult.cpp delete [] mCurrentRow;
7 mCurrentRow 65 server/shared/Database/QueryResult.cpp mCurrentRow = 0;
8 mCurrentRow 45 server/shared/Database/QueryResult.h Field *Fetch() const { return mCurrentRow; }
9 mCurrentRow 47 server/shared/Database/QueryResult.h const Field & operator [] (int index) const { return mCurrentRow[index]; }
10 mCurrentRow 53 server/shared/Database/QueryResult.h Field *mCurrentRow;
Mieux encore, vous pouvez demander à vous rendre directement à la ligne du fichier concerné (cela s'appuie sur la définition de la variable EDITOR)
[/home/cyril/src-repo/git-repo/wow/src] show 2
[/home/cyril/src-repo/git-repo/wow/src] l
1 mCurrentRow 28 server/shared/Database/QueryResult.cpp mCurrentRow = new Field[mFieldCount];
> 2 mCurrentRow 29 server/shared/Database/QueryResult.cpp ASSERT(mCurrentRow);
3 mCurrentRow 32 server/shared/Database/QueryResult.cpp mCurrentRow[i].SetType(ConvertNativeType(fields[i].type));
4 mCurrentRow 55 server/shared/Database/QueryResult.cpp mCurrentRow[i].SetValue(row[i]);
5 mCurrentRow 62 server/shared/Database/QueryResult.cpp if (mCurrentRow)
6 mCurrentRow 64 server/shared/Database/QueryResult.cpp delete [] mCurrentRow;
7 mCurrentRow 65 server/shared/Database/QueryResult.cpp mCurrentRow = 0;
8 mCurrentRow 45 server/shared/Database/QueryResult.h Field *Fetch() const { return mCurrentRow; }
9 mCurrentRow 47 server/shared/Database/QueryResult.h const Field & operator [] (int index) const { return mCurrentRow[index]; }
10 mCurrentRow 53 server/shared/Database/QueryResult.h Field *mCurrentRow;
Vous pouvez taper exit pour sortir ou ghelp pour en savoir plus.
Cet outil est vraiment très rapide pour indexer le contenu et très utile avec son wrapper pour se balader directement dans les fichiers et effectuer des modifications si nécessaire. N'oubliez pas bien sûr de rafraîchir le contenu indexé ensuite en ajoutant -i à la commande initiale pour activer l'indexation incrémentale (Vous pouvez bien sûr retirer l'option -v qui active le mode verbeux) :
find . -name "*.cpp" -o -name "*.h"|gtags -i -v -f -
checking /home/cyril/src-repo/git-repo/wow/src/GTAGS
GTAGS found at '/home/cyril/src-repo/git-repo/wow/src/GTAGS'.
[Mon Aug 22 10:08:12 CEST 2010] Gtags started.
Using default configuration.
Tag found in '/home/cyril/src-repo/git-repo/wow/src'.
Incremental update.
Global databases are up to date.
[Mon Aug 22 10:08:12 CEST 2010] Done.
Je m'étais aussi intéressé à l'outil gonzui qui est aussi disponible sous forme de paquet et s'appuie sur BerkeleyDB pour stocker ses tags. Cependant, cet outil est beaucoup plus lent que global à l'indexation puisque que l'on passe de 3 à 130 secondes, et aussi lors des recherches sur des expressions régulières. Il ne dispose pas d'un wrapper semblable à globash qui est sans nul doute très utile lorsqu'il faut débugger, et occupe nécessite beaucoup plus de places que global (167 Mo contre 13 Mo).
Voilà, j'espère que ce billet vous sera utile dans vos prochaines investigations sur du code concernant les bases de données ou autres, mais comme vous avez pu le remarquer j'ai sciemment cherché un exemple dans la branche database :)