CMake-Crashkurs – 3 Befehle zur ersten ausführbaren Datei

()

In diesem Beitrag möchte ich euch CMake vorstellen, welches euren Programmieralltag erheblich vereinfachen kann. Ich gebe hier einen schnellen Crashkurs, der euch die wichtigsten Informationen liefert, um eurer erstes ausführbares C++ Programm mit CMake zu erstellen. CMake kann problemlos über die CMake-Webseite oder mittels eines Paketmanagers installiert werden. In meinem ersten CMake-Video auf YouTube stelle ich die verschiedenen Möglichkeiten vor. Dort findet ihr auch weitere Videos, wenn ihr mehr über CMake erfahren wollt.

Diesen und andere Beiträge von mir kannst du als sauber formatierte PDF-Datei zum Ausdrucken oder offline Lesen erwerben. Mehr Informationen dazu findest du hier.

Nötige Vorkenntnisse

  • Falls ihr die folgenden Beispiele ausführen möchtet, sollte CMake sowie ein C++-Compiler auf eurem System installiert sein. Zudem solltet ihr ein Minimum an Programmierkenntnissen, im Optimalfall C++, mitbringen.
  • Weitere Vorkenntnisse oder Programme sind für diesen Artikel nicht notwendig.

Was ist CMake?

CMake ist ein plattformunabhängiger Build-System-Generator. Abgekürzt bedeutet das, dass CMake in der Lage ist Konfigurationsdateien für eine Vielzahl unterschiedlicher Build-Systeme (Make, Ninja, Visual Studio …) auf unterschiedlichen Plattformen (Linux, Windows, macOS) zu erzeugen. Zur Erstellung dieser Konfigurationsdateien müssen eine oder mehrere projektspezifische CMake-Script-Dateien mit dem Namen CMakeLists.txt erstellt werden.

Wenn ein Projekt eine solche CMakeLists.txt-Datei bereitstellt, ist es mit der Hilfe von CMake möglich, dass jede Person, die an diesem Projekt arbeitet, ihr bevorzugtes Build-System auf ihrer bevorzugten Plattform verwenden kann. Natürlich unter der Voraussetzung, dass der Code im Projekt auf diesen Plattformen lauffähig ist. CMake stellt dazu sogenannte Generatoren bereit, die die benötigten Konfigurationsdateien erzeugen. Welche Generatoren auf einem System verfügbar sind, kann durch den Kommandozeilenbefehl cmake –help überprüft werden:

$ cmake --help

...

Generators

The following generators are available on this platform (* marks default):
* Unix Makefiles               = Generates standard UNIX makefiles.
  Green Hills MULTI            = Generates Green Hills MULTI files
                                 (experimental, work-in-progress).
  Ninja                        = Generates build.ninja files.
  Ninja Multi-Config           = Generates build-<Config>.ninja files.
  Watcom WMake                 = Generates Watcom WMake makefiles.
  CodeBlocks - Ninja           = Generates CodeBlocks project files.
  CodeBlocks - Unix Makefiles  = Generates CodeBlocks project files.
  CodeLite - Ninja             = Generates CodeLite project files.
  CodeLite - Unix Makefiles    = Generates CodeLite project files.
  Sublime Text 2 - Ninja       = Generates Sublime Text 2 project files.
  Sublime Text 2 - Unix Makefiles
                               = Generates Sublime Text 2 project files.
  Kate - Ninja                 = Generates Kate project files.
  Kate - Unix Makefiles        = Generates Kate project files.
  Eclipse CDT4 - Ninja         = Generates Eclipse CDT 4.0 project files.
  Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files.

