Wofür ist CMake?
Laut Wikipedia:
CMake ist [...] Software zur Verwaltung des Erstellungsprozesses von Software mithilfe einer compilerunabhängigen Methode. Es unterstützt Verzeichnishierarchien und Anwendungen, die von mehreren Bibliotheken abhängen. Es wird in Verbindung mit nativen Build-Umgebungen wie make, Apples Xcode und Microsoft Visual Studio verwendet.
Mit CMake müssen Sie keine separaten Einstellungen mehr für Ihre Compiler- / Build-Umgebung vornehmen. Sie haben eine Konfiguration, die in vielen Umgebungen funktioniert .
CMake kann aus denselben Dateien eine Microsoft Visual Studio-Lösung, ein Eclipse-Projekt oder ein Makefile-Labyrinth generieren, ohne etwas daran zu ändern.
Bei einer Reihe von Verzeichnissen mit Code verwaltet CMake alle Abhängigkeiten, Erstellungsaufträge und anderen Aufgaben, die Ihr Projekt ausführen muss, bevor es kompiliert werden kann. Es kompiliert eigentlich nichts. Um CMake verwenden zu können, müssen Sie (unter Verwendung der Konfigurationsdateien CMakeLists.txt) mitteilen, welche ausführbaren Dateien Sie kompilieren müssen, mit welchen Bibliotheken sie verknüpft sind, welche Verzeichnisse sich in Ihrem Projekt befinden und welche darin enthalten sind sowie Details wie Flags oder alles andere, was Sie brauchen (CMake ist ziemlich mächtig).
Wenn dies korrekt eingerichtet ist, verwenden Sie CMake, um alle Dateien zu erstellen, die Ihre "native Build-Umgebung" Ihrer Wahl für ihre Arbeit benötigt. Unter Linux bedeutet dies standardmäßig Makefiles. Sobald Sie CMake ausführen, werden eine Reihe von Dateien für den eigenen Gebrauch sowie einige Makefile
s erstellt. Alles, was Sie danach tun müssen, ist, jedes Mal, wenn Sie Ihren Code bearbeitet haben, "make" aus dem Stammordner in die Konsole einzugeben, und bam wird eine kompilierte und verknüpfte ausführbare Datei erstellt.
Wie funktioniert CMake? Was tut es?
Hier ist ein Beispiel für ein Projekt-Setup, das ich durchgehend verwenden werde:
simple/
CMakeLists.txt
src/
tutorial.cxx
CMakeLists.txt
lib/
TestLib.cxx
TestLib.h
CMakeLists.txt
build/
Der Inhalt jeder Datei wird später angezeigt und erläutert.
CMake richtet Ihr Projekt gemäß dem Stammverzeichnis CMakeLists.txt
Ihres Projekts ein und zwar in dem Verzeichnis, cmake
aus dem Sie es in der Konsole ausgeführt haben. Wenn Sie dies aus einem Ordner heraus tun, der nicht das Stammverzeichnis Ihres Projekts ist, wird ein so genannter Out-of-Source- Build erstellt. Dies bedeutet, dass Dateien, die während der Kompilierung erstellt wurden (obj-Dateien, lib-Dateien, ausführbare Dateien, wie Sie wissen), in diesem Ordner abgelegt werden , vom eigentlichen Code getrennt gehalten. Es hilft, Unordnung zu reduzieren und wird auch aus anderen Gründen bevorzugt, auf die ich nicht eingehen werde.
Ich weiß nicht, was passiert, wenn Sie cmake
auf einer anderen als der Wurzel ausführen CMakeLists.txt
.
In diesem Beispiel muss ich, da alles in den build/
Ordner verschoben werden soll, zuerst dorthin navigieren und dann CMake das Verzeichnis übergeben, in dem sich das Stammverzeichnis CMakeLists.txt
befindet.
cd build
cmake ..
Standardmäßig wird hiermit Makefiles wie gesagt eingerichtet. So sollte der Build-Ordner jetzt aussehen:
simple/build/
CMakeCache.txt
cmake_install.cmake
Makefile
CMakeFiles/
(...)
src/
CMakeFiles/
(...)
cmake_install.cmake
Makefile
lib/
CMakeFiles/
(...)
cmake_install.cmake
Makefile
Was sind all diese Dateien? Das einzige, worüber Sie sich Sorgen machen müssen, sind das Makefile und die Projektordner .
Beachten Sie die Ordner src/
und lib/
. Diese wurden erstellt, weil simple/CMakeLists.txt
sie mit dem Befehl auf sie verweisen add_subdirectory(<folder>)
. Dieser Befehl weist CMake an, in diesem Ordner nach einer anderen CMakeLists.txt
Datei zu suchen und dieses Skript auszuführen. Daher muss in jedem auf diese Weise hinzugefügten Unterverzeichnis eine CMakeLists.txt
Datei enthalten sein. In diesem Projekt wird simple/src/CMakeLists.txt
beschrieben, wie die eigentliche ausführbare Datei erstellt wird und simple/lib/CMakeLists.txt
wie die Bibliothek erstellt wird. Jedes Ziel, das ein CMakeLists.txt
beschreibt, wird standardmäßig in seinem Unterverzeichnis innerhalb des Build-Baums platziert. Also nach einer kurzen
make
In der Konsole von build/
werden einige Dateien hinzugefügt:
simple/build/
(...)
lib/
libTestLib.a
(...)
src/
Tutorial
(...)
Das Projekt wird erstellt und die ausführbare Datei kann ausgeführt werden. Was tun Sie, wenn die ausführbaren Dateien in einem bestimmten Ordner abgelegt werden sollen? Legen Sie die entsprechende CMake-Variable fest oder ändern Sie die Eigenschaften eines bestimmten Ziels . Mehr zu CMake-Variablen später.
Wie sage ich CMake, wie ich mein Projekt erstellen soll?
Hier ist der erläuterte Inhalt jeder Datei im Quellverzeichnis:
simple/CMakeLists.txt
::
cmake_minimum_required(VERSION 2.6)
project(Tutorial)
# Add all subdirectories in this project
add_subdirectory(lib)
add_subdirectory(src)
Die minimal erforderliche Version sollte immer festgelegt werden, entsprechend der Warnung, die CMake auslöst, wenn Sie dies nicht tun. Verwenden Sie eine beliebige Version von CMake.
Der Name Ihres Projekts kann später verwendet werden und weist darauf hin, dass Sie mehr als ein Projekt aus denselben CMake-Dateien verwalten können. Ich werde mich jedoch nicht damit befassen.
Wie bereits erwähnt, wird add_subdirectory()
dem Projekt ein Ordner hinzugefügt. Dies bedeutet, dass CMake erwartet, dass es einen Ordner enthält CMakeLists.txt
, den es dann ausführt, bevor es fortfährt. Übrigens, wenn Sie eine CMake-Funktion definiert haben, können Sie sie aus anderen CMakeLists.txt
s in Unterverzeichnissen verwenden, aber Sie müssen sie definieren, bevor Sie sie verwenden, sonst add_subdirectory()
wird sie nicht gefunden. CMake ist jedoch schlauer in Bezug auf Bibliotheken, sodass dies wahrscheinlich das einzige Mal ist, dass Sie auf diese Art von Problem stoßen.
simple/lib/CMakeLists.txt
::
add_library(TestLib TestLib.cxx)
Um Ihre eigene Bibliothek zu erstellen, geben Sie ihr einen Namen und listen dann alle Dateien auf, aus denen sie erstellt wurde. Einfach. Wenn foo.cxx
zum Kompilieren eine andere Datei benötigt wird , schreiben Sie stattdessen add_library(TestLib TestLib.cxx foo.cxx)
. Dies funktioniert beispielsweise auch für Dateien in anderen Verzeichnissen add_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx)
. Mehr zur Variablen CMAKE_SOURCE_DIR später.
Sie können damit auch angeben, dass Sie eine gemeinsam genutzte Bibliothek möchten. Das Beispiel : add_library(TestLib SHARED TestLib.cxx)
. Fürchte dich nicht, hier beginnt CMake dein Leben zu erleichtern. Unabhängig davon, ob es freigegeben ist oder nicht, müssen Sie nur noch den Namen verwenden, den Sie hier angegeben haben, um eine auf diese Weise erstellte Bibliothek zu verwenden. Der Name dieser Bibliothek lautet jetzt TestLib, und Sie können von überall im Projekt darauf verweisen . CMake wird es finden.
Gibt es eine bessere Möglichkeit, Abhängigkeiten aufzulisten? Auf jeden Fall ja . Weitere Informationen hierzu finden Sie weiter unten.
simple/lib/TestLib.cxx
::
#include <stdio.h>
void test() {
printf("testing...\n");
}
simple/lib/TestLib.h
::
#ifndef TestLib
#define TestLib
void test();
#endif
simple/src/CMakeLists.txt
::
# Name the executable and all resources it depends on directly
add_executable(Tutorial tutorial.cxx)
# Link to needed libraries
target_link_libraries(Tutorial TestLib)
# Tell CMake where to look for the .h files
target_include_directories(Tutorial PUBLIC ${CMAKE_SOURCE_DIR}/lib)
Der Befehl add_executable()
funktioniert genauso wie add_library()
, außer dass stattdessen eine ausführbare Datei generiert wird. Diese ausführbare Datei kann jetzt als Ziel für Dinge wie referenziert werden target_link_libraries()
. Da tutorial.cxx Code aus der TestLib-Bibliothek verwendet, weisen Sie CMake wie gezeigt darauf hin.
Ebenso müssen alle .h-Dateien #, die von Quellen in add_executable()
diesem Verzeichnis enthalten sind, die sich nicht im selben Verzeichnis wie die Quelle befinden, irgendwie hinzugefügt werden. Wenn der target_include_directories()
Befehl lib/TestLib.h
nicht vorhanden ist, wird er beim Kompilieren des Lernprogramms nicht gefunden, sodass der gesamte lib/
Ordner zu den Include-Verzeichnissen hinzugefügt wird, in denen nach #includes gesucht werden soll. Möglicherweise wird auch der Befehl include_directories()
angezeigt, der sich auf ähnliche Weise verhält, mit der Ausnahme, dass Sie kein Ziel angeben müssen, da es für alle ausführbaren Dateien vollständig global festgelegt wird. Ich werde CMAKE_SOURCE_DIR später noch einmal erklären.
simple/src/tutorial.cxx
::
#include <stdio.h>
#include "TestLib.h"
int main (int argc, char *argv[])
{
test();
fprintf(stdout, "Main\n");
return 0;
}
Beachten Sie, wie die Datei "TestLib.h" enthalten ist. Sie müssen nicht den vollständigen Pfad angeben: CMake kümmert sich dank all dem um alles hinter den Kulissen target_include_directories()
.
Technisch gesehen, in einem einfachen Quellbaum wie das Sie ohne die tun können , CMakeLists.txt
s unter lib/
und src/
und das Hinzufügen nur so etwas wie add_executable(Tutorial src/tutorial.cxx)
zu simple/CMakeLists.txt
. Es liegt an Ihnen und den Anforderungen Ihres Projekts.
Was sollte ich noch wissen, um CMake richtig zu verwenden?
(AKA-Themen, die für Ihr Verständnis relevant sind)
Pakete finden und verwenden : Die Antwort auf diese Frage erklärt es besser als ich es jemals könnte.
Deklarieren von Variablen und Funktionen, Verwenden des Kontrollflusses usw . : In diesem Lernprogramm werden die Grundlagen des Angebots von CMake erläutert und eine gute Einführung im Allgemeinen gegeben.
CMake-Variablen : Es gibt viele. Im Folgenden finden Sie einen Crash-Kurs, der Sie auf den richtigen Weg bringt. Das CMake-Wiki ist ein guter Ort, um detailliertere Informationen zu Variablen und angeblich auch zu anderen Dingen zu erhalten.
Möglicherweise möchten Sie einige Variablen bearbeiten, ohne den Build-Baum neu zu erstellen. Verwenden Sie dazu ccmake (es bearbeitet die CMakeCache.txt
Datei). Denken c
Sie daran, eine Konfiguration vorzunehmen, wenn Sie mit den Änderungen fertig sind, und dann g
Makefiles mit der aktualisierten Konfiguration zu aktivieren.
Lesen Sie das zuvor erwähnte Tutorial , um mehr über die Verwendung von Variablen zu erfahren, aber kurz gesagt:
set(<variable name> value)
um eine Variable zu ändern oder zu erstellen.
${<variable name>}
um es zu benutzen.
CMAKE_SOURCE_DIR
: Das Stammverzeichnis der Quelle. Im vorherigen Beispiel ist dies immer gleich/simple
CMAKE_BINARY_DIR
: Das Stammverzeichnis des Builds. Im vorherigen Beispiel ist dies gleich simple/build/
, aber wenn Sie cmake simple/
von einem Ordner wie z. B. ausgeführt werden foo/bar/etc/
, werden alle Verweise auf CMAKE_BINARY_DIR
in diesem Build-Baum /foo/bar/etc
.
CMAKE_CURRENT_SOURCE_DIR
: Das Verzeichnis, in dem sich der Strom CMakeLists.txt
befindet. Dies bedeutet, dass er sich im Laufe der Zeit ändert: Drucken aus simple/CMakeLists.txt
Erträgen /simple
und Drucken aus simple/src/CMakeLists.txt
Erträgen /simple/src
.
CMAKE_CURRENT_BINARY_DIR
: Du hast die Idee. Dieser Pfad hängt nicht nur vom Ordner ab, in dem sich der Build befindet, sondern auch vom CMakeLists.txt
Speicherort des aktuellen Skripts.
Warum sind diese wichtig? Quelldateien befinden sich offensichtlich nicht im Build-Baum. Wenn Sie etwas wie target_include_directories(Tutorial PUBLIC ../lib)
im vorherigen Beispiel versuchen , ist dieser Pfad relativ zum Build-Baum, dh es ist wie beim Schreiben ${CMAKE_BINARY_DIR}/lib
, das nach innen schaut simple/build/lib/
. Es sind dort keine .h-Dateien enthalten. höchstens werden Sie finden libTestLib.a
. Du willst ${CMAKE_SOURCE_DIR}/lib
stattdessen.
CMAKE_CXX_FLAGS
: Flags, die an den Compiler weitergeleitet werden sollen, in diesem Fall an den C ++ - Compiler. Erwähnenswert ist auch, CMAKE_CXX_FLAGS_DEBUG
welche stattdessen verwendet wird, wenn CMAKE_BUILD_TYPE
DEBUG eingestellt ist. Es gibt mehr davon; Schauen Sie sich das CMake-Wiki an .
CMAKE_RUNTIME_OUTPUT_DIRECTORY
: Sagen Sie CMake, wo alle ausführbaren Dateien beim Erstellen abgelegt werden sollen. Dies ist eine globale Einstellung. Sie können es beispielsweise einstellen bin/
und alles ordentlich dort platzieren. EXECUTABLE_OUTPUT_PATH
ist ähnlich, aber veraltet, falls Sie darauf stoßen.
CMAKE_LIBRARY_OUTPUT_DIRECTORY
: Ebenso eine globale Einstellung, um CMake mitzuteilen, wo alle Bibliotheksdateien abgelegt werden sollen.
Zieleigenschaften : Sie können Eigenschaften festlegen, die nur ein Ziel betreffen, sei es eine ausführbare Datei oder eine Bibliothek (oder ein Archiv ... Sie haben die Idee). Hier ist ein gutes Beispiel für die Verwendung (mit set_target_properties()
.
Gibt es eine einfache Möglichkeit, einem Ziel automatisch Quellen hinzuzufügen? Verwenden Sie GLOB, um alles in einem bestimmten Verzeichnis unter derselben Variablen aufzulisten . Beispielsyntax ist FILE(GLOB <variable name> <directory>/*.cxx)
.
Können Sie verschiedene Build-Typen angeben? Ja, obwohl ich nicht sicher bin, wie das funktioniert oder welche Einschränkungen dies hat. Es erfordert wahrscheinlich einige if / then'ning, aber CMake bietet einige grundlegende Unterstützung, ohne etwas zu konfigurieren, wie CMAKE_CXX_FLAGS_DEBUG
zum Beispiel die Standardeinstellungen für. Sie können Ihren Build-Typ entweder CMakeLists.txt
über die Datei festlegen set(CMAKE_BUILD_TYPE <type>)
oder CMake beispielsweise über die Konsole mit den entsprechenden Flags aufrufen cmake -DCMAKE_BUILD_TYPE=Debug
.
Gibt es gute Beispiele für Projekte, die CMake verwenden? Wikipedia hat eine Liste von Open-Source-Projekten, die CMake verwenden, wenn Sie sich das ansehen möchten. Online-Tutorials waren in dieser Hinsicht bisher nichts anderes als eine Enttäuschung für mich, aber diese Frage zum Stapelüberlauf hat ein ziemlich cooles und leicht verständliches CMake-Setup. Es ist einen Blick wert.
Verwenden von Variablen aus CMake in Ihrem Code : Hier ist ein schnelles und schmutziges Beispiel (angepasst aus einem anderen Tutorial ):
simple/CMakeLists.txt
::
project (Tutorial)
# Setting variables
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 1)
# Configure_file(<input> <output>)
# Copies a file <input> to file <output> and substitutes variable values referenced in the file content.
# So you can pass some CMake variables to the source code (in this case version numbers)
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_SOURCE_DIR}/src/TutorialConfig.h"
)
simple/TutorialConfig.h.in
::
// Configured options and settings
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
Die von CMake generierte resultierende Datei simple/src/TutorialConfig.h
:
// Configured options and settings
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 1
Mit dieser cleveren Verwendung können Sie coole Dinge wie das Ausschalten einer Bibliothek und dergleichen tun. Ich empfehle, sich dieses Tutorial anzusehen , da es einige etwas fortgeschrittenere Dinge gibt, die früher oder später bei größeren Projekten sehr nützlich sein müssen.
Für alles andere steckt Stack Overflow voller spezifischer Fragen und prägnanter Antworten, was für alle außer den Uneingeweihten großartig ist.