🔝 Retour au Sommaire
Chapitre 2 · Section 2.4 · Configuration de l'IDE · Niveau Débutant
Vous venez de passer les sections précédentes à installer GCC 15, Clang 20, ccache, Ninja, CMake, clangd et à configurer votre IDE. Imaginez maintenant qu'un nouveau collègue rejoint l'équipe. Doit-il refaire tout ce processus manuellement, en espérant obtenir exactement la même configuration ? Ou que vous changez de machine — devez-vous tout reconfigurer de zéro ?
Les DevContainers (Development Containers) résolvent ce problème : ils encapsulent l'intégralité de la toolchain — compilateur, build system, debugger, serveur de langage, extensions IDE, configuration — dans un conteneur Docker décrit par un fichier de configuration versionné avec le projet. Le résultat : un git clone suivi d'un « Reopen in Container » dans VS Code, et l'environnement complet est prêt en quelques minutes, identique pour chaque développeur.
Sur un projet C++ professionnel, les divergences entre postes de développement créent des problèmes concrets :
- Un développeur utilise GCC 14, un autre GCC 15 — un warning
-Werrorfait échouer le build chez l'un mais pas chez l'autre. - La version de CMake diffère — un
CMakeLists.txtutilisant une fonctionnalité récente échoue sur la machine la plus ancienne. - La version de clangd ne correspond pas au compilateur — les diagnostics dans l'IDE divergent des erreurs de compilation réelles.
- Une dépendance système (
libssl-dev,libfmt-dev) est installée dans une version différente. - Le cache ccache contient des artefacts d'une ancienne version du compilateur.
Ces divergences sont insidieuses : elles ne se manifestent pas immédiatement et créent des bugs de build intermittents qui consomment du temps d'investigation.
Un DevContainer est un conteneur Docker qui contient toute la toolchain de développement. Votre code source est monté dans le conteneur (pas copié — les modifications sont immédiates et bidirectionnelles), et VS Code se connecte au conteneur pour y exécuter l'analyse de code, la compilation, le débogage et les extensions. Du point de vue du développeur, l'expérience est identique à un développement local — la seule différence est que tout s'exécute dans un environnement contrôlé et reproductible.
Les DevContainers reposent sur Docker. Installez Docker Engine sur Ubuntu :
# Installation de Docker via le dépôt officiel
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io Ajoutez votre utilisateur au groupe docker pour éviter d'utiliser sudo à chaque commande Docker :
sudo usermod -aG docker $USER
newgrp docker # Prend effet immédiatement (ou déconnectez/reconnectez) Vérification :
docker run --rm hello-worldInstallez l'extension Dev Containers :
code --install-extension ms-vscode-remote.remote-containersCette extension ajoute les commandes « Reopen in Container », « Rebuild Container » et « Add Dev Container Configuration Files » à la palette de commandes de VS Code.
La configuration d'un DevContainer se compose de deux éléments, placés dans un répertoire .devcontainer/ à la racine du projet :
mon-projet/
├── .devcontainer/
│ ├── devcontainer.json ← Configuration principale
│ └── Dockerfile ← Image Docker (optionnel)
├── CMakeLists.txt
├── src/
└── ...
C'est le fichier central. Il décrit l'image Docker à utiliser, les extensions VS Code à installer, les paramètres de l'éditeur, les ports à exposer et les commandes de post-création :
// .devcontainer/devcontainer.json
{
"name": "C++23 Development Environment",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
// Extensions VS Code installées automatiquement dans le conteneur
"customizations": {
"vscode": {
"extensions": [
"llvm-vs-code-extensions.vscode-clangd",
"ms-vscode.cmake-tools",
"ms-vscode.cpptools",
"twxs.cmake",
"usernamehw.errorlens"
],
"settings": {
"clangd.path": "/usr/bin/clangd-20",
"clangd.arguments": [
"--background-index",
"--clang-tidy",
"--header-insertion=iwyu",
"--completion-style=detailed"
],
"C_Cpp.intelliSenseEngine": "disabled",
"cmake.generator": "Ninja",
"cmake.configureOnOpen": true,
"cmake.configureSettings": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_CXX_COMPILER_LAUNCHER": "ccache",
"CMAKE_C_COMPILER_LAUNCHER": "ccache"
},
"cmake.copyCompileCommands": "${containerWorkspaceFolder}/compile_commands.json",
"editor.formatOnSave": true,
"editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd"
}
}
},
// Commande exécutée après la création du conteneur
"postCreateCommand": "cmake -B build -G Ninja && echo 'Environnement prêt.'",
// Monter le cache ccache de l'hôte pour persistance
"mounts": [
"source=${localEnv:HOME}/.cache/ccache,target=/home/vscode/.cache/ccache,type=bind,consistency=cached"
],
// Utilisateur non-root dans le conteneur
"remoteUser": "vscode",
// Capacités Linux nécessaires pour le débogage
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt", "seccomp=unconfined"
]
}Examinons les éléments clés.
build.dockerfile — pointe vers le Dockerfile qui construit l'image. Si vous préférez utiliser une image pré-construite, remplacez build par "image": "mcr.microsoft.com/devcontainers/cpp:ubuntu" (image Microsoft officielle pour C++).
customizations.vscode.extensions — les extensions installées automatiquement dans le conteneur. Chaque développeur obtient exactement les mêmes extensions, configurées de la même manière. Ces extensions s'exécutent dans le conteneur, pas sur la machine hôte — elles ont donc accès aux outils du conteneur (clangd, gdb, etc.).
customizations.vscode.settings — les paramètres VS Code appliqués dans le conteneur. Ils remplacent les paramètres du workspace (.vscode/settings.json) pour tout ce qui est spécifique au conteneur.
postCreateCommand — une commande exécutée une seule fois après la création du conteneur. Ici, on lance la configuration CMake pour que clangd ait immédiatement accès au compile_commands.json. Vous pouvez aussi installer des dépendances supplémentaires, générer des fichiers, etc.
mounts — monte des répertoires de l'hôte dans le conteneur. Ici, on partage le cache ccache de la machine hôte avec le conteneur, de sorte que les compilations précédentes (même hors conteneur) bénéficient au conteneur et vice versa.
remoteUser — l'utilisateur sous lequel VS Code s'exécute dans le conteneur. vscode est un utilisateur non-root créé par les images de base Microsoft. Travailler en non-root est une bonne pratique de sécurité et évite les problèmes de permissions sur les fichiers montés.
runArgs — arguments passés à docker run. --cap-add=SYS_PTRACE est nécessaire pour que GDB puisse attacher et contrôler les processus (le syscall ptrace est bloqué par défaut dans les conteneurs Docker). --security-opt seccomp=unconfined relâche le profil seccomp pour permettre le débogage et les sanitizers.
Le Dockerfile définit l'image Docker qui contient votre toolchain. Voici un Dockerfile complet pour un environnement C++23 sur Ubuntu :
# .devcontainer/Dockerfile
# ============================================================================
# Image de développement C++23 — Ubuntu 24.04
# ============================================================================
FROM ubuntu:24.04
# Éviter les prompts interactifs pendant l'installation
ENV DEBIAN_FRONTEND=noninteractive
# --- Outils de base ---
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
gdb \
cmake \
ninja-build \
ccache \
git \
curl \
wget \
ca-certificates \
pkg-config \
python3 \
python3-pip \
gnupg \
lsb-release \
sudo \
locales \
&& rm -rf /var/lib/apt/lists/*
# --- Locale UTF-8 ---
RUN locale-gen en_US.UTF-8
ENV LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
# --- GCC 15 ---
RUN apt-get update && apt-get install -y --no-install-recommends \
software-properties-common \
&& add-apt-repository ppa:ubuntu-toolchain-r/test -y \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
gcc-15 g++-15 \
&& update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-15 100 \
&& update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-15 100 \
&& rm -rf /var/lib/apt/lists/*
# --- Clang 20 + clangd + clang-tidy + clang-format ---
RUN wget -qO- https://apt.llvm.org/llvm.sh | bash -s -- 20 all \
&& update-alternatives --install /usr/bin/clang clang /usr/bin/clang-20 100 \
&& update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-20 100 \
&& update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-20 100 \
&& update-alternatives --install /usr/bin/clang-format clang-format \
/usr/bin/clang-format-20 100 \
&& update-alternatives --install /usr/bin/clang-tidy clang-tidy \
/usr/bin/clang-tidy-20 100 \
&& rm -rf /var/lib/apt/lists/*
# --- CMake récent (si la version Ubuntu est trop ancienne) ---
# Décommentez si nécessaire :
# RUN pip3 install --break-system-packages cmake --upgrade
# --- Dépendances projet (adapter selon votre projet) ---
# RUN apt-get update && apt-get install -y --no-install-recommends \
# libfmt-dev \
# libssl-dev \
# nlohmann-json3-dev \
# && rm -rf /var/lib/apt/lists/*
# --- Utilisateur non-root ---
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID
RUN groupadd --gid $USER_GID $USERNAME \
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
&& echo "$USERNAME ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/$USERNAME \
&& chmod 0440 /etc/sudoers.d/$USERNAME
# --- Configuration ccache pour l'utilisateur ---
RUN mkdir -p /home/$USERNAME/.cache/ccache \
&& chown -R $USERNAME:$USERNAME /home/$USERNAME/.cache
USER $USERNAME
# --- ccache dans le PATH ---
ENV PATH="/usr/lib/ccache:${PATH}"
# Vérifications
RUN echo "=== Toolchain ===" \
&& g++ --version | head -1 \
&& clang++ --version | head -1 \
&& cmake --version | head -1 \
&& ninja --version \
&& gdb --version | head -1 \
&& clangd --version | head -1 \
&& ccache --version | head -1Ce Dockerfile installe une toolchain complète et vérifiée. Les commentaires indiquent les sections à adapter selon les besoins spécifiques de votre projet (dépendances système, version de CMake, etc.).
La stratégie de gestion des couches Docker est délibérée : les outils qui changent rarement (paquets de base, compilateurs) sont installés dans les premières couches, tandis que les dépendances spécifiques au projet viennent en dernier. Cela maximise la réutilisation du cache Docker lors des reconstructions.
- Clonez le projet :
git clone https://github.com/org/projet.git - Ouvrez le dossier dans VS Code :
code projet/ - VS Code détecte le répertoire
.devcontainer/et affiche une notification : « Folder contains a Dev Container configuration file. Reopen folder to develop in a container. » - Cliquez sur Reopen in Container (ou
Ctrl+Shift+P→ « Dev Containers: Reopen in Container »). - VS Code construit l'image Docker (première fois uniquement — les fois suivantes, l'image est en cache), démarre le conteneur, installe les extensions et exécute le
postCreateCommand. - En quelques minutes, l'environnement complet est prêt : clangd indexe le projet, CMake est configuré, le debugger est opérationnel.
Les ouvertures suivantes sont beaucoup plus rapides : l'image Docker est en cache, le conteneur est déjà créé (il est simplement redémarré), et l'index clangd est persisté. Le temps de démarrage est de l'ordre de quelques secondes.
Quand vous modifiez le Dockerfile ou le devcontainer.json (par exemple pour ajouter une dépendance ou mettre à jour le compilateur), utilisez la commande Rebuild Container :
Ctrl+Shift+P → Dev Containers: Rebuild Container
VS Code reconstruit l'image et recrée le conteneur. Le code source n'est pas affecté (il est monté, pas copié).
Si vous ne souhaitez pas maintenir un Dockerfile personnalisé, Microsoft fournit des images de base préconfigurées pour le C++ :
// .devcontainer/devcontainer.json — Version simplifiée
{
"name": "C++ (Ubuntu)",
"image": "mcr.microsoft.com/devcontainers/cpp:ubuntu-24.04",
"customizations": {
"vscode": {
"extensions": [
"llvm-vs-code-extensions.vscode-clangd",
"ms-vscode.cmake-tools",
"ms-vscode.cpptools",
"twxs.cmake"
]
}
},
"postCreateCommand": "sudo apt-get update && sudo apt-get install -y ccache ninja-build",
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt", "seccomp=unconfined"
]
}Cette approche est plus simple (pas de Dockerfile à maintenir) mais moins flexible : vous obtenez les versions du compilateur fournies par l'image de base, qui ne sont pas nécessairement les plus récentes. Pour un contrôle fin de la toolchain, le Dockerfile personnalisé reste préférable.
Les DevContainers supportent un système de Features — des modules d'installation réutilisables que vous pouvez combiner dans votre devcontainer.json sans écrire de Dockerfile :
{
"image": "ubuntu:24.04",
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {
"installZsh": true,
"username": "vscode"
},
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"customizations": {
"vscode": {
"extensions": [
"llvm-vs-code-extensions.vscode-clangd",
"ms-vscode.cmake-tools",
"ms-vscode.cpptools"
]
}
},
"postCreateCommand": "sudo apt-get update && sudo apt-get install -y build-essential cmake ninja-build gdb ccache"
}Les Features sont publiées sur le registre GitHub Container (ghcr.io) et maintenues par la communauté. Elles gèrent les cas complexes (permissions, PPA, détection de version) de manière transparente. Consultez le catalogue officiel pour découvrir les Features disponibles.
Le code source est monté dans le conteneur via un bind mount. Les modifications sont bidirectionnelles et immédiates : si vous modifiez un fichier dans VS Code (qui s'exécute dans le conteneur), le fichier sur votre machine hôte est modifié aussi, et vice versa. Le code n'est jamais copié dans le conteneur.
Pour que ccache soit efficace entre les sessions (et entre le développement local et le conteneur), montez le cache de l'hôte dans le conteneur :
"mounts": [
"source=${localEnv:HOME}/.cache/ccache,target=/home/vscode/.cache/ccache,type=bind,consistency=cached"
]Avec ce montage, les compilations effectuées dans le conteneur alimentent le cache local de l'hôte, et inversement. La première compilation dans un nouveau conteneur bénéficie immédiatement du cache existant.
Par défaut, le répertoire de build (build/) est dans l'arborescence du projet — qui est montée depuis l'hôte. Cela fonctionne, mais les performances d'I/O peuvent être affectées par la couche de montage, surtout sur macOS (moins sur Linux).
Pour de meilleures performances de build sur macOS, vous pouvez placer le répertoire de build dans un volume Docker (stockage natif du conteneur, pas de couche de montage) :
"mounts": [
"source=project-build,target=${containerWorkspaceFolder}/build,type=volume"
]Sur Linux, la différence de performance est négligeable grâce aux bind mounts natifs.
Les extensions installées dans le conteneur et leurs données sont stockées dans un volume Docker automatiquement géré par VS Code. Elles survivent aux redémarrages du conteneur mais sont perdues lors d'un « Rebuild Container » (elles sont réinstallées depuis devcontainer.json).
Un avantage souvent sous-estimé des DevContainers est la convergence entre l'environnement de développement et l'environnement de CI. Le même Dockerfile (ou une version très proche) peut servir à construire l'image de développement locale et l'image de build dans votre pipeline CI :
# .github/workflows/build.yml (extrait)
jobs:
build:
runs-on: ubuntu-latest
container:
image: ghcr.io/org/projet-devcontainer:latest
options: --cap-add=SYS_PTRACE
steps:
- uses: actions/checkout@v4
- run: cmake -B build -G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- run: cmake --build build
- run: ctest --test-dir build --output-on-failureL'image peut être construite et publiée sur un registre (GitHub Container Registry, Docker Hub) par un job CI dédié, puis réutilisée à la fois par les développeurs (via devcontainer.json) et par les jobs de build CI.
Cette convergence élimine la classe de bugs « ça marche en local mais pas en CI » car les deux environnements sont strictement identiques.
Le répertoire .devcontainer/ doit être versionné dans Git. C'est la clé de la reproductibilité : chaque commit du projet référence une version précise de l'environnement de développement. Quand vous revenez à un ancien commit, l'environnement de développement correspondant est recréé automatiquement.
Dans le Dockerfile, spécifiez des versions exactes pour les outils critiques. Évitez les apt-get install gcc sans version, qui installera la version par défaut de la distribution — qui peut changer au fil des mises à jour de l'image de base.
# ✅ Bon : version explicite
RUN apt-get install -y gcc-15 g++-15
# ❌ À éviter : version implicite
RUN apt-get install -y gcc g++Chaque couche Docker ajoute de la taille à l'image. Quelques pratiques pour la maintenir raisonnable :
- Chaînez les commandes
apt-getet terminez parrm -rf /var/lib/apt/lists/*pour supprimer le cache apt. - Utilisez
--no-install-recommendspour éviter les paquets suggérés non nécessaires. - N'installez que ce qui est nécessaire au développement — pas les outils de production, de monitoring ou de déploiement.
Ajoutez un commentaire ou un fichier README.md dans .devcontainer/ pour expliquer les choix de toolchain et les éventuelles commandes post-installation :
<!-- .devcontainer/README.md -->
# Environnement de développement
Ce DevContainer fournit :
- GCC 15 et Clang 20 (C++23 complet)
- CMake 3.31 + Ninja
- clangd 20 + clang-tidy + clang-format
- GDB 15 + ccache
- Dépendances projet : libfmt, nlohmann-json, OpenSSL
## Première utilisation
1. Ouvrez le projet dans VS Code
2. Acceptez "Reopen in Container"
3. Attendez la fin du build (~5 min la première fois)
4. Le projet est prêt : CMake est configuré, clangd indexeCLion supporte le développement dans des conteneurs Docker via File > Settings > Build, Execution, Deployment > Toolchains > + → « Docker ». CLion crée une toolchain distante qui compile et débogue dans le conteneur. L'approche est différente de celle de VS Code (CLion reste sur l'hôte et communique avec le conteneur via SSH ou Docker exec), mais le résultat est similaire : la compilation et le débogage utilisent les outils du conteneur.
Neovim fonctionne nativement dans un terminal — y compris dans un conteneur Docker. L'approche la plus simple est d'installer Neovim et sa configuration dans le Dockerfile, puis de se connecter au conteneur :
docker exec -it mon-conteneur nvimAlternativement, lancez Neovim sur l'hôte et connectez-le au clangd du conteneur via un tunnel LSP — une configuration plus avancée que nous ne détaillerons pas ici.
GitHub Codespaces est un service cloud qui exécute des DevContainers dans des machines virtuelles hébergées par GitHub. Votre devcontainer.json et votre Dockerfile sont réutilisés tels quels — Codespaces les lit et construit un environnement identique à celui de votre machine locale, mais accessible depuis un navigateur ou VS Code connecté à distance. C'est utile pour l'onboarding instantané (un nouveau contributeur ouvre un Codespace et travaille en quelques minutes) et pour les machines peu puissantes (la compilation se fait dans le cloud).
- Les DevContainers encapsulent toute la toolchain (compilateur, build system, debugger, extensions IDE, configuration) dans un conteneur Docker décrit par des fichiers versionnés avec le projet.
devcontainer.jsonest le fichier central : il spécifie l'image Docker, les extensions, les paramètres, les montages et les commandes post-création.- Le Dockerfile décrit l'image de la toolchain. Versionnez-le et pinnez les versions des outils critiques.
--cap-add=SYS_PTRACEest nécessaire dansrunArgspour que GDB fonctionne dans le conteneur.- Montez le cache ccache de l'hôte dans le conteneur pour une persistance du cache entre sessions et entre le développement local et conteneurisé.
- Versionnez
.devcontainer/dans Git. C'est la clé de la reproductibilité : chaque développeur obtient exactement le même environnement. - La convergence dev/CI est un bénéfice majeur : le même Dockerfile peut servir en développement local et dans le pipeline CI, éliminant les divergences d'environnement.
- Pour un démarrage rapide sans Dockerfile personnalisé, utilisez une image de base Microsoft (
mcr.microsoft.com/devcontainers/cpp:ubuntu-24.04) et personnalisez viapostCreateCommandetfeatures.
⏭️ IA-assisted tooling : Copilot, Clangd AI et completion intelligente (2026)