Neben einigen generellen Erklärungen, die ich an dieser Stelle ausspare (dargestellt durch die drei Punkte ), erscheinen ganz unten in der Ausgabe die zur Verfügung stehenden Generatoren. Die obige Ausgabe wurde auf Ubuntu 20.04 erzeugt. Das Sternchen-Symbol * zeigt den voreingestellten Generator an, hier Unix Makefiles in Zeile 8. Das bedeutet, sollte beim Aufruf von CMake kein Generator explizit ausgewählt werden, so erstellt CMake auf diesem System Konfigurationsdateien für das Build-System Make.

Der CMake-Build-Prozess

Übersicht

Blicken wir nun einmal auf eine Übersicht, die den Prozess zur Erstellung einer ausführbaren Datei mit CMake darstellt.

Auf der linken Seite ist der Projektordner dargestellt, in dem sich die CMakeLists.txt-Datei befindet. Zudem befinden sich hier beispielhaft die Source-Dateien des Projektes, die sich aber natürlich auch in zusätzlichen Unterordnern befinden können. In eurem Projektordner solltet ihr einen Build-Ordner erstellen und in diesem Build-Ordner soll dann die Kompilierung des Projektes stattfinden. So könnt ihr die kompilierten Build-Dateien von den eigentlichen Dateien des Projektes trennen. Ich nenne den Build-Ordner ganz klassisch  „build“, wie auf der linken Seite oben zu sehen.

Der Inhalt des Build-Ordner „build“ ist auf der rechten Seite dargestellt. In diesem Ordner wird das Programm CMake ausgeführt, welches dazu die CMakeLists.txt-Datei auf der linken Seite respektive im darüberliegenden Ordner, verwendet. CMake erzeugt basierend auf der CMakeLists.txt-Datei CMake spezifische Dateien sowie Konfigurationsdateien für das gewählte Build-System und verlinkt darin die Source-Dateien des Projektes. Alle erstellten Dateien fasse ich unter den Begriffen Build-Dateien respektive Projektdateien zusammen.

Wenn das Build-System nun aufgerufen wird, liest es die Konfigurationsdateien ein, parsed und verarbeitet die darin spezifizierten Source-Dateien und erzeugt ausführbare Dateien und/oder Bibliotheken. Im CMake Kontext werden die erzeugten ausführbaren Dateien und Bibliotheken als Targets bezeichnet.

Ablauf

Um ein Projekt mit CMake zu erstellen, müssen die folgenden Schritte ausgeführt werden:

  1. Erstellen des Build-Ordners und Wechsel in selbigen
  2. Aufruf von CMake
  3. Aufruf des Build-Systems
  4. (optional) Ausführen des erzeugten Programms

Unter Ubuntu 20.04 kann dies beispielsweise innerhalb eines Terminals mit den folgenden Befehlen erreicht werden:

$ mkdir build
$ cd build
$ cmake ..
$ cmake --build .
$ ./hello_world

Die ersten beiden Zeilen erstellen den Ordner build und wechseln innerhalb der Kommandozeile in den erstellten Ordner.

Anschließend wird CMake mit dem Befehl cmake aufgerufen und mit den beiden Punkten .. wird auf den darüberliegenden Projektordner verwiesen, in dem die CMakeLists.txt-Datei liegt.

Daraufhin wird das Build-System mit cmake –build . aufgerufen, wobei der Punkt am Ende auf den aktuellen Build-Ordner verweist, in dem die Konfigurationsdateien gespeichert sind. CMake übernimmt mit diesem Befehl automatisch die Aufgabe, das Build-System korrekt aufzurufen, sodass man sich selbst nicht um den konkreten Befehl kümmern muss. Besonders für Einsteiger ist das sicherlich sehr hilfreich, aber insbesondere auch dann, wenn man über verschiedene System hinweg kompilieren möchte. Man kann an dieser Stelle natürlich auch einfach selbst das Build-System aufrufen, zum Beispiel durch den Aufruf von make für das Make Build-System.

Abschließend kann das erzeugte Programm ausgeführt werden, im obigen Beispiel trägt dies den Namen hello_world.

Die CMakeLists.txt-Datei

