ccache
est un compiler cache, sous licence GPLv3. Il permet d'accélérer la recompilation de projets C et C++, mais aussi CUDA ou encore Objective-C et Objective-C++ (quelqu'un utilise encore ces 2 langages en 2022 ?). Il supporte les principaux compilateurs du marché: GCC, Clang, MSVC.
Fonctionnement
Comment ccache
améliore le build d'un projet ? C'est en fait très simple. Quand vous compilez un fichier, il met le résultat en cache. Quand vous recompilez ce même fichier avec les mêmes options, il récupère directement le fichier objet du cache au lieu de le compiler vraiment.
Vous allez me dire :
Super, mais
make
ne recompile que les fichiers qui ont besoin de l'être, alors pourquoiccache
ferait mieux ?
Tout simplement parce make
regarde l'état de votre projet et le compare au précédent build. Imaginez exécuter les commandes suivantes :
make
make clean
make
Deux scénarios sont possibles lors de l'exécution de la dernière commande :
- Vous n'avez pas
ccache
, etmake
recompile tous les fichiers. - Vous avez
ccache
, tous les fichiers objets sont récupérés du cache et le build est très rapide.
Le bonheur quand tu changes de branche
Vous allez me dire que vous faites rarement des make clean
pour le plaisir. C'est vrai. Mais pensez à un scénario qui vous arrive souvent : le changement de branche avec Git (ou SVN, ou autre).
Imaginez un dossier dans lequel vous avez cloné votre dépôt Git. Vous êtes sur la branche master
et vous avez compilé l'intégralité du projet. Vous faites un checkout d'une autre branche et vous travaillez sur cette branche. Une heure plus tard, vous revenez sur master
.
Avec ccache
, recompiler la branche master
sera extrêmement rapide car tout est déjà dans le cache !
Le cache n'est pas limité à un dossier. Si vous avez plusieurs branches dans plusieurs dossiers, et que certains fichiers sont compilés de la même manière dans plusieurs branches, alors ccache
vous fera aussi gagner du temps.
Mettre en place ccache sur son projet
Attention les yeux, ça va aller vite !
Vous téléchargez ccache
, vous l'installez et vous l'ajouter au PATH.
Vous ajoutez les lignes suivantes à votre CMakeLists.txt
:
find_program(CCACHE_PROGRAM ccache)
if (CCACHE_PROGRAM)
message(NOTICE "ccache is enabled (found here: ${CCACHE_PROGRAM})")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "\"${CCACHE_PROGRAM}\"")
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "\"${CCACHE_PROGRAM}\"")
else ()
message(FATAL_ERROR "ccache has not been found")
endif ()
Voilà, c'est tout !
Mise à jour du 15 juillet 2024 : depuis la version 3.24 de CMake, RULE_LAUNCH_COMPILE
est désormais considérée comme interne et ne doit plus être utilisée. Même chose pour RULE_LAUNCH_LINK
. La documentation de CMake suggère d'utiliser désormais CMAKE_<LANG>_COMPILER_LAUNCHER
et CMAKE_<LANG>_LINKER_LAUNCHER
. Le wiki de ccache sur GitHub semble dire que CMAKE_<LANG>_COMPILER_LAUNCHER
est suffisante.
A la place du snippet précédent, il est donc préférable d'utiliser :
find_program(CCACHE_PROGRAM ccache)
if (CCACHE_PROGRAM)
message(STATUS "ccache is enabled (found here: ${CCACHE_PROGRAM})")
SET(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
SET(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
else ()
message(FATAL_ERROR "ccache has not been found")
endif ()
Aucun effet visible ?
Vous avez activé ccache
et vous ne voyez aucun effet. Ce n'est pas étonnant. Sur le site officiel, on peut lire :
The most important aspect of a compiler cache is to always produce exactly the same output that the real compiler would produce. [...] The only way you should be able to tell that you are using ccache is the speed.
Dans un usage normal, quand vous travaillez sur une branche particulière, vous ne verrez aucun effet. La différence est visible dans certaines situations, notamment quand vous changez de branche, quand on vous activez ou désactiver une option de compilation du projet, quand vous modifiez un header inclus dans de nombreux fichiers sources.
De plus, vous ne verrez pas de différence dans la liste de fichiers que CMake dit compiler ; vous verrez une différence sur le temps de build. En effet, CMake pense compiler la même chose, mais ccache
intercepte les appels au compilateur et décide s'il doit réellement appeler le compilateur, ou s'il peut récupérer un fichier objet dans son cache et le renvoyer directement.
A titre d'exemple, j'ai fait la manipulation suivante sur le projet sur lequel je bosse quotidiennement en mission :
- Je me place dans le dossier où se trouve mon projet et donc mon
CMakeLists.txt
. - Je nettoie le cache avec
ccache --clear
. - Je m'assure de partir d'un projet remis à zéro avec
cmake --build mon_dossier_de_build --target clean
. - Je builde mon projet avec
cmake --build --preset=mon_preset
(*). Lors de cette étape,ccache
va mémoriser tous les résultats de compilation. - Je cleane à nouveau mon projet.
- Je relance à nouveau une compilation. Cette fois,
ccache
peut utiliser son cache, créé 2 étapes plus haut, et ne recompiler aucun fichier.
Bilan : le premier build a duré 43 minutes ; le second a duré un peu moins de 4 minutes.
Splendide !
(*) = Si vous ne connaissez pas les CMake presets, je vous encourage à lire mon article sur le sujet, je suis sûr que vous allez aimer !
Conclusion
Je ne sais pas comment j'ai fait pour passer à côté de ccache
pendant 11 ans, dont presque 6 ans d'utilisation quotidienne de C++. Je ne sais pas, vraiment...
Une chose est sûre : ça fait désormais partie de mon CMakeLists.txt
template ! Comme je change souvent de branche ou que j'active puis désactive des options de debug, je vois souvent l'effet du cache. J'en suis au début de mon utilisation de ccache
et je vais prochainement m'intéresser à la pertinence de le mettre en place sur Jenkins.
Je gagne beaucoup de temps en local grâce à ccache
, et j'espère que vous en tirerez le même bénéfice !