Cmake vs machen Beispielcodes?

117

Ich habe mich gefragt, ob es einen Beispielcode für Makefiles ( make) und CMakeLists.txt( cmake) gibt, die beide dasselbe tun (der einzige Unterschied besteht darin, dass einer in makeund der andere in geschrieben ist cmake).

Ich habe versucht, nach 'cmake vs make' zu suchen, aber ich habe keine Codevergleiche gefunden. Es wäre wirklich hilfreich, die Unterschiede zu verstehen, auch wenn es sich nur um einen einfachen Fall handelt.

jlo
quelle
20
+1 Das ist eine gute Frage; Als ich anfing, cmakewollte ich das auch. Aber ich bezweifle, dass Sie es finden werden, weil die Funktionen einfach nicht so gut aufeinander abgestimmt sind. Wenn Sie versuchen, sich so zu cmakeverhalten make, werden Sie sich ernsthaft verrückt machen. Am besten einfach von vorne anfangen. Dinge, die trivial makesind, sind ziemlich involviert cmakeund umgekehrt.
Ernest Friedman-Hill
1
@ ErnestFriedman-Hill hast du mehr Details dazu? Also makeund cmakesind sie so unterschiedlich, dass sie eher als komplementäre als als konkurrierende Werkzeuge angesehen werden sollten?
Ehtesh Choudhury
2
@Shurane - cmake baut selbst nichts; Es werden Makefiles (und andere ähnliche Build-Skripte) erstellt, die Sie dann ausführen. Daher müssen Sie beim Schreiben von cmake-Dateien überlegen, ob ein Befehl während der Generierungszeit oder während der Erstellungszeit angewendet werden soll. Einige Aktionen - zum Beispiel das Kopieren eines Platzhaltersatzes von Dateien beim Erstellen - sind im Vergleich zu den Aktionen, die cp *.x $(OUTDIR)Sie in ein Makefile schreiben würden, ziemlich kompliziert . Der vielleicht nervigste Teil für mich ist, dass die generierten Makefiles von Natur aus völlig unportabel und unflexibel sind (Fortsetzung)
Ernest Friedman-Hill
1
(Fortsetzung) Sie können das Quellverzeichnis nicht einmal auf demselben Computer verschieben, ohne cmake erneut auszuführen, um das Makefile neu zu generieren! Die Wahl liegt also nicht zwischen cmake und make, sondern darin, tragbare Makefiles selbst zu schreiben oder cmake zu verwenden, um nicht portierbare auf jedem Build-Computer zu generieren (und da Sie Cygwin oder mingw unter Windows verwenden können, finde ich das erstere im Allgemeinen einfacher. )
Ernest Friedman-Hill
1
Eine nette Frage, aber es gibt keine konkrete Antwort, da beide Tools versuchen, ein anderes Problem zu lösen. cmake verwendet Informationen zum Erstellen von Programmen und generiert Makefiles, die das Programm erstellen. Daher ist cmake eine Sprache mit abstrakten Erstellungsregeln und gnu make ist eine Abhängigkeitsauflösung, die Programme auf einem gerichteten azyklischen Graphendurchlauf ausführt.
Alex

Antworten:

118

Das folgende Makefile baut eine ausführbare Datei namens progaus den Quellen prog1.c, prog2.c, prog3.c and main.c. progist gegen libmystatlib.a und libmydynlib.sodie beide auch aus der Quelle erstellt werden. Darüber hinaus progverwendet die Bibliothek libstuff.ain stuff/libund seinen Header in stuff/include. Das Makefile erstellt standardmäßig ein Release-Ziel, bietet aber auch ein Debug-Ziel:

#Makefile    
CC = gcc
CPP = g++
RANLIB = ar rcs
RELEASE = -c -O3 
DEBUG = -c -g -D_DEBUG
INCDIR = -I./stuff/include
LIBDIR = -L./stuff/lib -L.
LIBS = -lstuff -lmystatlib -lmydynlib
CFLAGS = $(RELEASE)

PROGOBJS = prog1.o prog2.o prog3.o

prog: main.o $(PROGOBJS) mystatlib mydynlib
    $(CC) main.o $(PROGOBJS) $(LIBDIR) $(LIBS) -o prog 
debug: CFLAGS=$(DEBUG)
debug: prog

mystatlib: mystatlib.o
    $(RANLIB) libmystatlib.a mystatlib.o
mydynlib: mydynlib.o
    $(CPP) -shared mydynlib.o -o libmydynlib.so

%.o: %.c
    $(CC) $(CFLAGS) $(INCDIR) $< -o $@ 
%.o: %.cpp
    $(CPP) $(CFLAGS) $(INCDIR) -fPIC  $< -o $@ 

Hier ist eine CMakeLists.txt, die (fast) genau das Gleiche tut, mit einigen Kommentaren, um die Ähnlichkeiten mit dem Makefile zu unterstreichen:

