Cmake vs creare codici di esempio?


118

Mi chiedevo se ci fosse un codice di esempio per Makefiles ( make) e CMakeLists.txt( cmake) che entrambi fanno la stessa cosa (l'unica differenza è che uno è scritto makee l'altro in cmake).

Ho provato a cercare "cmake vs make", ma non ho mai trovato alcun confronto di codice. Sarebbe davvero utile capire le differenze, anche solo per un semplice caso.


20
+1 Questa è una buona domanda; quando ho cominciato cmakevolevo anche questo. Ma dubito che lo troverai perché le capacità semplicemente non mappano bene l'una con l'altra. Se provi a cmakecomportarti come se makefossi impazzito, sul serio. Meglio solo per iniziare da zero. Le cose in cui sono banali makesono piuttosto coinvolte cmake, e viceversa.
Ernest Friedman-Hill

1
@ ErnestFriedman-Hill hai maggiori dettagli in merito? Quindi, makee cmakesono così distinti che dovrebbero essere visti più come strumenti complementari anziché concorrenti?
Ehtesh Choudhury

2
@Shurane - cmake non crea nulla da solo; crea Makefile (e altri script di build simili), che poi esegui. Pertanto, ogni volta che scrivi file cmake, devi pensare se un comando deve essere applicato durante il tempo di generazione o durante il tempo di compilazione. Alcune azioni, ad esempio copiare un insieme di file con caratteri jolly in fase di compilazione, sono piuttosto complicate rispetto a " cp *.x $(OUTDIR)" che scriveresti in un Makefile. Forse la parte più fastidiosa per me è che i Makefile generati sono per progettazione completamente non portatili e inflessibili (continua)
Ernest Friedman-Hill

1
(continua) Non puoi nemmeno spostare la directory dei sorgenti sulla stessa macchina senza rieseguire cmake per rigenerare il Makefile! Quindi la scelta non è tra cmake e make, ma piuttosto tra scrivere Makefile portatili da soli o usare cmake per generarne di non portatili su ogni build machine (e dato che puoi usare Cygwin o mingw su Windows, generalmente trovo che il primo sia più semplice. )
Ernest Friedman-Hill

1
una bella domanda, ma non c'è una risposta specifica, perché entrambi gli strumenti cercano di risolvere un problema diverso. cmake prende le informazioni su come costruire programmi genera makefile che compilano il programma. Quindi cmake è un linguaggio con regole di compilazione astratte e gnu make è una risoluzione delle dipendenze che esegue programmi su un attraversamento aciclico diretto del grafo.
Alex

Risposte:


118

Il seguente Makefile crea un eseguibile denominato progdai sorgenti prog1.c, prog2.c, prog3.c and main.c. progè collegato libmystatlib.a e libmydynlib.soche sono entrambi costruiti anche dal sorgente. Inoltre, progutilizza la libreria libstuff.ain stuff/libe la sua intestazione in stuff/include. Il Makefile crea di default un target di rilascio, ma offre anche un target di debug:

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

Eccone uno CMakeLists.txtche fa (quasi) esattamente lo stesso, con alcuni commenti per sottolineare le somiglianze con il 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")

In questo semplice esempio, le differenze più importanti sono:

  • CMake riconosce quali compilatori utilizzare per quale tipo di sorgente. Inoltre, richiama la giusta sequenza di comandi per ogni tipo di target. Pertanto, non v'è alcuna specificazione esplicita di comandi come $(CC) ..., $(RANLIB) ...e così via.

  • Tutti i normali flag del compilatore / linker che si occupano dell'inclusione di file header, librerie, ecc. Sono sostituiti da comandi indipendenti dalla piattaforma / build indipendenti dal sistema.

  • Flag di debug sono compresi impostando uno la variabile CMAKE_BUILD_TYPEa "Debug", o passando a CMake quando si richiama il programma: cmake -DCMAKE_BUILD_TYPE:STRING=Debug.

  • CMake offre anche l'inclusione indipendente dalla piattaforma del flag '-fPIC' (tramite la POSITION_INDEPENDENT_CODEproprietà) e molti altri. Tuttavia, impostazioni più oscure possono essere implementate manualmente in CMake così come in un Makefile (utilizzando COMPILE_FLAGS e proprietà simili). Ovviamente CMake inizia davvero a brillare quando le librerie di terze parti (come OpenGL) vengono incluse in modo portatile.

  • Il processo di compilazione prevede un passaggio se si utilizza un Makefile, ovvero la digitazione makedalla riga di comando. Per CMake, ci sono due passaggi: in primo luogo, è necessario configurare l'ambiente di compilazione (digitando cmake <source_dir>nella directory di compilazione o eseguendo un client GUI). Questo crea un Makefile o qualcosa di equivalente, a seconda del sistema di compilazione scelto (es. Make su Unix o VC ++ o MinGW + Msys su Windows). Il sistema di compilazione può essere passato a CMake come parametro; tuttavia, CMake effettua scelte predefinite ragionevoli a seconda della configurazione del sistema. In secondo luogo, si esegue la compilazione effettiva nel sistema di compilazione selezionato.

