Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .devcontainer/cpp/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
#
# SPDX-License-Identifier: MPL-2.0

# Dedicated C++ development container for power-grid-model.
# Matches the Linux CI toolchain (Ubuntu 26.04, GCC/G++ 16, Clang 22) and the
# C++ dependencies listed in docs/advanced_documentation/build-guide.md.
# clang-tidy remains on version 18 on purpose: CI does the same (see PR #1631).
FROM mcr.microsoft.com/devcontainers/base:resolute

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
gcc-16 g++-16 \
clang-22 lld-22 libclang-rt-22-dev clang-tidy-18 \
cmake ninja-build gdb \
libboost-dev libeigen3-dev nlohmann-json3-dev libmsgpack-cxx-dev doctest-dev \
gcovr lcov pkg-config git curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*

# The CMake presets resolve `gcc`/`g++`/`clang`/`clang++` via PATH
# (same approach as CI, which symlinks the versioned binaries).
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-16 100 \
&& update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-16 100 \
&& update-alternatives --install /usr/bin/clang clang /usr/bin/clang-22 100 \
&& update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-22 100 \
&& update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-18 100

# uv provides the Python runtime for the repository's uv-managed tooling.
# pre-commit, REUSE and clang-format are installed from uv.lock by the
# postCreateCommand, so they stay in sync with CI instead of being duplicated
# as global tools here.
ENV UV_INSTALL_DIR=/usr/local/bin
RUN curl -LsSf https://astral.sh/uv/install.sh | sh

ENV CC=gcc CXX=g++
Comment thread
bferanmi806-sketch marked this conversation as resolved.
ENV DEBIAN_FRONTEND=dialog
60 changes: 60 additions & 0 deletions .devcontainer/cpp/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
// Dedicated C++ development container (issue #1272).
// The default Python container in .devcontainer/devcontainer.json is unchanged;
// VS Code offers both configurations when reopening in a container.
"name": "power-grid-model (C++)",
"build": {
"dockerfile": "Dockerfile",
"context": "."
},

"workspaceFolder": "/power-grid-model",
"workspaceMount": "source=${localWorkspaceFolder},target=/power-grid-model,type=bind",

// Keep CMake build trees inside the container. Sharing cpp_build/ with the
// host leaves absolute paths from the host in CMakeCache.txt, which makes
// presets such as clang-debug fail with "CMakeCache.txt directory ... is
// different than the directory ... where CMakeCache.txt was created".
// ${devcontainerId} keeps the volume per checkout, so parallel worktrees do
// not share a cache either.
"mounts": ["type=volume,source=power-grid-model-cpp-build-${devcontainerId},target=/power-grid-model/cpp_build"],

"remoteUser": "vscode",

"remoteEnv": {
"CC": "gcc",
"CXX": "g++"
},

"postCreateCommand": "sudo chown -R vscode:vscode cpp_build && uv sync --frozen --group lint --group test && uv run --frozen pre-commit install && gcc --version && g++ --version && clang --version && clang-tidy --version && uv run --frozen clang-format --version && cmake --version && ninja --version && cmake --list-presets",

"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cpptools",
"ms-vscode.cmake-tools",
"matepek.vscode-catch2-test-adapter",
"jeff-hykin.better-cpp-syntax",
"tamasfe.even-better-toml",
"xaver.clang-format"
],
"settings": {
// NOTE: there is no supported setting that pre-selects a CMake configure
// preset. After the container is created, run `CMake: Select Configure
// Preset` in VS Code and choose `gcc-debug` (`clang-debug` works too).
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
// Use the clang-format version pinned in uv.lock (the same one CI uses
// through `uv run --frozen clang-format`) instead of a system package.
"clang-format.executable": "/power-grid-model/.venv/bin/clang-format",
"clang-format.style": "file",
"[c]": {
"editor.defaultFormatter": "xaver.clang-format"
},
"[cpp]": {
"editor.defaultFormatter": "xaver.clang-format"
},
"editor.formatOnSave": true
}
}
}
}
3 changes: 3 additions & 0 deletions .devcontainer/cpp/devcontainer.json.license
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>

