Unterschied zwischen der Verwendung von Makefile und CMake zum Kompilieren des Codes

288

Ich codiere unter C / C ++ und verwende ein (GNU) Makefile, um den Code zu kompilieren. Ich kann dasselbe mit CMake machen und ein MakeFile bekommen. Was ist jedoch der Unterschied zwischen der Verwendung von Makefile und CMake zum Kompilieren des Codes?

rish
quelle
2
cmake kann auch Dateien erstellen, um ninja zu verwenden
Bићовић

Antworten:

402

Make (oder besser gesagt ein Makefile) ist ein Buildsystem - es steuert den Compiler und andere Build-Tools, um Ihren Code zu erstellen.

CMake ist ein Generator von Buildsystemen. Es kann Makefiles erzeugen, es kann Ninja-Build-Dateien erzeugen, es kann KDEvelop- oder Xcode-Projekte erzeugen, es kann Visual Studio-Lösungen erzeugen. Aus demselben Startpunkt dieselbe CMakeLists.txt-Datei. Wenn Sie also ein plattformunabhängiges Projekt haben, können Sie es mit CMake auch systemunabhängig machen.

Wenn Sie Windows-Entwickler an Visual Studio- und Unix-Entwickler gewöhnt haben, die auf GNU Make schwören, ist CMake der richtige Weg.

Ich würde immer empfehlen, CMake (oder einen anderen Buildsystem-Generator, aber CMake ist meine persönliche Präferenz) zu verwenden, wenn Sie beabsichtigen, dass Ihr Projekt plattformübergreifend oder allgemein verwendbar ist. CMake selbst bietet auch einige nette Funktionen wie Abhängigkeitserkennung, Verwaltung der Bibliotheksschnittstelle oder Integration in CTest, CDash und CPack.

Die Verwendung eines Buildsystem-Generators macht Ihr Projekt zukunftssicherer. Selbst wenn Sie jetzt nur GNU-Make sind, was ist, wenn Sie sich später für eine Erweiterung auf andere Plattformen entscheiden (sei es Windows oder etwas Eingebettetes) oder einfach nur eine IDE verwenden möchten?

Angew ist nicht mehr stolz auf SO
quelle
5
@rish Ja, das ist das Wesentliche. Beachten Sie jedoch, dass es unter Linux mehr Möglichkeiten zum Programmieren gibt als Makefiles - siehe z. B. QtCreator, KDEvelop, Ninja. Für jedes dieser Elemente wird entweder "ein Projekt erstellen und mit dem Makefile synchronisieren" oder "CMake erneut ausführen". Und wie in der Antwort erwähnt, verfügt CMake auch über andere Funktionen, z. B. die Erkennung von Abhängigkeiten (z. B. find_package()) oder die Unterstützung von Tests / Verpackungen.
Angew ist nicht mehr stolz auf SO
3
Ich habe gelesen, dass CMake keine nicht rekursiven Makefiles erstellen kann. Ist das noch wahr?
Maxim Egorushkin
1
@Angew Nicht rekursiv ist, wenn make einmal mit vollständigem Projektabhängigkeitsbaum aufgerufen wird. Im Gegensatz zu rekursiv, wenn ein Makefile der obersten Ebene Makefiles für Unterprojekte in einer bestimmten Reihenfolge aufruft.
Maxim Egorushkin
3
Dies ist eine wichtige Schwäche von CMake - GNU make hat seine Falten, aber wenn Sie sich die Zeit nehmen, es zu lernen, ist es äußerst leistungsfähig und vielseitig und funktioniert auf einer enormen Anzahl von Plattformen. Das Fehlen eines vollständigen Abhängigkeitsbaums zum Analysieren ist ein schwerwiegender Fehler. Google nur für "rekursives Make als schädlich".
Erik Alapää
1
@ ErikAlapää Ich werde den Artikel im Detail lesen, aber auf den ersten Blick - sie scheinen von rekursivem Make zu sprechen, bei dem die Rekursionstiefe datengesteuert ist (dh von der Tiefe des Quellverzeichnisses usw. abhängt). Dies ist bei CMake nicht der Fall: Die Gesamttiefe der Make-Aufrufe beträgt unabhängig von der Projektstruktur immer 3. Es ist nur so , dass einige Bits auf einen submakefile statt alles in einem delegiert werden, aber es funktioniert nicht Projektstruktur in irgendeiner Weise widerspiegeln. Außerdem sind die Submake-Dateien nicht wirklich "in sich geschlossen", sodass sie nicht unter dem Problem der Über- / Unterabhängigkeit leiden.
Angew ist nicht mehr stolz auf SO
39