Fonti e istruzioni per la creazione sono disponibili su https://github.com/rhoelzel/make_cmake .


3
Il Makefile non è eccessivamente complicato? Usando CPPFLAGSinvece di INCDIRuno si sarebbero potute usare le regole integrate e l'invocazione esplicita del compilatore sarebbe stata ridondante. Allo stesso modo per la gestione di ar, le regole integrate possono coprire anche questo. Inoltre, perché impostare CPPed CCesplicitamente? Sono già impostati su valori buoni da make, sono variabili predefinite. makericonosce anche quale compilatore usare per quale tipo di sorgente, le regole integrate sono molte.
Christian Hujer

1
E molte di queste variabili dovrebbero essere assegnate con :=invece di =.
Christian Hujer

1
Guardando la descrizione, cmakeè più paragonabile a automakequello make.
ivan_pozdeev

Il Makefile fornito potrebbe essere ridotto a 3/4 righe. Dovresti specificare INCLUDESinvece di INCDIR. Non hai bisogno delle regole% .o:%. C.
shuva

6

Prendi un software che utilizza CMake come sistema di compilazione (ci sono molti progetti opensource tra cui scegliere come esempio). Ottieni il codice sorgente e configuralo utilizzando CMake. Leggi i makefile risultanti e divertiti.

Una cosa da tenere presente che questi strumenti non mappano uno a uno. La differenza più ovvia è che CMake esegue la scansione per le dipendenze tra file diversi (ad esempio, intestazione C e file sorgente), mentre make lo lascia agli autori del makefile.


4

Se questa domanda riguarda un Makefileoutput di esempio del CMakeList.txtfile, controlla i sorgenti di cmake-backend e generane uno di questi Makefile. Se non è poi aggiungendo alla risposta di @Roberto sto cercando di renderlo semplice nascondendo i dettagli.

Funzione CMake

Sebbene Makesia uno strumento flessibile per regole e ricette, CMakeè uno strato di astrazione che aggiunge anche la funzionalità di configurazione.

La mia pianura CMakeLists.txtsarà come la seguente,

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

Nota, che CMakenasconde howla build può essere fatto. Abbiamo solo specificato whatè l'input e l'output.

L' CMakeLists.txtelenco contiene le chiamate di funzione definite da cmake.

(Funzione CMake) Vs Crea regole

MakefileNel rules and recipesvengono utilizzati invece functions. Oltre alla functionfunzionalità simile, rules and recipesfornire concatenamento. Il mio minimalista Makefilesarà simile al seguente,

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

Mentre executable.mkapparirà come il seguente,

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)

Partendo da zero inizierò con un Makefilesimile al seguente,

all: testapp.bin

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

.PHONY: all clean

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

Ho preso questo snippet da qui e l'ho modificato. Nota che alcune regole implicite vengono aggiunte a questo file che può essere trovato nella documentazione del makefile. Alcune variabili implicite sono rilevanti anche qui.

Nota, questo Makefilefornisce i dettagli che recipemostrano che howla build può essere eseguita. È possibile scrivere executable.mkper mantenere i dettagli definiti in un file. In questo modo il makefile può essere ridotto come ho mostrato in precedenza.

Variabili interne in CMakeeMake

Ora diventando un po 'avanzato, CMakepossiamo impostare un flag del compilatore come il seguente,

set(CMAKE_C_FLAGS "-Wall")

Ulteriori informazioni sulle CMakevariabili predefinite nel CMakeCache.txtfile. Il CMakecodice sopra sarà equivalente al Makecodice sotto,

CFLAGS = -Wall

Nota che CFLAGSè una variabile interna in Make, allo stesso modo, CMAKE_C_FLAGSè una variabile interna in CMake.

aggiunta di inclusione e percorso della libreria in CMake

Possiamo farlo cmakeusando le funzioni.

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

Rispetto all'aggiunta di include e percorso della libreria in Make

Possiamo aggiungere include e librerie aggiungendo linee come le seguenti,

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

Nota che queste righe sopra possono essere generate da strumenti auto-gen o pkg-config. (sebbene Makefile non dipenda dagli strumenti di configurazione automatica)

CMake configure / tweek

Normalmente è possibile generare alcuni config.hfile proprio come gli auto-configstrumenti utilizzando la configure_filefunzione. È possibile fare più trucchi scrivendo funzioni personalizzate. E finalmente possiamo selezionare una configurazione come la seguente,

cmake --build . --config "Release"

È possibile aggiungere alcune opzioni configurabili utilizzando la optionfunzione.

Makefile configurare / modificare

Se in qualche modo abbiamo bisogno di compilarlo con qualche flag di debug, possiamo invocare il makesimile,

make CXXFLAGS=NDEBUG

Penso variabili interne Makefile-rulese CMake-functionssono un buon inizio per il confronto, buona fortuna con ulteriori scavi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.