aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Game-Server

Was ist CMake? Ein C++-Serverprojekt bauen

Wenn Sie schon einmal versucht haben, ein mehrdateiges C++-Serverprojekt von Hand mit g++ zu kompilieren, wissen Sie, wie schnell die Kommandozeile zum Albtraum wird: Dutzende .cpp-Dateien, Include-Pfade, externe Bibliotheken und ein Projekt, das bei jeder Änderung komplett neu kompiliert. Genau hier kommt die Frage was ist CMake ins Spiel. CMake ist ein plattformunabhängiger Build-System-Generator, mit dem Sie in einer einzigen Rezeptdatei beschreiben, wie Ihr Projekt gebaut wird. In diesem Artikel richten wir CMake von Grund auf ein, am Beispiel eines Game-/Chat-Servers.

Was ist CMake und warum braucht man es?

CMake ist selbst kein Compiler. Es liest die von Ihnen geschriebene CMakeLists.txt und erzeugt die tatsächlichen Build-Dateien passend zu Ihrem System: meist Makefiles unter Linux, Visual-Studio-Projekte unter Windows oder Ninja-Dateien, falls Sie das bevorzugen. Anders gesagt ist CMake ein "Meta-Build-System": Sie schreiben ein Rezept, und es funktioniert auf jeder Plattform.

  • Portabilität: Dieselbe CMakeLists.txt läuft auf Ihrem Linux-VPS und auf der Windows-Maschine, auf der Sie entwickeln.
  • Abhängigkeitsverwaltung: CMake verfolgt, welche Datei von welcher abhängt, und kompiliert nur die geänderten Dateien neu.
  • Bibliotheken einbinden: Bibliotheken wie pthread, OpenSSL oder Boost binden Sie mit einer einzigen Zeile ein.

Serverprojekte haben genau diese drei Bedürfnisse: viele Dateien, externe Bibliotheken und die Möglichkeit, in unterschiedlichen Umgebungen zu bauen. Deshalb ist CMake bei jedem ernsthaften C++-Server praktisch der Standard.

Beispiel-Projektstruktur

Nehmen wir an, wir schreiben einen einfachen TCP-Server. Teilen wir die Dateien in sinnvolle Ordner auf; das ist sowohl für die Lesbarkeit als auch für die CMake-Konfiguration wichtig.

server/
├── CMakeLists.txt
├── include/
│   └── server/
│       ├── Server.hpp
│       └── Connection.hpp
├── src/
│   ├── main.cpp
│   ├── Server.cpp
│   └── Connection.cpp
└── build/        (die Build-Ausgabe landet hier)

Header unter include/ und Quelldateien unter src/ zu halten, ist eine gängige Aufteilung. Der Ordner build/ enthält die temporären Dateien, die CMake erzeugt, und wird meist zur .gitignore hinzugefügt.

Ihre erste CMakeLists.txt

Schreiben wir nun eine CMakeLists.txt im Wurzelverzeichnis. Ich gehe jede Zeile und ihre Funktion durch.

