Skip to content

Installing Compiler

To develop in C++, a strictly compliant toolchain is required. The following details the Installation of the LLVM/Clang and GCC toolchains.

The reference environment for this course is Clang 16+ and CMake 3.25+.

  • Clang/LLVM: A compiler front-end built on the LLVM infrastructure. It is preferred for this course due to its modular architecture, superior static analysis, and meaningful error messages.
  • MSYS2: A software distribution and building platform for Windows. It provides a Unix-like environment to manage native Windows software.
  • UCRT64 (Universal C Runtime): The modern Windows C runtime library. Unlike legacy MinGW (which linked against msvcrt.dll), UCRT links ucrtbase.dll and ensures strict standard compliance, proper UTF-8 locale support, and binary compatibility with modern Windows system libraries.
  • Ninja: A small build system with a focus on speed, designed to replace Make.
  • Target Triple: A string of the form <arch>-<vendor>-<os>-<env> that uniquely identifies a compilation target (e.g., x86_64-pc-linux-gnu``aarch64-apple-darwin22). The compiler driver uses this to select the correct code generator and default library paths [N4950 §6.7.1].

Select your operating system to view specific installation instructions.

On Windows, we utilize MSYS2 to provide a native Clang toolchain that links against the Universal C Runtime (UCRT). This avoids the legacy msvcrt.dll issues and provides a command-line Experience consistent with Linux and macOS.

  1. Download the installer (msys2-x86_64-*.exe) from the official MSYS2 website.
  2. Run the installer. Use the default installation folder (C:\msys64).
  3. When complete, ensure the box “Run MSYS2 UCRT64 now” is checked.
  • Crucial: Do not use the “MSYS” or “MINGW64” terminals. You must specifically use the UCRT64 environment to ensure the correct runtime linking.

In the terminal, execute the following to update the system packages:

Terminal window
pacman -Syu

Note: If the terminal asks to close/restart, allow it, then reopen “MSYS2 UCRT64” from the Start Menu and run the command again to finish updates.

Install the Clang compiler, CMake, Ninja, and the LLVM debugging tools (LLDB). We explicitly select The ucrt-x86_64 variants.

Terminal window
pacman -S mingw-w64-ucrt-x86_64-clang \
mingw-w64-ucrt-x86_64-lld \
mingw-w64-ucrt-x86_64-lldb \
mingw-w64-ucrt-x86_64-cmake \
mingw-w64-ucrt-x86_64-ninja \
mingw-w64-ucrt-x86_64-make

To access these tools from PowerShell, VS Code, or standard Command Prompt, add the binary directory To your Windows PATH.

  1. Press Win + RType sysdm.cplAnd press Enter.

  2. Go to the Advanced tab and click Environment Variables.

  3. Under System variables (bottom pane), locate Path and click Edit.

  4. Click New and add the following entry:

    Terminal window
    C:\msys64\ucrt64\bin
  5. Click OK on all dialogs.

Warning: Adding directory to PATH may not be the best practice. Running commands from the msys2 ucrt64 terminal can be a better choice if multiple toolchains are installed to prevent Conflicts.

Open a new PowerShell window and verify the compiler resolves to the UCRT version:

Terminal window
clang++ --version

Target Output: Target: x86_64-w64-windows-gnu (The version should be 16.0 or higher).

Before proceeding to Module 1.2, verify that the environment can compile and link a C++23 program.

Create a file named test.cpp:

#include <iostream>
#include <vector>
#include <numeric>
int main() {
// Test C++20/23 feature: Designated Initializers and Ranges
struct Config { int id; float value; };
Config cfg{ .id = 1, .value = 3.14f };
std::vector<int> data = {1, 2, 3, 4, 5};
// Verify Output
if (cfg.value > 3.0f) {
std::cout << "Environment Verified. Standard: " << __cplusplus << "\n";
return 0;
}
return 1;
}

Run the following commands in your terminal:

Terminal window
clang++ -std=c++23 -O3 test.cpp -o infra_test
./infra_test

:::caution If you are using MSVC, replace clang++ with cl.exe and ensure you have the latest Visual Studio 2022 installed. :::

Success Criteria:

  1. No compilation errors or warnings.
  2. Output contains “Environment Verified”.
  3. The output standard version is 202302 (or similar, depending on exact compiler patch level).

