====== Variablen setzen und Quelldateien finden ======
Bislang war unser CMake-Projekt sehr klein und wenig hilfreich. Spannender wird CMake aber vor allem beim Bauen gegen externe Bibliotheken, wenn Projekte konfiguriert werden können oder wenn die Ordnerstruktur komplizierter wird. Normale GNU-Makefiles arbeiten mit eingebauten String- und Dateifunktionen oder mit Shell-Zugriff, CMake hat eigene eingebaute Funktionen um Dateien in Ordnern zu finden. Anders als GNU-Makefiles kann CMake selbst Bibliotheken im System aufspüren und auch Headerpfade richtig setzen.
===== Variablen =====
Aber eins nach dem Anderen. Zunächst wollen wir ein paar CMake-Variablen setzen und benutzen. Einige Variablen kennt CMake selbst, bspw. den C-Standard, den wir setzen können. Um den C-Standard auf C99 zu setzen, verwenden wir folgende Befehle:
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED True)
CMake kennt eine ganze Reihe von Variablen, die entweder Informationen bringen oder aber CMake Informationen mitteilen können. Eine Auflistung dieser Variablen ist im [[https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html|CMake-Handbuch]] zu finden.
Mit ''set'' werden aber auch Variablen gesetzt, die wir in unserem CMakefile weiter verwenden wollen. Das benutzen wir bspw. wenn wir Ordnernamen ein einziges Mal eingeben wollen. Der ''set''-Befehl könnte dann so aussehen:
set(GIT_TAG "")
Gesetzt Variablen werden, ähnlich wie Variablen (manchmal) in Bash mit ''${...}'' verwendet, also beispielsweise:
string(STRIP "${GIT_TAG}" GIT_TAG)
Der String Befehl erlaubt eine Reihe von String-Operationen, also Manipulation und Finden von und innerhalb von Zeichenketten. Weitere Informationen sind im [[https://cmake.org/cmake/help/latest/command/string.html|CMake-Handbuch zu finden]].
===== if-then-else =====
Variablen werden oftmals verwendet um in bestimmten Befehlen gesetzt zu werden und dann geprüft zu werden. Mit if-then-else finden wir heraus, ob eine Bedingung stimmt und führen Befehle aus, falls das zutrifft. Wichtig zu beachten ist, dass CMake verlangt, dass auch das ''endif'' ein Klammerpaar besitzt und Leerzeichen zwischen Befehl und Klammern nicht erlaubt. if-then-else nimmt in CMake folgende Form an:
if(A)
# do A
elseif(B)
# do B
else()
# do something else
endif()
Die Bedingungen sind vielfältig, beispielsweise können wir prüfen ob Variablen gesetzt sind mit ''DEFINED'', ob Strings identisch sind mit ''STREQUAL'' oder ob Dateien existieren mit ''EXISTS''. Das folgende Beispiel könnte eine Versionserkennung sein:
set(GIT_TAG "")
set(VERSION "")
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/version)
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/version VERSION)
message("-- VERSION from file: ${VERSION}")
elseif(NOT ${GIT_TAG} STREQUAL "")
set(VERSION "v${GIT_TAG}")
message("-- VERSION git tag: ${VERSION}")
elseif(DEFINED $ENV{PKG_VERSION})
set(VERSION "${ENV{PKG_VERSION}")
message("-- VERSION PKG Version: ${VERSION}")
else()
set(VERSION "unknown")
message("-- VERSION unknown version")
endif()
Zu beachten am Beispiel ist, dass die Umgebungsvariable ''PKG_VERSION'' von der Umgebung gesetzt sein könnte, und ''GIT_TAG'' muss von uns weiter oben vorbelegt werden. Sollte auch die Datei ''version'' im aktuellen Source-Verzeichnis fehlen, erhalten wir hier eine unbekannte Versionsangabe.
====== Quelldateien finden ======
Spannend wird CMake aber vor allem dann, da es sich aus verschiedenen Verzeichnissen Quelldateien zusammensammeln kann und zu einer Binary bauen kann. Mit dem ''file''-Befehl geben wir CMake die Aufgabe Datein in einem Verzeichnis zu suchen. Oftmals wird der Befehl zu benutzt:
# Syntax: file(GLOB_RECURSE
# [FOLLOW_SYMLINKS] [LIST_DIRECTORIES true|false]
# [RELATIVE ] [CONFIGURE_DEPENDS]
# [...])
file(GLOB_RECURSE SRC_FILES "*.c")
Hier geben wir den Unterbefehl ''GLOB_RECURSE'' an, zusammen mit der Zielvariable ''SRC_FILES'' und dem Globbing-String ''"*.c"''. ''GLOB_RECURSE'' gibt an, dass CMake in diesem Verzeichnis und allen Unterverzeichnissen (rekursiv) nach Dateien suchen soll, die einem Globbing-String entsprechen. Ein Globbing-String ist in etwa wie eine Schablone. Wir geben ''*.c'' an; das Sternchen dient als Platzhalter für beliebige Zeichen, also suchen wir nach allen Dateien, die auf ''.c'' enden.
Neben ''GLOB_RECURSE'' gibt es noch andere Unterbefehle, bspw. ''GLOB'', weitere Informationen zu den Befehlen gibt es im [[https://cmake.org/cmake/help/latest/command/file.html|CMake-Handbuch]].
''file'' legt nun aber alle Dateien im aktuellen Verzeichnis in der Variable ''SRC_FILES'' ab. Diese Variable können wir nutzen um unser Programm zu bauen, bspw. so:
add_executable(hello_world ${SRC_FILES})
Egal wie viele Dateien CMake gefunden hat, es wird nun hier alle übersetzen und zur Programmdatei ''hello_world'' zusammenlinken.