Nowe możliwości CMake 4.0 – analiza kluczowych innowacji i ich znaczenie dla współczesnego budowania oprogramowania
CMake 4.0, wydany w styczniu 2025 roku, stanowi znaczący krok naprzód w rozwoju tego narzędzia do budowania oprogramowania. Ta główna aktualizacja wprowadza liczne ulepszenia, skupione na zwiększeniu wydajności, rozszerzeniu funkcjonalności i poprawie integracji ze współczesnymi ekosystemami programistycznymi. Do najważniejszych innowacji należą: nowy File-Based API z rozszerzonym wsparciem dla debugowania, istotne rozszerzenia wiersza poleceń umożliwiające lepszą kontrolę nad procesem kompilacji, rewolucyjne zmiany w obsłudze linkerów poprzez wprowadzenie prefiksu LINKER:, a także znaczące ulepszenia w modułach integracyjnych dla języków takich jak Python i Ruby. Równocześnie CMake 4.0 usuwa kompatybilność z wersjami starszymi niż 3.5, wymuszając modernizację starszych projektów. Te zmiany, w połączeniu z ulepszeniami w CPack i CTest, tworzą fundament dla bardziej niezawodnego i wydajnego procesu budowania w projektach o dowolnej skali.
Zmiany w architekturze i interfejsie API
File-Based API w wersji 2.8
Interfejs File-Based API, wprowadzony wcześniej, został znacząco rozbudowany w wersji 4.0. Najważniejszą innowacją jest dodanie pola debugger w obiekcie „target”, co umożliwia precyzyjne konfigurowanie ustawień debugera dla poszczególnych celów kompilacji. Pole to przechowuje ścieżkę do pliku wykonywalnego debugera, argumenty wiersza poleceń oraz zmienne środowiskowe specyficzne dla danego celu.
Dla przykładu, konfiguracja debugera dla projektu w języku C++ może wyglądać następująco:
set_target_properties(my_app PROPERTIES DEBUGGER "gdb" DEBUGGER_ARGS "--command=init.gdb")
Ta zmiana pozwala na głębszą integrację z IDE, takimi jak Visual Studio Code czy CLion, które mogą automatycznie odczytywać te metadane i konfigurować środowisko debugowania.
Generator wyrażeń $<PATH:NATIVE_PATH>
Nowa operacja NATIVE_PATH w generatorze wyrażeń $<PATH> umożliwia konwersję ścieżek w formacie CMake na format natywny dla danego systemu operacyjnego. Jest to szczególnie przydatne przy przekazywaniu ścieżek do zewnętrznych narzędzi, które mogą wymagać specyficznego formatowania. Przykładowo:
$<PATH:NATIVE_PATH,${CMAKE_CURRENT_SOURCE_DIR}/include>
Na systemie Windows wygeneruje ścieżkę z odwróconymi ukośnikami, podczas gdy na systemach Unix pozostanie ze zwykłymi ukośnikami. Eliminuje to konieczność ręcznego przekształcania ścieżek w skryptach CMake, zwiększając przenośność projektów.
Rozszerzenia wiersza poleceń i zmienne
Opcja --link-no-warning-as-error
To nowe przełączanie wiersza poleceń pozwala tymczasowo wyłączyć traktowanie ostrzeżeń linkera jako błędy, co normalnie jest kontrolowane przez zmienną CMAKE_LINK_WARNING_AS_ERROR lub właściwość celu LINK_WARNING_AS_ERROR. Jest szczególnie przydatne podczas iteracyjnego rozwijania dużych projektów, gdzie pełna kompilacja po każdej zmianie byłaby niepraktyczna.
Użycie:
cmake --build . --target my_app --link-no-warning-as-error
Zachowuje inne ustawienia kompilacji, ale ignoruje ostrzeżenia linkera, przyspieszając proces debugowania.
Opcja --project-file dla niestandardowych nazw plików
Choć CMake ściśle preferuje standardową nazwę CMakeLists.txt, nowa opcja --project-file pozwala tymczasowo stosować alternatywne nazwy plików (np. ProjectCMake.txt), ułatwiając stopniową migrację starszych projektów. Przy użyciu niestandardowej nazwy CMake zawsze emituje ostrzeżenie, podkreślając tymczasowy charakter tego rozwiązania.
Przykład użycia:
cmake -S . --project-file=LegacyProject.txt
To rozwiązanie jest szczególnie cenne dla dużych, historycznych baz kodu, gdzie pełna migracja wymagałaby znaczącego nakładu pracy.
Wsparcie dla systemu AIX
Zmienne AIX i CMAKE_HOST_AIX pozwalają jednoznacznie identyfikować systemy IBM AIX zarówno jako system docelowy, jak i hosta. Umożliwia to precyzyjne dostosowywanie skryptów budowania dla specyfiki tego systemu. Przykład:
if(AIX)
add_compile_options(-qstaticinline)
endif()
Ulepszenia w komendach i obsłudze linkerów
Prefiks LINKER: w target_link_libraries()
Rewolucyjna zmiana pozwala na przekazywanie flag specyficznych dla linkera w przenośny sposób, bez konieczności sprawdzania aktualnego linkera. Nowa składnia jest znacznie czytelniejsza:
target_link_libraries(my_app
PRIVATE
"LINKER:-z,relro"
"LINKER:--as-needed"
)
CMake automatycznie konwertuje te flagi na właściwy format dla używanego linkera (np. ld, lld, link.exe), znacząco upraszczając pisanie przenośnych skryptów z zaawansowanymi opcjami linkowania.
Zmienna CMAKE_<LANG>_LINKER_WRAPPER_FLAG
Wprowadzenie tej zmiennej i odpowiadającej jej właściwości celu pozwala na wygodne przekazywanie flag do linkera przez sterownik kompilatora. Przykład dla GCC:
target_link_options(my_app PRIVATE "SHELL:-Wl,--export-dynamic")
Pozwala to na skrócenie i uproszczenie konfiguracji dla niestandardowych scenariuszy linkowania.
Nowe zmienne i właściwości
Sterowanie zachowaniem przy błędach: CMAKE_COMMAND_ERROR_IS_FATAL
Ta nowa zmienna określa domyślne zachowanie CMake przy napotkaniu błędów podczas wykonywania poleceń. Ustawienie jej na TRUE powoduje natychmiastowe przerwanie konfiguracji po pierwszym błędzie, co jest przydatne w środowiskach CI/CD.
set(CMAKE_COMMAND_ERROR_IS_FATAL TRUE)
Diagnostyka czasu linkowania: CMAKE_<LANG>_LINKER_LAUNCHER
Zmienne i właściwości celu umożliwiają określenie narzędzi „opakowujących” (np. ccache dla linkera, narzędzi do profilowania) uruchamianych przed właściwym linkerem.
set(CMAKE_CXX_LINKER_LAUNCHER "ccache")
Traktowanie ostrzeżeń jako błędów: CMAKE_LINK_WARNING_AS_ERROR
Działa analogicznie do znanej zmiennej dla kompilatorów, ale stosuje się do etapu linkowania. Aktywacja jej pomaga „utrzymać czystość” kodu:
set(CMAKE_LINK_WARNING_AS_ERROR ON)
Kontrola bibliotek wykonawczych MSVC: CMAKE_MSVC_RUNTIME_LIBRARY
Pozwala na jawne wybieranie wersji bibliotek wykonawczych MSVC (statyczna/dynamiczna, debug/release), zapewniając spójność w różnych konfiguracjach:
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>")
Rozszerzenia modułów i integracja zewnętrznych narzędzi
Ulepszenia modułu FindPython
Moduł ten zyskał możliwość wielokrotnego wywoływania w tym samym katalogu dzięki zmiennej Python_ARTIFACTS_PREFIX, która zapobiega konfliktom między różnymi wersjami Pythona. Na Windows poprawiono rozpoznawanie flag ABI.
find_package(Python COMPONENTS Interpreter Development)
Wsparcie dla rbenv w FindRuby
Moduł automatycznie wykrywa instalacje Ruby zarządzane przez rbenv, upraszczając konfigurację w środowiskach developerskich:
find_package(Ruby REQUIRED)
Rozszerzenia dla protobuf: PROTOC_EXE
W module FindProtobuf dodano opcję pozwalającą jawnie wskazać niestandardową lokalizację kompilatora protoc, co jest niezbędne przy wielu wersjach Protobuf:
find_package(Protobuf REQUIRED)
protobuf_generate(
PROTOC_EXE /opt/protobuf/bin/protoc
...
)
Ulepszenia w CTest i CPack
Wsparcie Windows Error Reporting w CTest
Opcja ctest --interactive-debug-mode na Windows domyślnie włącza Windows Error Reporting (WER), pozwalając generować zrzuty pamięci podczas awarii testów. Ułatwia diagnozowanie niestabilnych testów.
ctest -T Test --interactive-debug-mode 1
Obsługa nieskompresowanych archiwów w CPack
Generator archiwów CPack zyskał możliwość tworzenia nieskompresowanych archiwów .tar, co przydaje się przy dużych zbiorach danych. Zmienna CPACK_ARCHIVE_THREADS została rozszerzona na wszystkie typy pakietów, pozwalając lepiej wykorzystać wielordzeniowe środowiska.
set(CPACK_ARCHIVE_COMPRESS "NONE")
Kompatybilność wsteczna i migracja
Usunięto wsparcie dla wersji CMake starszych niż 3.5. Wywołania cmake_minimum_required(VERSION ...) lub cmake_policy(VERSION ...) z wersją niższą niż 3.5 spowodują błąd. Zalecana składnia:
cmake_minimum_required(VERSION 3.5...4.0)
Dla ułatwienia migracji dodano zmienną CMAKE_POLICY_VERSION_MINIMUM, którą można ustawić przez zmienną środowiskową.
Na macOS zrezygnowano z domyślnego wybierania SDK – kompilatory muszą teraz samodzielnie wybrać domyślny SDK. Projekty ze starszymi kompilatorami muszą jawnie ustawić:
cmake -DCMAKE_OSX_SYSROOT=macosx
Nowe kierunki rozwoju: instrumentacja budowania
CMake 4.0 wprowadza eksperymentalną funkcję instrumentacji, która szczegółowo mierzy czas konfiguracji, kompilacji, testowania i instalacji. Generowane dane mogą być analizowane przez narzędzia własne lub integrowane z CI/CD do monitorowania wydajności budowania w czasie rzeczywistym.
cmake --instrument="path/to/output.json"
Praktyczne zastosowania nowych funkcji
Integracja z debuggerami IDE
Konfiguracja debuggera w File-Based API pozwala generatorom IDE tworzyć precyzyjne ustawienia. Dla Xcode pole debugger mapowane jest na ustawienia schematu:
set_target_properties(my_app PROPERTIES XCODE_SCHEME_DEBUGGER /usr/bin/lldb XCODE_SCHEME_DEBUGGERARGS "--source=init.lldb")
Dla Visual Studio właściwość VS_DEBUGGER_COMMAND_ARGUMENTS korzysta z tych samych danych.
Przenośne flagi linkera dla bezpieczeństwa
Dzięki prefiksowi LINKER: można spójnie stosować flagi bezpieczeństwa:
target_link_libraries(my_app PUBLIC
"LINKER:-z,now"
"LINKER:-z,relro"
"LINKER:-z,noexecstack"
)
CMake automatycznie zadba o poprawne przekazanie tych flag niezależnie od używanego linkera.
Wielowarstwowa konfiguracja Pythona
W projektach korzystających z wielu interpreterów Pythona, nowy mechanizm prefiksów zapobiega konfliktom:
set(Python_ARTIFACTS_PREFIX "Py39")
find_package(Python 3.9 REQUIRED)
set(Python_ARTIFACTS_PREFIX "Py310")
find_package(Python 3.10 REQUIRED)
Podsumowanie i znaczenie dla ekosystemu
CMake 4.0 wyznacza nowy standard w zarządzaniu budowaniem oprogramowania, koncentrując się na trzech kluczowych obszarach: przenośności (dzięki abstrakcjom jak LINKER: czy NATIVE_PATH), wydajności (ulepszenia CTest/CPack i instrumentacja), oraz integracji (ulepszone moduły dla Python/Ruby/Protobuf). Usunięcie wsparcia dla starszych wersji wymusza modernizację projektów, ale CMAKE_POLICY_VERSION_MINIMUM daje kontrolowaną ścieżkę migracji.
Dla społeczności, najważniejszymi implikacjami są:
- Projekty używające niestandardowych linkerów lub zaawansowanych flag zyskują możliwość pisania przenośnych skryptów,
- ekosystem Data Science (Python/R) zyskuje lepszą integrację z CMake dzięki ulepszonym modułom,
- duże projekty korporacyjne mogą wykorzystać instrumentację do optymalizacji potoków CI/CD,
- usunięcie przestarzałych funkcji zmniejsza koszty utrzymania samego CMake, pozwalając rozwijać innowacje.
Wyzwaniem pozostaje migracja starszych projektów, ale nowe mechanizmy jak --project-file i precyzyjna kontrola polityk znacznie zmniejszają ryzyko związane z aktualizacją. CMake 4.0 nie tylko utrzymuje pozycję narzędzia de facto dla budowania C++, ale także poszerza swoje zastosowanie w ekosystemach wielojęzycznych.