Eine einfache CMakeLists.txt-Datei zum Erstellen einer ausführbaren Datei mit dem Namen hello_world kann nun wie folgt aussehen, wobei lediglich die drei Befehle cmake_minimum_required(), project() und add_executable() benötigt werden:

cmake_minimum_required(VERSION 3.7)

project(
   hello_world_project
   LANGUAGES CXX
)

add_executable(hello_world main.cpp)

Genereller Aufbau von CMake-Befehlen

CMake-Befehle werden immer kleingeschrieben. Hingegen werden alle Keywords, wie zum Beispiel VERSION oder LANGUAGES, vollständig in Großbuchstaben geschrieben. Keywords, oder übersetzt Schlüsselwörter, sind feststehende Begriffe in CMake-Befehlen, die als Argumente mit übergeben werden. Die meisten Keywords sind optional, es gibt aber auch einige, die immer übergeben werden müssen. Hinter den Keywords können dann häufig zusätzliche Argumente übergeben werden, wie die Zahl 3.7 nach dem Keyword VERSION im cmake_minimum_required()-Befehl. Im Folgenden gehe ich kurz auf die drei hier verwendeten Befehle ein. Zusätzlich ist jeder Befehl mit einem Link hinterlegt, der zu der jeweiligen CMake-Dokumentation des Befehls führt.

Der Befehl cmake_minimum_required()

Der Befehl cmake_minimum_required() sollte am Anfang einer jeden CMakeLists.txt-Datei stehen, denn mit diesem Befehl spezifiziert ihr die mindestens benötigte CMake-Version zum Ausführen dieses Scripts. In diesem Beispiel ist das Version 3.7, welches nach dem Keyword VERSION folgt.

Der Befehl project()

In Zeile 3-7 folgt der project()-Befehl, der ebenfalls in jeder CMakeLists.txt-Datei auftauchen sollte, und zwar am besten direkt nach dem cmake_minimum_required()-Befehl. Hier wird der Name des Projektes festgelegt, zum Beispiel hello_world_project, und was noch deutlich wichtiger ist: Es wird nach den benötigten Compilern für das Projekt auf dem System gesucht. Durch das Keyword LANGUAGES und der darauffolgenden Bezeichnung CXX, wird an dieser Stelle angegeben, dass es sich um C++-Projekt handelt und somit ein C++-Compiler benötigt wird. Das Keyword LANGUAGES (inkl. der Bezeichnung CXX), kann auch weggelassen werden, dann sucht CMake automatisch nach einem C- und C++-Compiler. Wird kein entsprechender Compiler gefunden, gibt CMake an dieser Stelle einen Fehler aus.

Der Befehl add_executable()

Schlussendlich folgt in Zeile 8 der add_executable()-Befehl, durch den CMake eine ausführbare Datei mit dem Namen hello_world zum Build-Prozess hinzufügt. Die Source-Dateien folgen auf den Namen hello_world der ausführbaren Datei. In diesem Beispiel ist das nur eine main.cpp-Datei, die z. B. wie folgt aussehen kann:

#include <iostream>

int main()
{
    std::cout << "Hello World!" << std::endl;
    return 0;
}

Wird die erstellte hello_world-Datei nun auf dem Rechner ausgeführt, gibt dieses Programm „Hello World!“ auf der Konsole aus.

Output des Build-Prozesses

Ausführen im Terminal

Unter Ubuntu 20.04 sieht der Output all der oben genannten Schritte wie folgt aus:

$ mkdir build
$ cd build
$ cmake ..
-- The CXX compiler identification is GNU 9.4.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: <PATH_TO_PROJECT>/build
$ cmake --build .
Scanning dependencies of target hello_world
[ 50%] Building CXX object CMakeFiles/hello_world.dir/main.cpp.o
[100%] Linking CXX executable hello_world
[100%] Built target hello_world
$ ./hello_world 
Hello World!

