C++
Toolchain on Windows: VS Code + MSYS2 UCRT64 + CMake
UCRT64 is the MSYS2 environment to pick for new work: it links against the
Universal C Runtime that ships with Windows 10 and later, rather than the legacy
msvcrt.dll used by the MINGW64 environment.
Install the toolchain
Install msys2-x86_64-*.exe and VSCodeUserSetup-x64-*.exe, then from the
MSYS2 UCRT64 shell:
pacman -Syu # update first; may ask to reopen the shell
pacman -S mingw-w64-ucrt-x86_64-toolchain
pacman -S mingw-w64-ucrt-x86_64-cmake
pacman -S mingw-w64-ucrt-x86_64-ninja
The toolchain group installs:
mingw-w64-ucrt-x86_64-binutilsmingw-w64-ucrt-x86_64-crt-gitmingw-w64-ucrt-x86_64-gccmingw-w64-ucrt-x86_64-gdbmingw-w64-ucrt-x86_64-gdb-multiarchmingw-w64-ucrt-x86_64-headers-gitmingw-w64-ucrt-x86_64-libmangle-gitmingw-w64-ucrt-x86_64-libwinpthread-gitmingw-w64-ucrt-x86_64-winpthreads-gitmingw-w64-ucrt-x86_64-makemingw-w64-ucrt-x86_64-pkgconfmingw-w64-ucrt-x86_64-tools-gitmingw-w64-ucrt-x86_64-winstorecompat-git
Install CMake and Ninja from the same mingw-w64-ucrt-x86_64- prefix. Mixing
an MSYS2 package with a separately installed Windows CMake or with another
environment's GCC is the usual root cause of generator and ABI errors.
Add the toolchain to PATH
# Opens the environment variables editor
rundll32 sysdm.cpl,EditEnvironmentVariables
Add C:\msys64\ucrt64\bin to PATH. Do not add C:\msys64\usr\bin — it
contains MSYS2's POSIX-emulation copies of find, sort and similar, which
shadow the Windows ones and break unrelated tooling.
Open a new terminal and verify:
gcc --version
cmake --version
where gcc
Configure VS Code
Install the extensions CMake Tools and C/C++ (both from Microsoft).
settings.json:
{
"cmake.cmakePath": "C:\\msys64\\ucrt64\\bin\\cmake.exe",
"cmake.generator": "Ninja"
}
Make MSYS2 the default terminal
settings.json:
{
"terminal.integrated.profiles.windows": {
"MSYS2 UCRT64": {
"path": [
"C:\\msys64\\usr\\bin\\bash.exe",
"${env:windir}\\System32\\cmd.exe"
],
"args": [
"--login",
"-i"
],
"env": {
"MSYSTEM": "UCRT64",
"CHERE_INVOKING": "1",
"MSYS2_PATH_TYPE": "inherit"
}
}
},
"terminal.integrated.defaultProfile.windows": "MSYS2 UCRT64"
}
MSYSTEM must match the environment you installed into. Setting it to
MINGW64 while the packages live under ucrt64 puts the wrong bin directory
first in PATH, and the resulting binaries mix runtimes. CHERE_INVOKING=1
keeps the shell in the current directory instead of jumping to $HOME.
First project
Press F1, type >, then cmake: and choose Quick Start. Pick the
GCC kit from the UCRT64 directory when prompted.
That produces a CMake project template. Press Debug — if the toolchain is
set up correctly, execution stops at the breakpoint on the first line of
main().
Ninja is used as a default generator
The error means CMake selected Ninja but cannot use it. Check, in order:
- Ninja is installed in the same environment
(
pacman -S mingw-w64-ucrt-x86_64-ninja). cmakeandgccboth resolve insideC:\msys64\ucrt64\bin(where cmake,where gcc). A Windows-installed CMake paired with an MSYS2 GCC is the most common mismatch.- The build directory does not carry a stale cache. Delete
build/— or run CMake: Delete Cache and Reconfigure — after changing generator or kit, becauseCMakeCache.txtpins the generator it was created with.
Falling back to "cmake.generator": "MinGW Makefiles" works, at the cost of
slower builds.
Compiler flags
RTTI (Run-Time Type Identification)
-frtti # enable (GCC and Clang default for C++)
-fno-rtti # disable
- RTTI gives a standard way to determine an object's type at run time, through
typeidanddynamic_cast. - The underlying representation is vendor-specific, so type information cannot be exchanged across compilers or across an ABI boundary.
- It only applies to polymorphic classes — a class with at least one virtual
function.
dynamic_caston a non-polymorphic type is a compile error. - Mixing translation units built with and without RTTI produces
undefined reference to typeinfoat link time. The flag has to be consistent across the whole binary, including static libraries. - Embedded projects often disable it to save flash.
-fno-exceptionsis usually turned off alongside for the same reason; both must then be applied to every object in the image.
Selected FPU does not support instructions
The floating-point ABI and the FPU model must match the target core, and must be identical for every object file and every prebuilt library that gets linked.
# Cortex-M4F and Cortex-M7 with single-precision FPU
-mfloat-abi=hard -mfpu=fpv4-sp-d16
# Cortex-M7 with double-precision FPU
-mfloat-abi=hard -mfpu=fpv5-d16
# Cortex-M33
-mfloat-abi=hard -mfpu=fpv5-sp-d16
-mfloat-abi selects how floating-point values are passed:
| Value | Meaning |
|---|---|
soft | Software floating point; no FPU instructions emitted. |
softfp | FPU instructions, but arguments passed in integer registers — link-compatible with soft libraries. |
hard | FPU instructions and arguments in FPU registers. Fastest, and incompatible with soft/softfp objects. |
Check the specific values against the reference manual for your part; the
combinations above are the common ones, not a complete list. On STM32 the FPU
also has to be enabled at run time (CMSIS does this in SystemInit when
__FPU_PRESENT is set), otherwise the first FPU instruction raises a UsageFault.