#CMakeLists.txt     
cmake_minimum_required(VERSION 2.8)                    # stuff not directly
project(example)                                       # related to building

include_directories(${CMAKE_SOURCE_DIR}/stuff/include) # -I flags for compiler
link_directories(${CMAKE_SOURCE_DIR}/stuff/lib)        # -L flags for linker

set(PROGSRC prog1.c prog2.c prog3.c)                   # define variable 

add_executable(prog main.c ${PROGSRC})                 # define executable target prog, specify sources
target_link_libraries(prog mystatlib mydynlib stuff)   # -l flags for linking prog target

add_library(mystatlib STATIC mystatlib.c)              # define static library target mystatlib, specify sources

add_library(mydynlib SHARED mydynlib.cpp)              # define shared library target mydynlib, specify sources
#extra flags for linking mydynlib
set_target_properties(mydynlib PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 
#alternatively:
#set_target_properties(mydynlib PROPERTIES COMPILE_FLAGS "-fPIC")

In diesem einfachen Beispiel sind die wichtigsten Unterschiede:

  • CMake erkennt, welche Compiler für welche Art von Quelle verwendet werden sollen. Außerdem wird für jeden Zieltyp die richtige Befehlsfolge aufgerufen. Daher gibt es keine explizite Angabe von Befehlen wie $(CC) ..., $(RANLIB) ...und so weiter.

  • Alle üblichen Compiler- / Linker-Flags, die sich mit der Aufnahme von Header-Dateien, Bibliotheken usw. befassen, werden durch plattformunabhängige / systemunabhängige Build-Befehle ersetzt.

  • Debugging-Flags werden eingeschlossen, indem die Variable entweder CMAKE_BUILD_TYPEauf "Debug" gesetzt oder beim Aufrufen des Programms an CMake übergeben wird : cmake -DCMAKE_BUILD_TYPE:STRING=Debug.

  • CMake bietet auch die plattformunabhängige Aufnahme des '-fPIC'-Flags (über die POSITION_INDEPENDENT_CODEEigenschaft) und vieler anderer. Noch dunkelere Einstellungen können in CMake genauso wie in einem Makefile von Hand implementiert werden (unter Verwendung COMPILE_FLAGS ähnlicher Eigenschaften). Natürlich beginnt CMake wirklich zu glänzen, wenn Bibliotheken von Drittanbietern (wie OpenGL) auf tragbare Weise enthalten sind.

  • Der Erstellungsprozess besteht aus einem Schritt, wenn Sie ein Makefile verwenden, nämlich der Eingabe makeüber die Befehlszeile. Für CMake gibt es zwei Schritte: Erstens müssen Sie Ihre Build-Umgebung einrichten (entweder durch Eingabe cmake <source_dir>in Ihr Build-Verzeichnis oder durch Ausführen eines GUI-Clients). Dadurch wird je nach Build-System Ihrer Wahl ein Makefile oder etwas Ähnliches erstellt (z. B. make unter Unixes oder VC ++ oder MinGW + Msys unter Windows). Das Build-System kann als Parameter an CMake übergeben werden. CMake trifft jedoch abhängig von Ihrer Systemkonfiguration angemessene Standardentscheidungen. Zweitens führen Sie den eigentlichen Build im ausgewählten Build-System durch.

Quellen und Build-Anweisungen finden Sie unter https://github.com/rhoelzel/make_cmake .

Roberto
quelle
2
Ist das Makefile nicht zu kompliziert? Durch die Verwendung CPPFLAGSanstelle von INCDIRhätte man die eingebauten Regeln verwenden können und der explizite Aufruf des Compilers wäre überflüssig gewesen. In ähnlicher Weise können die eingebauten Regeln für den Umgang mit ar auch dies abdecken. Auch warum setzen CPPund CCexplizit? Sie sind bereits auf gute Werte eingestellt make, sie sind vordefinierte Variablen. makeerkennt auch, welcher Compiler für welche Art von Quelle verwendet werden soll, die integrierten Regeln sind vielfältig.
Christian Hujer
1
Und viele dieser Variablen sollten mit :=statt zugewiesen werden =.
Christian Hujer
1
Ein Blick auf die Beschreibung cmakeist vergleichbarer automakeals make.
ivan_pozdeev
Das bereitgestellte Makefile kann auf 3/4 Zeilen reduziert werden. Sie sollten INCLUDESanstelle von angeben INCDIR. Sie benötigen keine% .o:%. C-Regeln.
Shuva
6

Besorgen Sie sich eine Software, die CMake als Buildsystem verwendet (als Beispiel stehen zahlreiche Open Source-Projekte zur Auswahl). Holen Sie sich den Quellcode und konfigurieren Sie ihn mit CMake. Lesen Sie die resultierenden Makefiles und genießen Sie.

Beachten Sie, dass diese Tools nicht eins zu eins zugeordnet werden. Der offensichtlichste Unterschied besteht darin, dass CMake nach Abhängigkeiten zwischen verschiedenen Dateien sucht (z. B. C-Header- und Quelldateien), während make dies den Makefile-Autoren überlässt.

Tadeusz A. Kadłubowski
quelle
4

Wenn es sich bei dieser Frage um ein Beispiel handelt Makefile der CMakeList.txtDatei handelt, überprüfen Sie bitte die cmake-Backend-Quellen und generieren Sie eine solche Makefile. Wenn dies nicht der Fall ist, versuche ich, die Antwort von @Roberto zu vereinfachen, indem ich die Details verstecke.

CMake-Funktion

Während Make es ein flexibles Werkzeug für Regeln und Rezepte ist, CMakeist es eine Abstraktionsebene, die auch die Konfigurationsfunktion hinzufügt.

Meine Ebene CMakeLists.txt wird wie folgt aussehen:

cmake_minimum_required(VERSION 2.8)
project(example)
file(GLOB testapp_SOURCES *.cc)
add_executable(testapp ${testapp_SOURCES})

Beachten Sie, dass sich CMakeversteckthow der Build getan werden kann. Wir haben nur whatdie Ein- und Ausgabe angegeben.

Das CMakeLists.txt enthält eine Liste von Funktionsaufrufen, die durch definiert sind cmake.

(CMake-Funktion) Vs Regeln erstellen

In Makefileder rules and recipeswerden statt verwendet functions. functionStellen Sie zusätzlich zu einer ähnlichen Funktion rules and recipeseine Verkettung bereit. Mein Minimalist Makefilesieht wie folgt aus:

-include "executable.mk"
TARGETS=testapp.bin
all:${TARGETS}

Während das executable.mkwie folgt aussehen wird,

SOURCES=$(wildcard *.cpp)
OBJECTS=$(SOURCES:.cpp=.o)
DEPS=$(SOURCES:.cpp=.d)

%.bin:$(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) $(DEPS) $(TARGETS)

-include $(DEPS)

Ausgehend von Grund auf beginne ich mit einem Makefilewie folgt:

all: testapp.bin

testapp.bin:sourcea.o sourcb.o
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) testapp.bin

