Cmake vs zrobić przykładowe kody?

118

Zastanawiałem się, czy istnieje przykładowy kod dla Makefiles ( make) i CMakeLists.txt( cmake), które robią to samo (jedyną różnicą jest to, że jeden jest zapisany, makea drugi cmake).

Próbowałem poszukać „cmake vs make”, ale nigdy nie znalazłem żadnych porównań kodu. Zrozumienie różnic byłoby naprawdę pomocne, nawet w prostym przypadku.

jlo
źródło
20
+1 To dobre pytanie; kiedy zaczynałem cmake, też tego chciałem. Ale wątpię, żebyś go znalazł, ponieważ możliwości po prostu nie są dobrze odwzorowane. Jeśli spróbujesz cmakeudawać make, że doprowadzisz się do szału, poważnie. Najlepiej zacząć od zera. Rzeczy, w które są trywialne, makesą dość zaangażowane cmakei vice versa.
Ernest Friedman-Hill
1
@ ErnestFriedman-Hill, czy masz więcej szczegółów na ten temat? Więc makei cmaketak wyraźna, że należy je traktować raczej jako komplementarne, a nie konkurencyjne narzędzia?
Ehtesh Choudhury
2
@Shurane - cmake sam niczego nie buduje; tworzy Makefile (i inne, podobne skrypty budowania), które następnie uruchamiasz. Dlatego za każdym razem, gdy piszesz pliki cmake, musisz pomyśleć, czy polecenie powinno zostać zastosowane w czasie generowania, czy w czasie kompilacji. Niektóre działania - na przykład kopiowanie zestawu plików ze znakami wieloznacznymi w czasie kompilacji - są dość skomplikowane w porównaniu z " cp *.x $(OUTDIR)" zapisywanymi w Makefile. Być może najbardziej irytujące dla mnie jest to, że generowane pliki Makefile są z założenia całkowicie nieprzenośne i nieelastyczne (ciąg dalszy)
Ernest Friedman-Hill
1
(ciąg dalszy) Nie możesz nawet przenieść katalogu źródłowego na tę samą maszynę bez ponownego uruchomienia cmake w celu ponownego wygenerowania pliku Makefile! Tak więc wybór nie polega między cmake a make, ale raczej między samodzielnym pisaniem przenośnych plików Makefile lub używaniem cmake do generowania nieprzenoszalnych plików na każdej maszynie budującej (a biorąc pod uwagę, że możesz używać Cygwin lub mingw w systemie Windows, ogólnie uważam, że ten pierwszy jest łatwiejszy. )
Ernest Friedman-Hill
1
ładne pytanie, ale nie ma konkretnej odpowiedzi, ponieważ oba narzędzia próbują rozwiązać inny problem. cmake zbiera informacje o tym, jak budować programy, generuje pliki makefile, które budują program. Stąd cmake jest językiem z abstrakcyjnymi regułami budowania, a gnu make to zależność rozwiązująca, która wykonuje programy na skierowanym acyklicznym przemierzaniu grafów.
Alex

Odpowiedzi:

118

Poniższy plik Makefile tworzy plik wykonywalny nazwany progna podstawie źródeł prog1.c, prog2.c, prog3.c and main.c. progjest powiązany libmystatlib.a i libmydynlib.sooba są również zbudowane ze źródła. Dodatkowo progwykorzystuje bibliotekę libstuff.aw stuff/libi jego nagłówek stuff/include. Plik Makefile domyślnie buduje cel wydania, ale oferuje również cel debugowania:

#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 $@ 

Oto plik, CMakeLists.txtktóry robi (prawie) dokładnie to samo, z kilkoma komentarzami podkreślającymi podobieństwa do pliku Makefile:

#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")

W tym prostym przykładzie najważniejsze różnice to:

  • CMake rozpoznaje, których kompilatorów użyć dla jakiego rodzaju źródła. Ponadto wywołuje odpowiednią sekwencję poleceń dla każdego typu celu. Dlatego też, nie ma wyraźnego określenie poleceń, takich jak $(CC) ..., $(RANLIB) ...i tak dalej.

  • Wszystkie typowe flagi kompilatora / konsolidatora dotyczące dołączania plików nagłówkowych, bibliotek itp. Są zastępowane przez polecenia niezależne od platformy / niezależne od systemu kompilacji.

  • Debugowanie flagi są włączone albo przez ustawienie zmiennej CMAKE_BUILD_TYPEdo „Debug”, lub przekazując go do CMake podczas wywoływania programu: cmake -DCMAKE_BUILD_TYPE:STRING=Debug.

  • CMake oferuje również niezależne od platformy włączenie flagi „-fPIC” (poprzez POSITION_INDEPENDENT_CODEwłaściwość) i wielu innych. Mimo to, bardziej niejasne ustawienia można zaimplementować ręcznie w CMake, tak samo dobrze jak w Makefile (używając COMPILE_FLAGS i podobnych właściwości). Oczywiście CMake naprawdę zaczyna świecić, gdy biblioteki innych firm (takie jak OpenGL) są dołączane w sposób przenośny.

  • Jeśli używasz pliku Makefile, proces budowania ma jeden krok, a mianowicie wpisywanie makew wierszu poleceń. W przypadku CMake są dwa kroki: Najpierw musisz skonfigurować środowisko kompilacji (wpisując cmake <source_dir>katalog kompilacji lub uruchamiając klienta GUI). Tworzy to Makefile lub coś równoważnego, w zależności od wybranego systemu budowania (np. Make na Unixes lub VC ++ lub MinGW + Msys w Windows). System kompilacji można przekazać do CMake jako parametr; jednak CMake dokonuje rozsądnych domyślnych wyborów w zależności od konfiguracji systemu. Po drugie, wykonujesz rzeczywistą kompilację w wybranym systemie kompilacji.