cmake_minimum_required(VERSION 3.16)
project(GameServer VERSION 1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(server
    src/main.cpp
    src/Server.cpp
    src/Connection.cpp
)

target_include_directories(server PRIVATE include)

Zeile für Zeile:

  • cmake_minimum_required: legt die niedrigste CMake-Version fest, die Sie unterstützen möchten. 3.16 ist eine sinnvolle Basis für moderne Funktionen.
  • project(...): definiert Projektname, Version und Sprache (CXX = C++).
  • set(CMAKE_CXX_STANDARD 17): nutzt den C++17-Standard. Mit STANDARD_REQUIRED ON schlägt der Build fehl, wenn dieser Standard nicht verfügbar ist, statt stillschweigend zurückzufallen.
  • add_executable: erstellt ein ausführbares Target namens server und teilt CMake mit, aus welchen Quellen es gebaut wird.
  • target_include_directories: weist den Compiler an, Header unter include/ zu suchen. PRIVATE bedeutet, dass diese Einstellung nur zu diesem Target gehört.

Das Projekt bauen: Out-of-Source-Builds

Der empfohlene Weg, mit CMake zu bauen, besteht darin, die Build-Dateien getrennt vom Quellbaum zu halten. Deshalb arbeiten wir in einem eigenen Ordner build/:

mkdir build
cd build
cmake ..
cmake --build .

Der Befehl cmake .. liest die CMakeLists.txt im übergeordneten Ordner und erzeugt das Build-System (dieser Schritt heißt Configure). Anschließend führt cmake --build . das erzeugte Makefile oder die Ninja-Datei aus, um die eigentliche Kompilierung durchzuführen. Dieser Befehl ist plattformunabhängig und portabler als der direkte Aufruf von make. Das Ergebnis ist eine ausführbare Datei namens server in build/.

Bibliotheken einbinden und in Module aufteilen

Server stehen selten für sich allein. Meist benötigen sie Threads (pthread), Verschlüsselung (OpenSSL) oder andere Bibliotheken. CMake löst das mit find_package und target_link_libraries:

find_package(Threads REQUIRED)
target_link_libraries(server PRIVATE Threads::Threads)

find_package(Threads REQUIRED) findet die Threading-Bibliothek des Systems; dank REQUIRED stoppt der Configure-Schritt, wenn sie nicht gefunden wird. Anschließend binden wir das Target Threads::Threads in server ein. Das ist der "moderne CMake"-Ansatz: Sie binden Bibliotheken als Targets statt als bloße Namen ein, sodass Include-Pfade und Compiler-Flags automatisch mitkommen.

Wenn Ihr Projekt wächst, ist es sinnvoll, einen Teil des Codes in eine eigene Bibliothek auszulagern. Zum Beispiel können Sie die Netzwerkschicht zu einer wiederverwendbaren Bibliothek machen:

add_library(netcore
    src/Server.cpp
    src/Connection.cpp
)
target_include_directories(netcore PUBLIC include)

add_executable(server src/main.cpp)
target_link_libraries(server PRIVATE netcore Threads::Threads)

Hier ist netcore ein Bibliotheks-Target, und dank PUBLIC include erbt alles, was sich dagegen linkt, automatisch den include/-Pfad. Das ist ein mächtiges Muster, das Wiederholungen in großen Projekten vermeidet.

Debug, Release und praktische Tipps

Während der Entwicklung des Servers wollen Sie Debug-Informationen; beim Ausliefern wollen Sie Optimierung. CMake steuert das über Build-Typen:

cmake -DCMAKE_BUILD_TYPE=Release ..

Debug fügt Symbole hinzu und deaktiviert die Optimierung; Release schaltet Optimierungen wie -O2 ein. Ein paar praktische Empfehlungen:

  • Schalten Sie Warnungen ein: target_compile_options(server PRIVATE -Wall -Wextra) deckt versteckte Fehler früh auf.
  • Fügen Sie den Ordner build/ immer zur .gitignore hinzu; committen Sie generierte Dateien niemals in die Versionsverwaltung.
  • Für schnellere Builds nutzen Sie Ninja: cmake -G Ninja .. ist eine Änderung von einer Zeile, aber beim parallelen Kompilieren deutlich schneller.

Häufige Fragen

Was ist der Unterschied zwischen CMake und Make?

Make ist ein direktes Build-Werkzeug, das Makefiles ausführt. CMake erzeugt diese Makefiles (oder andere Formate wie Ninja und Visual-Studio-Projekte) für Sie. CMake arbeitet also auf einer höheren, plattformunabhängigen Ebene, während Make die von ihm erzeugte Ausgabe verarbeitet.

Muss ich jede Datei einzeln in add_executable auflisten?

Ja, das ist der zuverlässigste Weg. Zwar lassen sich Dateien mit file(GLOB ...) automatisch sammeln, doch CMake bemerkt möglicherweise nicht, wenn Sie eine neue Datei hinzufügen. Quellen explizit aufzulisten ist der empfohlene Ansatz.

Sollte ich den build-Ordner in die Versionsverwaltung aufnehmen?

Nein. build/ enthält vollständig generierte, maschinenspezifische Dateien. Fügen Sie ihn zur .gitignore hinzu; jeder kann ihn auf seiner eigenen Maschine mit cmake .. neu erzeugen.

Brauchen Sie ein solides Build-Setup für Ihr Serverprojekt? Ob eine CMake-Konfiguration von Grund auf oder das Kompilieren eines bestehenden C++-Projekts – wir bringen das gemeinsam in Ordnung. Kontaktieren Sie mich.

Bu kategorideki tüm yazılar →

Devamı için