Both GCC and Clang follow a major.minor.patch versioning scheme. ABI compatibility is guaranteed Within the same major version for GCC, and across Clang versions when using the same libc++ ABI Version.

CompilerVersionC++23 SupportNotes
GCC13.xMost featuresSome C++23 features still experimental
GCC14.xFull supportRecommended for production
Clang17.xMost featuresRequires -std=c++23 flag
Clang18.xFull supportRecommended for production
MSVC19.38+Most featuresC++23 support varies by feature

The __cplusplus predefined macro indicates which C++ standard the compiler is targeting [N4950 §6.10.9]:

Flag__cplusplus Value
-std=c++17201703L
-std=c++20202002L
-std=c++23202302L

Important: MSVC does not correctly set __cplusplus by default. It always reports 199711L Unless you pass /Zc:__cplusplus. This is a long-standing bug acknowledged by Microsoft.

#include <iostream>
int main() {
#if defined(__clang__)
std::cout << "Clang " << __clang_major__ << "." << __clang_minor__ << "\n";
#elif defined(__GNUC__)
std::cout << "GCC " << __GNUC__ << "." << __GNUC_MINOR__ << "\n";
#elif defined(_MSC_VER)
std::cout << "MSVC " << _MSC_VER << "\n";
#endif
std::cout << "C++ standard: " << __cplusplus << "\n";
#if __has_include(<ranges>)
std::cout << "Ranges header available\n";
#endif
#if __has_include(<concepts>)
std::cout << "Concepts header available\n";
#endif
return 0;
}

The three major C++ compilers differ significantly in diagnostics, optimization capabilities, and Standard library integration. The following matrix compares them across dimensions relevant to Production systems engineering.

FeatureClang/LLVMGCCMSVC
LicenseApache 2.0 / UIUCGPL v3Proprietary (VS license)
PlatformsLinux, macOS, Windows, FreeBSD, WebAssemblyLinux, Windows (MinGW), FreeBSD, embeddedWindows only
Default stdliblibc++ (macOS), libstdc++ (Linux)libstdc++MSVC STL
Diagnostics qualityExcellent (columnar, fix-its, notes)Good (improving)Good (improving, C++20+)
Static analysisClang-Tidy, Clang Static Analyzer-fanalyzer (GCC 10+)/analyze (Code Analysis)
Sanitizer supportASan, UBSan, MSan, TSan, libFuzzerASan, UBSan, TSanASan (partial)
LTOFull LTO + ThinLTOFull LTO + LTO (improving)LTCG (Link-Time Code Generation)
ModulesC++20 modules (Clang 16+)C++20 modules (GCC 12+)Partial C++20 modules
AST dump / tooling-Xclang -ast-dumpLibclang, libToolingNo equivalent AST dumpNo equivalent
Cross-compilation-target flag, flexibleRequires separate cross-toolchain packagesRequires Windows SDK + cross-comp setup
Debug infoDWARF 5, CodeView (Windows)DWARF 5PDB (Program Database)
Incremental compilationNo (requires full recompile)NoEdit and Continue (with link.exe)

The three compilers employ different optimization strategies. GCC’s -O2 and -O3 tend to produce Marginally faster binaries for numerical workloads due to aggressive loop vectorization with Graphite. Clang excels at build-time performance (faster compilation at -O2) and produces Competitive binaries via its Polly loop optimizer. MSVC’s LTCG is competitive but only within the Windows ecosystem.

For production C++23 code, the recommendation is:

  • Linux: Clang 18+ with libc++ for best diagnostics, or GCC 14+ for maximum binary performance.
  • macOS: Upstream Clang via Homebrew (Apple Clang lags behind on C++23).
  • Windows: MSVC for native integration, or Clang with MSVC ABI (clang-cl) for cross-platform parity.

Compiler Flag Equivalents Across Compilers

Section titled “Compiler Flag Equivalents Across Compilers”

One of the challenges of cross-platform C++ development is that equivalent functionality is often Controlled by different flags. The following table maps common flags across the three compilers.