Die Aussage, dass CMake ein "Build-Generator" ist, ist ein weit verbreitetes Missverständnis.

Es ist technisch nicht falsch; Es beschreibt nur, wie es funktioniert, aber nicht, was es tut.

Im Kontext der Frage machen sie dasselbe: Nehmen Sie eine Reihe von C / C ++ - Dateien und verwandeln Sie sie in eine Binärdatei.

Was ist der wahre Unterschied?

  • CMake ist viel höherrangig. Es ist auf das Kompilieren von C ++ zugeschnitten, für das Sie viel weniger Build-Code schreiben, kann aber auch für allgemeine Builds verwendet werden. makeEs gibt auch einige integrierte C / C ++ - Regeln, die jedoch meistens unbrauchbar sind.

  • CMakeführt einen zweistufigen Build durch: Es generiert ein Build-Skript auf niedriger Ebene in ninjaoder makeoder vielen anderen Generatoren, und dann führen Sie es aus. Alle Shell-Skript-Teile, in die normalerweise gestapelt wird, Makefilewerden nur in der Generierungsphase ausgeführt. Somit kann der CMakeBuild um Größenordnungen schneller sein.

  • Die Grammatik von CMakeist für externe Tools viel einfacher zu unterstützen als für make .

  • Sobald makeein Artefakt erstellt wurde, vergisst es, wie es erstellt wurde. Aus welchen Quellen wurde es erstellt, aus welchen Compiler-Flags? CMakeverfolgt es, makeüberlässt es Ihnen. Wenn eine der Bibliotheksquellen seit der vorherigen Version von entfernt wurde Makefile, makewird sie nicht neu erstellt.

  • Modern CMake(ab Version 3.something) arbeitet in Bezug auf Abhängigkeiten zwischen "Zielen". Ein Ziel ist (leider) immer noch eine einzelne Otput-Datei, kann jedoch transitive Abhängigkeiten ("öffentliche" / "Schnittstelle" in CMake-Begriffen) aufweisen. Diese transitiven Abhängigkeiten können den abhängigen Paketen ausgesetzt oder vor ihnen verborgen sein. CMakeverwaltet auch Verzeichnisse für Sie. Mit makestecken Sie auf einer Ebene von Datei zu Datei fest und verwalten Verzeichnisse von Hand.

Sie könnten etwas codieren, makeindem Sie Flag-Dateien verwenden, um die letzten beiden Lücken zu schließen, aber Sie sind allein. makeenthält eine vollständige Turing-Sprache (sogar zwei, manchmal drei, die Guile zählen ), und alle sind schrecklich.

Um ehrlich zu sein, ist es das , was CMakeund makehaben gemeinsam - ihre Sprachen sind ziemlich schrecklich:

  • Sie haben keine Typen;
  • keine Arrays, nur durch Leerzeichen getrennte Strings, die der Hölle entkommen;
  • Normalerweise übergeben Sie Argumente an Funktionen, indem Sie globale Variablen festlegen. (Dies wird im modernen CMake angegangen - Variablen können jetzt Namespaces haben; ein Ziel ist ein Namespace für seine Eigenschaften)
  • Das Verweisen auf eine undefinierte Variable wird standardmäßig stillschweigend ignoriert.

beginnen mit.

Aber CMakeSie schreiben viel weniger Codezeilen.

Victor Sergienko
quelle
1
Einige gute Informationen hier, aber eine Bemerkung ist völlig falsch: cmake hat einen LIST-Typ, da mit richtigen LIST-Funktionen, die für viele Build-System-Aufgaben entscheidend sind, ein kleiner
LösenJ
Ich würde es nicht "völlig" falsch nennen, aber danke für die Korrektur.
Victor Sergienko