Ich habe diesen Ausschnitt von hier bekommen und es modifiziert. Beachten Sie, dass dieser Datei einige implizite Regeln hinzugefügt werden, die in der Makefile-Dokumentation zu finden sind. Einige implizite Variablen sind auch hier relevant.

Beachten Sie, dass Makefiledas Detail recipezeigt , dass howder Build ausgeführt werden kann. Es ist möglich zu schreiben executable.mk, um die Details in einer Datei zu behalten. Auf diese Weise kann das Makefile reduziert werden, wie ich zuvor gezeigt habe.

Interne Variablen in CMakeundMake

Jetzt, da CMakewir ein wenig fortgeschritten sind, können wir ein Compiler-Flag wie das folgende setzen:

set(CMAKE_C_FLAGS "-Wall")

Weitere Informationen zu CMakeStandardvariablen finden Sie in der CMakeCache.txtDatei. Der CMakeobige Code entspricht dem folgenden MakeCode.

CFLAGS = -Wall

Beachten Sie, dass CFLAGSeine interne Variable Make, die gleiche Art und Weise, CMAKE_C_FLAGSist die interne Variable CMake.

Hinzufügen von Include- und Bibliothekspfad in CMake

Wir können es cmakemit Funktionen tun .

target_include_directories(testapp PRIVATE "myincludes")
list(APPEND testapp_LIBRARIES
    mytest mylibrarypath
)
target_link_libraries(testapp ${testapp_LIBRARIES})

Vs Hinzufügen von Include- und Bibliothekspfad in Make

Wir können Include- und Bibliotheken hinzufügen, indem wir Zeilen wie die folgenden hinzufügen:

INCLUDES += -Imyincludes
LIBS += -Lmylibrarypath -lmytest

Beachten Sie, dass diese obigen Zeilen mit Auto-Gen-Tools oder pkg-config generiert werden können. (obwohl Makefile nicht von Auto-Config-Tools abhängig ist)

CMake configure / tweek

Normalerweise ist es möglich, einige config.hDateien wie auto-configWerkzeuge mithilfe der configure_fileFunktion zu generieren . Es ist möglich, benutzerdefinierte Funktionen mit mehr Tricks zu schreiben. Und schließlich können wir eine Konfiguration wie die folgende auswählen:

cmake --build . --config "Release"

Mit der optionFunktion können einige konfigurierbare Optionen hinzugefügt werden.

Makefile konfigurieren / optimieren

Wenn wir es irgendwie mit einem Debug-Flag kompilieren müssen, können wir das Gleiche aufrufen make,

make CXXFLAGS=NDEBUG

Ich denke, interne Variablen, Makefile-rulesund CMake-functionssind ein guter Anfang für den Vergleich, viel Glück mit mehr Graben.

Shuva
quelle