PurposeClang / GCCMSVC
C++17 strict ISO-std=c++17/std:c++17
C++20 strict ISO-std=c++20/std:c++20
C++23 strict ISO-std=c++23/std:c++latest
GNU extensions-std=gnu++23N/A (MSVC has no GNU mode)
No extensions-std=c++23 (implicit)/permissive-
PurposeClang / GCCMSVC
All common warnings-Wall/W4
Extra warnings-Wextra/w14640 (enables more)
Pedantic (ISO strictness)-Wpedantic/permissive-
Warnings as errors-Werror/WX
Disable specific warning-Wno-<warning-name>/wd<warning-number>
Treat unknown warning err-Werror=unknown-warning-optionN/A
PurposeClang / GCCMSVC
No optimization-O0/Od
Balanced optimization-O2/O2
Aggressive optimization-O3/Ox
Size optimization-Os/O1
Fast math-ffast-math/fp:fast
Debug info-g/Zi
Define macro-DFOO=bar/DFOO=bar
Include path-I/path/I/path
PurposeClang
Use libstdc++ (default)-stdlib=libstdc++
Use libc++-stdlib=libc++
PurposeClang / GCCMSVC
Address sanitizer-fsanitize=address/fsanitize=address (partial)
Undefined behavior-fsanitize=undefinedN/A
Thread sanitizer-fsanitize=threadN/A
Memory sanitizer-fsanitize=memory (Clang only)N/A

Using update-alternatives on Debian/Ubuntu

Section titled “Using update-alternatives on Debian/Ubuntu”

When multiple compiler versions are installed, you can manage the default compiler:

Terminal window
# Install multiple versions
sudo apt install clang-17 clang-18
# Configure alternatives
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-17 100
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-18 200
# Switch between versions interactively
sudo update-alternatives --config clang++

Multi-Version Coexistence Without Alternatives

Section titled “Multi-Version Coexistence Without Alternatives”

In professional environments, it is common to maintain multiple compiler versions without changing The system-wide default. CMake makes this straightforward:

Terminal window
# GCC 14 build
cmake -S . -B build-gcc14 \
-DCMAKE_C_COMPILER=gcc-14 \
-DCMAKE_CXX_COMPILER=g++-14
# Clang 18 build (parallel directory)
cmake -S . -B build-clang18 \
-DCMAKE_C_COMPILER=clang-18 \
-DCMAKE_CXX_COMPILER=clang++-18
# Both build directories coexist; switch by choosing which one to build
cmake --build build-gcc14
cmake --build build-clang18

This pattern is essential for ABI validation: compiling the same code with two different compilers And verifying that both produce correct output catches compiler-specific bugs and non-portable Constructs.

CMake detects compilers automatically. You can override with:

Terminal window
# Specify compiler explicitly
cmake -S . -B build \
-DCMAKE_C_COMPILER=clang-18 \
-DCMAKE_CXX_COMPILER=clang++-18

For projects that must build with multiple compilers (e.g., open-source libraries that support both GCC and Clang), use CMake’s compiler-ID detection to apply conditional flags:

cmake_minimum_required(VERSION 3.25)
project(CrossCompiler CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_compile_options(-fcolor-diagnostics)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 18)
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
add_link_options(-fsanitize=address)
endif()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(-fdiagnostics-color=always)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13)
add_compile_options(-fanalyzer)
endif()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_options(/W4 /permissive- /Zc:__cplusplus)
endif()

ABI Compatibility Implications of Compiler Choice

Section titled “ABI Compatibility Implications of Compiler Choice”

The choice of compiler has direct implications for binary compatibility. Key considerations:

GCC and Clang on Linux share the Itanium C++ ABI [Itanium ABI], which defines name mangling, Vtable layout, exception handling, and class layout. This means an object file compiled with GCC can be linked with one compiled by Clang, provided both use the same standard library Implementation and version.

MSVC uses a completely different ABI (the Microsoft ABI), which is incompatible at the binary Level with the Itanium ABI. You cannot link a .obj produced by MSVC with a .o produced by GCC.

When Clang is used on Linux, it defaults to GCC’s libstdc++. Switching to libc++ changes the Standard library ABI. The two libraries implement std::string``std::vectorAnd other types with Different memory layouts:

#include <iostream>
#include <string>
int main() {
std::cout << "sizeof(std::string) = " << sizeof(std::string) << "\n";
// libstdc++ (GCC/Clang default on Linux, 64-bit): 32
// libc++ (Clang -stdlib=libc++, 64-bit): 24
// MSVC STL (64-bit): 32
return 0;
}

Mixing object files compiled with different -stdlib= values in the same binary produces undefined Behavior because the two libraries disagree on the layout of every standard type.