Źródła i instrukcje kompilacji są dostępne pod adresem https://github.com/rhoelzel/make_cmake .

Roberto
źródło
3
Czy Makefile nie jest zbyt skomplikowany? Używając CPPFLAGSzamiast INCDIRjednego, można by użyć wbudowanych reguł, a jawne wywołanie kompilatora byłoby zbędne. Podobnie w przypadku obsługi ar, wbudowane reguły mogą to również obejmować. Poza tym, po co ustawiać CPPi CCjawnie? Są już ustawione na dobre wartości przez make, są predefiniowanymi zmiennymi. makerozpoznaje również, którego kompilatora użyć dla jakiego rodzaju źródła, wbudowanych reguł jest wiele.
Christian Hujer,
1
I wielu z tych zmiennych powinno być przypisanych :=zamiast =.
Christian Hujer,
1
Patrząc na opisie cmakejest bardziej porównywalna automakeniż make.
ivan_pozdeev
Dostarczony plik Makefile można zmniejszyć do 3/4 linii. POWINIENEŚ określić INCLUDESzamiast INCDIR. Nie potrzebujesz reguł% .o:%. C.
shuva
6

Pobierz oprogramowanie, które używa CMake jako systemu kompilacji (na przykład jest wiele projektów open source do wyboru). Pobierz kod źródłowy i skonfiguruj go za pomocą CMake. Przeczytaj wynikowe pliki makefile i ciesz się.

Należy pamiętać, że te narzędzia nie mapują jeden do jednego. Najbardziej oczywistą różnicą jest to, że CMake skanuje zależności między różnymi plikami (np. Nagłówkiem C i plikami źródłowymi), podczas gdy make pozostawia to autorom makefile.

Tadeusz A. Kadłubowski
źródło
4

Jeśli to pytanie dotyczy przykładowego Makefilewyjścia CMakeList.txtpliku, sprawdź źródła zaplecza cmake i wygeneruj takie Makefile. Jeśli nie, to dodając do odpowiedzi @Roberto, staram się to uprościć, ukrywając szczegóły.

Funkcja CMake

Chociaż Makejest elastycznym narzędziem do reguł i receptur, CMakejest warstwą abstrakcji, która dodaje również funkcję konfiguracji.

Moja równina CMakeLists.txtbędzie wyglądać następująco,

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

Zauważ, że CMakeukrywanie howkompilacji można zrobić. Podaliśmy tylko whatdane wejściowe i wyjściowe.

CMakeLists.txtZawiera listę funkcji oraz połączeń, które są zdefiniowane przez cmake.

(Funkcja CMake) Vs Tworzenie reguł

W stosowane są zamiast . Oprócz funkcji podobnej do podobnej, zapewnij tworzenie łańcuchów. Mój minimalistyczny będzie wyglądał następująco,Makefilerules and recipesfunctionsfunctionrules and recipesMakefile

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

Podczas gdy executable.mkwygląd będzie wyglądał następująco,

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)

Zaczynając od zera, zacznę od czegoś Makefilepodobnego:

all: testapp.bin

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

.PHONY: all clean

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

Mam stąd ten fragment i zmodyfikowałem go. Zauważ, że do tego pliku dodane są pewne niejawne reguły, które można znaleźć w dokumentacji makefile. Niektóre niejawne zmienne są tutaj również istotne.

Zauważ, że Makefilezawiera szczegóły recipepokazujące, howże kompilacja może być wykonana. Istnieje możliwość zapisania executable.mkszczegółów w jednym pliku. W ten sposób makefile można zmniejszyć, jak pokazałem wcześniej.

Zmienne wewnętrzne w CMakeiMake

Trochę bardziej zaawansowany, CMakemożemy ustawić flagę kompilatora, jak poniżej,

set(CMAKE_C_FLAGS "-Wall")

Dowiedz się więcej o CMakedomyślnych zmiennych w CMakeCache.txtpliku. Powyższy CMakekod będzie równoważny z Makekodem poniżej,

CFLAGS = -Wall

Zauważ, że CFLAGSjest to zmienna wewnętrzna w Make, w ten sam sposób, CMAKE_C_FLAGSjest zmienną wewnętrzną w CMake.

dodawanie dołączeń i ścieżek do biblioteki w CMake

Możemy to zrobić cmakeużywając funkcji.

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

Vs dodając dołączenie i ścieżkę do biblioteki w Make

Możemy dodawać dołączenia i biblioteki, dodając wiersze takie jak poniżej,

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

Zauważ, że powyższe wiersze mogą być wygenerowane z narzędzi auto-gen lub pkg-config. (chociaż Makefile nie jest zależny od narzędzi do automatycznej konfiguracji)

Konfiguracja / tweek CMake

Zwykle możliwe jest wygenerowanie jakiegoś config.hpliku, tak jak auto-confignarzędzia, przy użyciu configure_filefunkcji. Możliwe jest wykonanie większej liczby sztuczek, pisząc funkcje niestandardowe. Na koniec możemy wybrać konfigurację taką jak poniżej,

cmake --build . --config "Release"

Za pomocą optionfunkcji można dodać konfigurowalną opcję .

Konfiguracja / poprawianie pliku Makefile

Jeśli w jakiś sposób musimy go skompilować z jakąś flagą debugowania, możemy wywołać coś makepodobnego,

make CXXFLAGS=NDEBUG

Myślę zmiennych wewnętrznych, Makefile-rulesa CMake-functionsto dobry początek dla porównania, powodzenia więcej kopania.

shuva
źródło