SPDX-License-Identifier: MPL-2.0
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
*.h text eol=lf
*.cpp text eol=lf
*.hpp text eol=lf
Dockerfile text eol=lf
2 changes: 2 additions & 0 deletions docs/advanced_documentation/build-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ In this repository there are three builds:
If you just want to develop the _Python_ side of `power-grid-model`, you can use the
[provided development container](./devcontainer-setup.md).
It automatically sets up the entire environment so you can start developing immediately.
For _C++_ development there is a separate [C++ development container](./devcontainer-setup.md#c-development-container)
with the required toolchain, dependencies and IDE debug support.
```

```{contents}
Expand Down
108 changes: 106 additions & 2 deletions docs/advanced_documentation/devcontainer-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ SPDX-License-Identifier: MPL-2.0

# Development Container Setup Guide

This document explains how you can use our development container (dev container) setup to contribute to the _Python_
side of `power-grid-model`.
This document explains how you can use our development container (dev container) setups to contribute to
`power-grid-model`.
There are two separately selectable configurations:

* The default container (`.devcontainer/devcontainer.json`) for the _Python_ side of `power-grid-model`.
* A dedicated container (`.devcontainer/cpp/devcontainer.json`) for _C++_ development (see
[C++ Development Container](#c-development-container)).

```{note}
A [development container](https://containers.dev/) is a pre-configured
Expand Down Expand Up @@ -124,3 +129,102 @@ Note that the _Professional_ edition is required to have native development cont
* Open the [welcome screen](https://www.jetbrains.com/help/pycharm/welcome-screen.html) of PyCharm.
* Click `Remote Development`, choose `Dev Containers` and click the `Create Dev Containers` button.
* Select your local path to the cloned repository and choose your container engine.

## C++ Development Container

The dedicated C++ container (`.devcontainer/cpp/devcontainer.json`) is based on Ubuntu 26.04 and mirrors the CI
toolchain: `gcc-16`/`g++-16`, `clang-22`, `clang-tidy-18` (kept at 18 in CI as well), `CMake` (>= 3.23), `Ninja`,
`gdb`, and the C++ dependencies from the [build guide](./build-guide.md) (`boost`, `eigen3`, `nlohmann-json`,
`msgpack-cxx`, `doctest`).
It also installs `uv`, which provides the `clang-format`, `pre-commit` and `reuse` versions pinned in `uv.lock`, so
the formatting and licence checks use the same versions as CI.
The Python container remains the default and is unchanged; combined Python/C++ and docs-building environments are
out of scope.

### Selecting the C++ container in VS Code

* Install the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
extension and open the cloned `power-grid-model` folder.
* Click the green `Remote Indicator` in the bottom-left corner or open the Command Palette
(`Ctrl+Shift+P` / `Cmd+Shift+P`), then select `Dev Containers: Reopen in Container`.
* When prompted, pick the `power-grid-model (C++)` configuration (`.devcontainer/cpp/devcontainer.json`).

### Configure and build (CMake preset)

No configure preset is pre-selected: `cmake.configurePreset` is not a setting the CMake Tools extension
provides, so the container cannot activate `gcc-debug` for you on first open. In VS Code, open the Command
Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`), run `CMake: Select Configure Preset`, and choose `gcc-debug`
(`clang-debug` remains available as the Clang alternative).
From a VS Code terminal inside the container, or any terminal with the environment set up:

```shell
cmake --preset gcc-debug
cmake --build --preset gcc-debug
```

Alternatively, use the CMake Tools extension preset picker, or the shortcut `./build.sh -p gcc-debug`.

Each preset records absolute paths in its CMake cache. The container therefore keeps `cpp_build/` in a Docker
volume instead of sharing it with the host, so a build tree created on the host can no longer collide with a build
tree created in the container (this was the cause of the `clang-debug` `CMakeCache.txt` path error). The volume
name includes the dev container ID, so separate checkouts and worktrees get their own build tree.
To start from a clean build tree, remove the volume and rebuild:

```shell
docker volume ls --filter name=power-grid-model-cpp-build
docker volume rm <volume-name-from-the-previous-command> # or: podman volume rm <volume-name>
```

### Verify the selected compiler

Both presets set their compiler explicitly, so switching presets is enough:

```shell
cmake --preset gcc-debug
grep -E '^CMAKE_(C|CXX)_COMPILER:' cpp_build/gcc-debug/CMakeCache.txt # /usr/bin/gcc, /usr/bin/g++
cmake --preset clang-debug
grep -E '^CMAKE_(C|CXX)_COMPILER:' cpp_build/clang-debug/CMakeCache.txt # /usr/bin/clang, /usr/bin/clang++
```

### Run the C++ tests

```shell
ctest --preset gcc-debug --output-on-failure
```

The `doctest` suites are also discoverable in the VS Code Test Explorer via the Catch2 test adapter.

### Format, check licences and run pre-commit

The container installs the `Clang-Format` VS Code extension (`xaver.clang-format`) and points it at
`/power-grid-model/.venv/bin/clang-format`, the version pinned in `uv.lock` (the same one CI uses), with
`-style=file`, so saving a C/C++ file applies the repository `.clang-format` configuration. `pre-commit` is
installed as a Git hook by the container setup. All hooks and tools run through the project environment, which the
`postCreateCommand` created with `uv sync --frozen --group lint --group test`:

```shell
uv run --frozen pre-commit run --all-files # all hooks
uv run --frozen pre-commit run clang-format --files path/to/file.cpp
uv run --frozen pre-commit run reuse --all-files # REUSE licence compliance
uv run --frozen clang-format -style=file -i path/to/file.cpp
uv run --frozen gersemi -i --warnings-as-errors cmake/ # CMake formatting
```

Run `uv run --frozen pre-commit run <hook-id> --all-files` to check a single hook across the repository. The
`reuse`, `ruff`, `mypy`, `clang-format`, `gersemi` and `pytest` hook IDs are defined in `.pre-commit-config.yaml`.
If the environment is missing (for example after a rebuild without the setup command), recreate it with
`uv sync --frozen --group lint --group test`.

### Debug a C++ test in the IDE

* Build with a Debug preset as above so binaries carry symbols.
* Set a breakpoint, then start the preconfigured `Debug validation test [Unix]` launch configuration
(`.vscode/launch.json`, `gdb`-based), or right-click an individual test in the Test Explorer and debug it.
* The launch configuration resolves the test binary via `${command:cmake.buildDirectory}`
(e.g. `cpp_build/gcc-debug/bin/power_grid_model_validation_tests`).
Comment thread
bferanmi806-sketch marked this conversation as resolved.

### Returning to the Python container

Reopen the folder in the default container (`Dev Containers: Reopen in Container`, then pick the
`power-grid-model` configuration), or `Dev Containers: Reopen Folder Locally` first and then reopen in the
Python container.