Compiler PairSame OS?ABI Compatible?Condition
GCC 13 \leftrightarrow GCC 14YesYesSame -stdlibSame -D_GLIBCXX_USE_CXX11_ABI
Clang 17 \leftrightarrow Clang 18YesYesSame -stdlibSame ABI version
Clang 18 \leftrightarrow GCC 14LinuxYesBoth use libstdc++Same ABI flags
Clang 18 (libc++) \leftrightarrow GCC 14 (libstdc++)LinuxNoDifferent standard library ABIs
MSVC 2022 \leftrightarrow MinGW ClangWindowsNoDifferent C++ ABI (MSVC vs Itanium)

See Language Standard and ABI Compatibility for a Deeper treatment of ABI breakage scenarios.

MSYS2 provides three environments: MSYS``MINGW64And UCRT64. The MSYS environment uses Cygwin-style POSIX emulation and is not suitable for native Windows compilation. The MINGW64 Environment links against the legacy msvcrt.dll. Always use UCRT64 for modern C++ development.

On macOS, clang++ resolves to Apple Clang, which is based on an older LLVM fork. Apple Clang lags 1-2 years behind upstream LLVM. To use the latest features, install upstream LLVM via Homebrew and Ensure /opt/homebrew/opt/llvm/bin appears before /usr/bin in your PATH.

Terminal window
# Verify which clang is being used
which clang++
clang++ --version
# Apple Clang: "Apple clang version 15.x.x"
# Upstream LLVM: "clang version 18.x.x"

Pitfall 3: Inconsistent C++ Standard Between Build and Runtime

Section titled “Pitfall 3: Inconsistent C++ Standard Between Build and Runtime”

If you compile with -std=c++23 but link against a C++ runtime library built for C++17, you may get Linker errors for missing symbols (e.g., std::format``std::print). Ensure the C++ standard Library version matches the compiler’s standard mode.

Pitfall 4: Missing libstdc++ or libc++ on Linux

Section titled “Pitfall 4: Missing libstdc++ or libc++ on Linux”

GCC links libstdc++ by default. Clang can use either libstdc++ (GCC’s library) or libc++ (LLVM’s library). Mixing runtime libraries in the same binary causes undefined behavior:

Terminal window
# Clang with GCC's standard library (default on most Linux distros)
clang++ -std=c++23 -stdlib=libstdc++ test.cpp
# Clang with LLVM's standard library
clang++ -std=c++23 -stdlib=libc++ test.cpp

On Windows with MSYS2, ensure you install the correct architecture. The ucrt-x86_64 packages Produce 64-bit binaries. If you need 32-bit, use ucrt-i686 packages. Mixing 32-bit and 64-bit Libraries causes linker errors.

Pitfall 6: Forgetting -stdlib=libc++abi with -stdlib=libc++

Section titled “Pitfall 6: Forgetting -stdlib=libc++abi with -stdlib=libc++”

On Linux, when using Clang with libc++You must also link libc++abi for exception handling and Runtime type information support. Omitting it produces linker errors for __cxa_begin_catch and __gxx_personality_v0:

Terminal window
# This will fail at link time on Linux
clang++ -std=c++23 -stdlib=libc++ test.cpp
# This is correct
clang++ -std=c++23 -stdlib=libc++ test.cpp -lc++abi

Pitfall 7: MSVC __cplusplus Always Reports 199711L

Section titled “Pitfall 7: MSVC __cplusplus Always Reports 199711L”

MSVC sets __cplusplus to 199711L by default regardless of the /std: flag, unless /Zc:__cplusplus is specified. This breaks feature detection macros that rely on __cplusplus. In Cross-platform code, use __has_include and compiler-specific version macros as a workaround, or Always pass /Zc:__cplusplus:

// This works on Clang/GCC but fails on MSVC without /Zc:__cplusplus
#if __cplusplus >= 202002L
// C++20 code
#endif
// Portable alternative
#if defined(__cpp_lib_format)
// std::format is available
#endif
FlagPurpose
-std=c++23Target C++23 standard
-O0 / -O2 / -O3Optimization level (none / balanced / aggressive)
-gGenerate debug information
-Wall -WextraEnable common and extra warnings
-WerrorTreat warnings as errors
-fsanitize=addressEnable AddressSanitizer
-fno-exceptionsDisable exception support
FlagPurpose
-stdlib=libc++Use LLVM’s standard library instead of GCC’s
-fcolor-diagnosticsColored diagnostic output (enabled by default)
-fmodulesEnable C++20 modules (experimental)
FlagPurpose
-fdiagnostics-color=alwaysColored diagnostic output
-fanalyzerEnable static analysis pass

