Recompiler vos projets C et C++ plus rapidement avec ccache
Tags: cpp, todayilearned, programming
Published: 2022-03-10 07:39:11

[`ccache`](https://ccache.dev/) 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 pourquoi `ccache` 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 :

```bash
make
make clean
make
```

Deux scénarios sont possibles lors de l'exécution de la dernière commande :

1. Vous n'avez pas `ccache`, et `make` recompile _tous_ les fichiers.
2. 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](https://dev.to/younup/cmake-on-stm32-episode-4-additional-clean-files-3f8j). 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](https://ccache.dev/download.html) `ccache`, vous l'installez et vous l'ajouter au _PATH_.

Vous ajoutez les lignes suivantes à votre `CMakeLists.txt` :

```cmake

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 !

## 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 :

1. Je me place dans le dossier où se trouve mon projet et donc mon `CMakeLists.txt`.
2. Je nettoie le cache avec `ccache --clear`.
3. Je m'assure de partir d'un projet remis à zéro avec `cmake --build mon_dossier_de_build --target clean`.
4. Je _builde_ mon projet avec `cmake --build --preset=mon_preset` (*). Lors de cette étape, `ccache` va mémoriser tous les résultats de compilation.
5. Je _cleane_ à nouveau mon projet.
6. 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](https://www.younup.fr/blog/cmake-presets-partager-configurations-projets), 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 !