Das Erstellen des Ordners build mit dem Befehl mkdir build und dem wechsel in diesen Ordner mit dem Befehl cd build erzeugt keinen Output, wie in den ersten beiden Zeilen zu sehen.

Beim Aufruf von cmake .. (Zeile 3) wird zunächst der gefundene C++-Compiler ausgegeben (Zeile 4). Es folgen einige weitere Informationen, die aber zunächst nicht weiter interessant sind. Am Ende wird dann noch der Pfad zu den Build-Dateien ausgegeben (Zeile 12), wobei <PATH_TO_PROJECT> als Platzhalter für den Pfad zum Code fungiert.

Es folgt der Aufruf des Compilers mit dem Befehl cmake –build . (Zeile 13), welcher das Target hello_world erstellt (Zeile 17).

Zuletzt wird das hello_world Programm ausgeführt und wie erwartet „Hello World!“ auf der Konsole ausgegeben (Zeile 18 und 19).

Erstellte Dateien

Im Zuge dieses Build-Prozesses werden die folgenden Dateien im Build Ordner erzeugt:

$ ll
...
... CMakeCache.txt
... CMakeFiles/
... cmake_install.cmake
... hello_world*
... Makefile

Mit den Punkten () spare ich erneut einen Teil des Outputs aus, der an dieser Stelle nicht interessant ist.

Die Datei CMakeCache.txt

In der Datei CMakeCache.txt werden die Cache-Variablen von CMake gespeichert, die so über mehrere Aufrufe von CMake hinweg verfügbar sind. Die hier gespeicherten Variablen werden in der CMake-GUI angezeigt. Mehr Informationen zu Cache-Variablen in CMake und zur CMake-GUI zeige ich euch in einem Video auf YouTube.

Der Ordner CMakeFiles

In diesem Ordner werden viele CMake spezifische Dateien gespeichert. In der Regel ist dieser Ordner nicht interessant und sollte so belassen werden, wie er ist.

Die Datei cmake_install.cmake

Die Datei cmake_install.cmake wird von CMake generiert und wird intern von CMake respektive CPack zur Installation von Packages verwendet. Mit diesen Features von CMake braucht man sich erst dann zu beschäftigen, wenn man Packages erzeugen und installieren möchte. Mehr Informationen zu dieser Datei finden sich in der CMake-Dokumentation.

Die ausführbare Datei hello_world

Die ausführbare Datei hello_world ist das erstellte Target, welches oben im Terminal ausgeführt wurde. Das Sternchen-Symbol * zeigt unter Ubuntu 20.04 an, dass es sich, um eine ausführbare Datei handelt.

Das Makefile

Die Konfigurationsdatei für das Build-System make, welche durch Verwendung des „Unix Makefiles“ Generators von CMake generiert wurde. Diese Datei kann mit jedem normalen Texteditor geöffnet und gelesen werden.

Weitere Informationen

Den Code zu diesem Beitrag könnt ihr gerne weiter verwenden, ihr findet ihn dazu auf GitHub.

In meinem Buch „CMake für Einsteiger“ und in meinen Videos auf YouTube stelle ich dieses und weitere CMake Themen noch einmal detaillierter vor. Bei Fragen oder Anmerkungen schreibt mir gerne einen Kommentar 🙂

Meine Webseite ist komplett werbefrei. Falls dir dieser Beitrag gefallen hat und du meine Arbeit gerne unterstützen möchtest, schau daher doch einmal auf meiner Support-Seite vorbei. Das würde mich sehr freuen :).

Wie hilfreich war dieser Beitrag?

Klicke auf die Sterne um zu bewerten!

Durchschnittliche Bewertung / 5. Anzahl Bewertungen:

Bisher keine Bewertungen! Sei der Erste, der diesen Beitrag bewertet.

Abonnieren
Benachrichtige mich bei
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Nach oben scrollen
WordPress Cookie Plugin von Real Cookie Banner