Das Projekt konfigurieren

Viele Projekte haben die Möglichkeit beim Bauen bestimmte Teilaspekte aus oder anzuschalten. Beispielweise könnten wir Module haben, die wir einzeln ein oder ausschalten können wollen um an Speicherplatz zu sparen oder weil wir bestimmte Abhängigkeiten nicht haben, die diese Module benötigen oder wir brauchen einfach die Funktionalität nicht.

Dann helfen uns die Möglichkeiten von CMake um unser Projekt zu konfigurieren. Nehmen wir bspw. ein Hello-World Programm, welches wir erweitern wollen, um eine Namenseingabe. Die Namenseingabe ist dabei unser ausschaltbares Modul.

Nehmen wir also folgenden C-Code als Ausgangsprogramm:

#include <stdio.h>
#include <unistd.h>
 
#define VERSION "1.0"
#define INPUT_QUESTION "What is your name?"
 
int main() {
    printf("Hello World, version " VERSION "\n");
 
    printf(INPUT_QUESTION "\n");
    char buffer[20];
    read(STDIN_FILENO, buffer, 20);
    printf("Hello %s\n", buffer);
}

CMake erlaubt uns nun Variablen in eine Header-Datei zu exportieren. Dafür brauchen wir den Befehl configure_file, der eine Quelldatei angibt und eine Zieldatei. Die Quelldatei ist im ein Template des Zielheaders, der die zu exportierenden Macros enthält mit Platzhaltern für die passende CMake-Variable. Die Eingabe erfolgt, falls ein relativer Pfad angegeben ist, relativ zum Source-Ordner, die Ausgabe relativ zum Build-Ordner.

Folgender Befehle erstellt aus einer config.h.in eine build/config.h. Außerdem setzen wir noch den include-Pfad um die erzeugte config.h im Suchpfad zu haben.

configure_file (
  "config.h.in"
  "config.h"
)
 
include_directories(${CMAKE_CURRENT_BINARY_DIR})

Jetzt fehlen noch die Variablen, die wir belegen wollen. Beispielsweise fügen wir eine Variable für die Version hinzu und eine Variable für die Frage, die vor der Eingabe dem Nutzer stellen, also fügen wir vor das obige configure_file:

set(VERSION 1.0)
set(INPUT_QUESTION "What is your Name?")

Nun brauchen wir nur noch das Template der Header-Datei. Hier erlaubt uns CMake einige einfache Textersetzungen, so werden '@…@' durch den Inhalt der Variable '…' ersetzt. In diesem einfachen Fall genügt also eine folgende Header-Datei:

#define VERSION "@VERSION@"
#define INPUT_QUESTION "@INPUT_QUESTION@"

Nun haben wir ein paar Variablen aus CMake in unser Programm übernommen, wenn wir den Header inkludieren, aber noch keine Optionen an unser Projekt.

Compile-Zeit Optionen

Für an- und ausschaltbare Optionen brauchen wir den option Befehl. Dieser erlaubt uns in eine Variable den Wert eines Configure-Zeit Parameters zu speichern, eine Beschreibung anzugeben und einen Standard-Wert anzugeben, also ob die Option an oder aus sein soll, falls nichts anderes angegeben ist.

Nehmen wir also bspw. die Frage, ob wir Eingaben in unser Programm einbauen wollen, oder nicht. Dann könnte die Stelle in der CMakeLists.txt so aussehen:

option(INPUT_ENABLE "Enable Input" ON)
if(INPUT_ENABLE)
    set(INPUT_QUESTION "What is your name?")
endif()

Wir geben CMake die Option, INPUT_ENABLE, die standardmäßig auf an geschalten ist. Dann fragen wir mit if ab, ob diese Variable gesetzt ist und setzen unseren Fragestring. Nur wenn die Eingabe angeschaltet ist, brauchen wir diese Variable.

Wir ergänzen, bzw. verändern die config.h.in nun wie folgt:

#define VERSION "@VERSION@"
 
#cmakedefine INPUT_ENABLE
#cmakedefine INPUT_QUESTION "@INPUT_QUESTION@"

Wir bekommen also nun in unserem Programm die Version aus CMake. Mit cmakedefine erhalten wir eine der beiden Zeilen, je nachdem ob die Option INPUT_ENABLE gesetzt ist.

#define INPUT_ENABLE
/* #undef INPUT_ENABLE */

Unser Hello-World Programm sieht nun so aus; wir benutzen die Präprozessor-Definitionen um Teile unseres Codes ein oder auszuschalten und verwenden die Konstanten in unserem Programm:

#include "config.h"
 
#include <stdio.h>
#ifdef INPUT_ENABLE
#include <unistd.h>
#endif
 
int main() {
    printf("Hello World, version " VERSION "\n");
#ifdef INPUT_ENABLE
    printf(INPUT_QUESTION "\n");
    char buffer[20];
    read(STDIN_FILENO, buffer, 20);
    printf("Hello %s\n", buffer);
#endif
}

Verwendung der Optionen

Die Optionen können nun mit dem CMake Programm im Termal gesetzt werden. Mit der -D Option setzen wir unsere Projektkonfigurationen mit wahlweise:

cmake -DINPUT_ENABLE=off ..
cmake -DINPUT_EANBLE=on ..

Dann übersetzen wir das Projekt wie gewohnt mit make.

Eine andere Möglichkeit ist die Konfiguration mit der CMake-GUI. Lesen wir damit die Projektdateien ein, erhalten wir eine INPUT_ENABLE Option, die wir an oder ausschalten können.