Sistemi di Build: CMake e Gestione dei Pacchetti
Padroneggia i fondamenti di CMake, il CMake moderno basato sui target, FetchContent per le dipendenze, i package manager vcpkg e Conan e i flag del compilatore per build sicure e ottimizzate.
Perché i Sistemi di Build Sono Importanti
Quando un progetto C++ cresce oltre il singolo file, ti serve un sistema di build per gestire compilazione, linking, dipendenze e differenze tra piattaforme. Lanciare a mano g++ *.cpp non scala: ti servono build incrementali (ricompilare solo i file modificati), rilevamento delle librerie, supporto multipiattaforma e build riproducibili. CMake è diventato lo standard de facto tra i generatori di sistemi di build per il C++. Non compila il codice da solo — genera i file di build nativi (Makefile, file Ninja, progetti Visual Studio) per la tua piattaforma.
Le Basi di CMake
Un file CMakeLists.txt descrive la struttura del tuo progetto. Il CMake moderno (3.12+) usa un approccio basato sui target, in cui tutto viene agganciato ai target (eseguibili o librerie):
# CMakeLists.txt — minimum viable project
cmake_minimum_required(VERSION 3.20)
project(MyApp VERSION 1.0.0 LANGUAGES CXX)
# Set C++ standard for the entire project
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) # use -std=c++20, not -std=gnu++20
# Create an executable target
add_executable(myapp
src/main.cpp
src/engine.cpp
src/utils.cpp
)
# Add include directories for this target
target_include_directories(myapp PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# Create a library target
add_library(mathlib STATIC
src/math/vector.cpp
src/math/matrix.cpp
)
target_include_directories(mathlib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include/math
)
# Link the library to the executable
target_link_libraries(myapp PRIVATE mathlib)
# Compiler warnings — essential for quality code
target_compile_options(myapp PRIVATE
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra -Wpedantic -Werror>
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
)Compilare con CMake
CMake usa un processo in due passi: configure (genera i file di build) e build (compila). Usa sempre build fuori dall'albero dei sorgenti, per mantenerlo pulito:
# Configure — generate build files in a 'build' directory
cmake -B build -DCMAKE_BUILD_TYPE=Release
# Build — compile the project
cmake --build build --parallel $(nproc)
# Install (optional)
cmake --install build --prefix /usr/local
# Common build types:
# Debug — -O0 -g (full debug symbols, no optimization)
# Release — -O3 -DNDEBUG (max optimization, no asserts)
# RelWithDebInfo — -O2 -g -DNDEBUG (optimized with debug info)
# MinSizeRel — -Os -DNDEBUG (optimize for size)
# Run with sanitizers (add to CMakeLists.txt or command line)
cmake -B build-asan -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer"FetchContent: Gestione delle Dipendenze
Il modulo FetchContent di CMake scarica e compila le dipendenze al momento della configurazione, rendendo facile includere librerie senza configurazioni manuali:
cmake_minimum_required(VERSION 3.20)
project(MyApp LANGUAGES CXX)
include(FetchContent)
# Fetch GoogleTest
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
# Fetch fmt (formatting library)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.2.1
)
# Download and make available
FetchContent_MakeAvailable(googletest fmt)
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE fmt::fmt)
# Test executable
enable_testing()
add_executable(tests tests/test_main.cpp)
target_link_libraries(tests PRIVATE GTest::gtest_main)
add_test(NAME unit_tests COMMAND tests)vcpkg e Conan
Per progetti più grandi con molte dipendenze, i package manager dedicati al C++ offrono una migliore risoluzione delle dipendenze e una migliore cache:
vcpkg (Microsoft) — si integra direttamente con CMake tramite un file di toolchain. Installa i pacchetti globalmente oppure in "manifest mode" con un file vcpkg.json:
{
"dependencies": ["fmt", "spdlog", "nlohmann-json", "catch2"]
}Da usare con: cmake -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
Conan — usa un file conanfile.txt o conanfile.py per dichiarare le dipendenze. Supporta più configurazioni di build e la cache dei binari:
[requires]
fmt/10.2.1
spdlog/1.13.0
[generators]
CMakeDeps
CMakeToolchainEntrambi i package manager mantengono repository con migliaia di librerie C++ corredate di binari precompilati e testati.
Segui queste regole per un CMake manutenibile:
1. Usa sempre i comandi basati sui target: target_include_directories, target_compile_options, target_link_libraries — mai include_directories o add_definitions (che inquinano lo scope globale).
2. Comprendi PRIVATE/PUBLIC/INTERFACE: PRIVATE = solo per questo target, PUBLIC = per questo target e per chi lo usa, INTERFACE = solo per chi lo usa.
3. Imposta lo standard C++ per target o per l'intero progetto, mai tramite flag grezzi come -std=c++20.
4. Attiva il massimo dei warning: -Wall -Wextra -Wpedantic (GCC/Clang) oppure /W4 (MSVC). Valuta -Werror / /WX in CI.
5. Usa cmake --preset per build riproducibili (CMakePresets.json).
6. Non scrivere mai percorsi fissi nel codice — usa find_package, FetchContent o i package manager.
Usare file(GLOB ...) per raccogliere i sorgenti: file(GLOB SOURCES src/*.cpp) NON viene rieseguito quando si aggiungono o rimuovono file. I nuovi file non verranno compilati finché non riesegui manualmente la configurazione. Elenca sempre i file sorgente in modo esplicito.
Dimenticare CMAKE_CXX_EXTENSIONS OFF: senza questa impostazione, GCC usa -std=gnu++20 invece di -std=c++20, abilitando estensioni non standard che compromettono la portabilità.
Mescolare add_subdirectory e FetchContent: entrambi importano progetti esterni, ma interagiscono in modo diverso con la cache di build. Scegli un solo approccio per ogni dipendenza.
Non impostare CMAKE_EXPORT_COMPILE_COMMANDS ON: questa opzione genera compile_commands.json, necessario a clang-tidy, clangd (LSP) e a molte integrazioni con gli IDE. Attivala sempre.
- CMake è il generatore di build standard per il C++ — impara la sua API basata sui target
- Usa i comandi
target_*(non lo scope globale) con la visibilità PRIVATE/PUBLIC/INTERFACE - FetchContent scarica e compila le dipendenze al momento della configurazione — ideale per insiemi ridotti di dipendenze
- vcpkg (in manifest mode) e Conan gestiscono grafi di dipendenze più grandi con la cache dei binari
- Attiva sempre i warning (
-Wall -Wextra -Wpedantic) e usa build fuori dall'albero dei sorgenti - Genera
compile_commands.jsonper gli IDE e gli strumenti di analisi statica
Quiz — Metti alla prova le tue conoscenze
(15 XP)1. Che cosa fa `target_link_libraries(myapp PRIVATE mathlib)`?
2. Perché `file(GLOB SOURCES src/*.cpp)` è considerata una cattiva pratica in CMake?
3. A che cosa serve `CMAKE_CXX_EXTENSIONS OFF`?