Before starting development, run through this checklist:

  1. Compiler version matches the reference environment (Clang 16+ or GCC 13+).
  2. __cplusplus reports the correct value for the target standard.
  3. The infrastructure test compiles and runs without errors.
  4. On macOS: which clang++ points to Homebrew’s LLVM, not Apple Clang.
  5. On Windows: the MSYS2 UCRT64 terminal is used (not MSYS or MINGW64).
  6. On Linux: ldd shows the correct standard library linkage.

Appendix: Verifying Standard Library Linkage

Section titled “Appendix: Verifying Standard Library Linkage”

After installation, verify that the compiler is correctly linked against the expected standard Library. This is essential for debugging linker errors and ABI mismatches.

Terminal window
# Verify standard library linkage
ldd ./infra_test
# Expected (libstdc++):
# libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
# Expected (libc++):
# libc++.so.1 => /usr/lib/x86_64-linux-gnu/libc++.so.1
# libc++abi.so.1 => /usr/lib/x86_64-linux-gnu/libc++abi.so.1
Terminal window
# Show DT_NEEDED entries (shared library dependencies)
readelf -d ./infra_test | grep NEEDED

Windows: dumpbin (MSVC) or objdump (MinGW)

Section titled “Windows: dumpbin (MSVC) or objdump (MinGW)”
Terminal window
# MSVC: show DLL dependencies
dumpbin /dependents infra_test.exe
# MinGW: show DLL dependencies (using llvm-objdump or gnu objdump)
objdump -p infra_test.exe | grep "DLL Name"
/usr/lib/libc++.1.dylib
# Show shared library dependencies
otool -L ./infra_test
# Expected (libc++):
# /usr/lib/libSystem.B.dylib
#include <version>
#include <iostream>
int main() {
std::cout << "__cplusplus = " << __cplusplus << "\n";
#if __has_cpp_attribute(nodiscard) >= 201907L
std::cout << "C++20 [[nodiscard]] with reason supported\n";
#endif
#if __cpp_lib_ranges >= 202110L
std::cout << "C++20 ranges fully supported\n";
#endif
#if __cpp_lib_print >= 202207L
std::cout << "C++23 std::print supported\n";
#endif
#if __cpp_lib_expected >= 202211L
std::cout << "C++23 std::expected supported\n";
#endif
return 0;
}

For projects that must build with both GCC and Clang, the following CMake script verifies that both Compilers produce correct output for the same input:

abi_validation.cmake
function(validate_compiler compiler_id compiler_cxx)
set(bin_dir ${CMAKE_BINARY_DIR}/validate_${compiler_id})
file(MAKE_DIRECTORY ${bin_dir})
execute_process(
COMMAND ${CMAKE_COMMAND}
-S ${CMAKE_SOURCE_DIR}/tests/abi_check
-B ${bin_dir}
-DCMAKE_CXX_COMPILER=${compiler_cxx}
-DCMAKE_CXX_STANDARD=23
OUTPUT_QUIET
ERROR_VARIABLE err
RESULT_VARIABLE rc
)
if (NOT rc EQUAL 0)
message(WARNING "ABI validation failed for ${compiler_id}: ${err}")
endif()
execute_process(
COMMAND ${CMAKE_COMMAND} --build ${bin_dir}
OUTPUT_QUIET
ERROR_VARIABLE err
RESULT_VARIABLE rc
)
if (NOT rc EQUAL 0)
message(WARNING "ABI validation build failed for ${compiler_id}: ${err}")
endif()
endfunction()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
validate_compiler("GCC" "g++")
validate_compiler("Clang" "clang++")
endif()

This topic covers the core concepts of installing compiler, including underlying theory, practical implementation, and key applications.

Key concepts include:

  • relational databases and SQL
  • normalisation (1NF, 2NF, 3NF)
  • entity-relationship diagrams
  • transaction processing (ACID)
  • NoSQL and distributed databases

Understanding these concepts thoroughly is essential for both examinations and practical programming, and requires both theoretical knowledge and hands-on practice.

Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.