Come viene utilizzato CMake? [chiuso]


95

È notoriamente difficile ottenere informazioni utili su CMake come principiante. Finora, ho visto alcuni tutorial su come impostare un progetto molto semplice o un altro. Tuttavia, nessuno di questi spiega il ragionamento alla base di tutto ciò che viene mostrato in essi, lasciando sempre molti buchi da riempire.

Che cosa chiede CMake su un CMakeLists significa ? Dovrebbe essere chiamato una volta per albero di build o cosa? Come posso utilizzare impostazioni diverse per ogni build se utilizzano tutti lo stesso file CMakeLists.txt dalla stessa origine? Perché ogni sottodirectory necessita del proprio file CMakeLists? Avrebbe senso usare CMake su un CMakeLists.txt diverso da quello alla radice del progetto? In caso affermativo, in quali casi? Qual è la differenza tra specificare come creare un eseguibile o una libreria dal file CMakeLists nella propria sottodirectory rispetto a farlo nel file CMakeLists alla radice di tutti i sorgenti? Posso creare un progetto per Eclipse e un altro per Visual Studio, cambiando semplicemente l' -Gopzione quando chiamo CMake? È anche così che viene utilizzato?

Nessuno dei tutorial, delle pagine di documentazione o delle domande / risposte che ho visto finora fornisce informazioni utili per capire come utilizzare CMake. Gli esempi non sono esaustivi. Non importa quali tutorial leggo, mi sento come se mi mancasse qualcosa di importante.

Ci sono molte domande poste dai neofiti di CMake come me che non lo chiedono esplicitamente, ma che rendono ovvio il fatto che, in quanto neofiti, non abbiamo idea di come affrontare CMake o cosa farne.


8
Forse dovresti invece scrivere un post sul blog.
steveire

2
Sono tentato di votare "troppo ampio" ... Questo sembra più un saggio personale su CMake che una buona misura per stackoverflow. Perché non hai inserito tutte queste domande in domande separate? E in che modo la tua domanda + risposta è diversa da molte altre, ad esempio stackoverflow.com/q/20884380/417197 , stackoverflow.com/q/4745996/417197 , stackoverflow.com/q/4506193/417197 ?
André

13
@Andre Perché questa domanda: ho passato una settimana a cercare di capire CMake e nessun tutorial che ho trovato era completo. Quei collegamenti che hai indicato sono buoni, ma per qualcuno che ha trovato CMake perché ha ricevuto un progetto con un mucchio di CMakeList all'interno, non fanno molta luce su come funziona CMake. In tutta onestà ho provato a scrivere una risposta che avrei voluto inviare indietro nel tempo a me stesso quando ho iniziato a provare a imparare CMake. E le sotto-domande hanno lo scopo di mostrare che sto effettivamente cercando di chiedere cosa dovrei sapere per non avere quei dubbi. Fare la domanda giusta è DIFFICILE.
leinaD_natipaC

2
Non una risposta, ma ti invito a dare un'occhiata a jaws.rootdirectory.de . Mentre la documentazione di CMake (e la risposta accettata ...) mostra molti piccoli frammenti di ciò che potresti fare, ho scritto una configurazione di base (assolutamente "completa" o "perfetta"), inclusi commenti in tutto, che IMHO mostra cosa CMake (e CTest e CPack e CDash ...) possono fare per te. Si costruisce fuori dagli schemi e puoi facilmente adattarlo / estenderlo alle esigenze del tuo progetto.
DevSolar

9
È un peccato che al giorno d'oggi tali domande vengano chiuse. Penso che le domande generali che richiedono tutorial come le risposte ma riguardano argomenti scarsamente / oscuramente documentati (un altro esempio sarebbe l'api Torch7 C) e le domande a livello di ricerca dovrebbero rimanere aperte. Quelli sono molto più interessanti del tipico "hey, ho un segfault quando realizzo un progetto di giocattoli" che affligge il sito.
Ash

Risposte:


137

A cosa serve CMake?

Secondo Wikipedia:

CMake è un [...] software per la gestione del processo di compilazione del software utilizzando un metodo indipendente dal compilatore. È progettato per supportare gerarchie di directory e applicazioni che dipendono da più librerie. Viene utilizzato in combinazione con ambienti di compilazione nativi come make, Xcode di Apple e Microsoft Visual Studio.

Con CMake, non è più necessario mantenere impostazioni separate specifiche per il compilatore / ambiente di compilazione. Hai una configurazione e funziona per molti ambienti.

CMake può generare una soluzione Microsoft Visual Studio, un progetto Eclipse o un labirinto di Makefile dagli stessi file senza modificare nulla in essi.

Dato un mucchio di directory con codice al loro interno, CMake gestisce tutte le dipendenze, gli ordini di compilazione e altre attività che il tuo progetto deve svolgere prima di poter essere compilato. In realtà NON compila nulla. Per usare CMake, devi dirgli (usando i file di configurazione chiamati CMakeLists.txt) quali eseguibili hai bisogno di compilare, a quali librerie si collegano, quali directory ci sono nel tuo progetto e cosa c'è dentro, così come tutti i dettagli come i flag o qualsiasi altra cosa di cui hai bisogno (CMake è abbastanza potente).

Se è impostato correttamente, puoi utilizzare CMake per creare tutti i file di cui il tuo "ambiente di compilazione nativo" preferito ha bisogno per svolgere il suo lavoro. In Linux, per impostazione predefinita, questo significa Makefile. Quindi, una volta eseguito CMake, creerà un gruppo di file per il proprio uso più alcuni Makefiles. Tutto quello che devi fare in seguito è digitare "make" nella console dalla cartella principale ogni volta che hai finito di modificare il tuo codice, e bam, viene creato un eseguibile compilato e collegato.

Come funziona CMake? Che cosa fa?

Ecco un esempio di configurazione del progetto che userò in tutto:

simple/
  CMakeLists.txt
  src/
    tutorial.cxx
    CMakeLists.txt
  lib/
    TestLib.cxx
    TestLib.h
    CMakeLists.txt
  build/

Il contenuto di ogni file viene mostrato e discusso in seguito.

CMake imposta il tuo progetto in base alla radice CMakeLists.txt del tuo progetto e lo fa in qualsiasi directory tu abbia eseguito cmakedalla console. Farlo da una cartella che non è la radice del tuo progetto produce quella che viene chiamata build out-of-source , il che significa che i file creati durante la compilazione (file obj, file lib, eseguibili, lo sai) verranno inseriti in detta cartella , tenuto separato dal codice effettivo. Aiuta a ridurre il disordine ed è preferito anche per altri motivi, che non discuterò.

Non so cosa succede se esegui cmakesu qualsiasi altro che la radice CMakeLists.txt.

In questo esempio, dato che voglio che tutto sia posizionato all'interno della build/cartella, devo prima navigare lì, quindi passare CMake alla directory in cui CMakeLists.txtrisiede la radice .

cd build
cmake ..

Per impostazione predefinita, questo imposta tutto utilizzando i Makefile come ho detto. Ecco come dovrebbe apparire la cartella build ora:

simple/build/
  CMakeCache.txt
  cmake_install.cmake
  Makefile
  CMakeFiles/
    (...)
  src/
    CMakeFiles/
      (...)
    cmake_install.cmake
    Makefile
  lib/
    CMakeFiles/
      (...)
    cmake_install.cmake
    Makefile

Cosa sono tutti questi file? L'unica cosa di cui ti devi preoccupare è il Makefile e le cartelle del progetto .

Notare le cartelle src/e lib/. Questi sono stati creati perché simple/CMakeLists.txtli punta utilizzando il comando add_subdirectory(<folder>). Questo comando dice a CMake di cercare in detta cartella un altro CMakeLists.txtfile ed eseguire quello script, quindi ogni sottodirectory aggiunta in questo modo deve contenere un CMakeLists.txtfile. In questo progetto, simple/src/CMakeLists.txtdescrive come costruire l'eseguibile effettivo e simple/lib/CMakeLists.txtdescrive come costruire la libreria. Ogni obiettivo che CMakeLists.txtviene descritto verrà posizionato per impostazione predefinita nella sua sottodirectory all'interno dell'albero di compilazione. Quindi, dopo un rapido

make

nella console eseguita da build/, vengono aggiunti alcuni file:

simple/build/
  (...)
  lib/
    libTestLib.a
    (...)
  src/
    Tutorial
    (...)

Il progetto è costruito e l'eseguibile è pronto per essere eseguito. Cosa fai se vuoi che gli eseguibili vengano messi in una cartella specifica? Imposta la variabile CMake appropriata o modifica le proprietà di un obiettivo specifico . Maggiori informazioni sulle variabili CMake più avanti.

Come dico a CMake come creare il mio progetto?

Ecco il contenuto, spiegato, di ogni file nella directory dei sorgenti:

simple/CMakeLists.txt:

cmake_minimum_required(VERSION 2.6)

project(Tutorial)

# Add all subdirectories in this project
add_subdirectory(lib)
add_subdirectory(src)

La versione minima richiesta dovrebbe essere sempre impostata, in base all'avviso che CMake lancia quando non lo fai. Usa qualunque sia la tua versione di CMake.

Il nome del tuo progetto può essere utilizzato in seguito e suggerisce il fatto che puoi gestire più di un progetto dagli stessi file CMake. Non mi addentrerò in questo, però.

Come accennato prima, add_subdirectory()aggiunge una cartella al progetto, il che significa che CMake si aspetta che abbia un CMakeLists.txtinterno, che verrà quindi eseguito prima di continuare. A proposito, se ti capita di avere una funzione CMake definita puoi usarla da altri CMakeLists.txts nelle sottodirectory, ma devi definirla prima di usarla add_subdirectory()o non la troverà. Tuttavia, CMake è più intelligente riguardo alle librerie, quindi questa è probabilmente l'unica volta in cui ti imbatterai in questo tipo di problema.

simple/lib/CMakeLists.txt:

add_library(TestLib TestLib.cxx)

Per creare la tua libreria personale, dagli un nome e poi elenca tutti i file da cui è costruita. Semplice. Se avesse bisogno di un altro file foo.cxx,, per essere compilato, dovresti invece scrivere add_library(TestLib TestLib.cxx foo.cxx). Questo funziona anche per i file in altre directory, ad esempio add_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx). Maggiori informazioni sulla variabile CMAKE_SOURCE_DIR più avanti.

Un'altra cosa che puoi fare con questo è specificare che vuoi una libreria condivisa. L'esempio: add_library(TestLib SHARED TestLib.cxx). Non temere, è qui che CMake inizia a semplificarti la vita. Che sia condivisa o meno, ora tutto ciò che devi gestire per utilizzare una libreria creata in questo modo è il nome che le hai dato qui. Il nome di questa libreria è ora TestLib e puoi fare riferimento ad esso da qualsiasi punto del progetto. CMake lo troverà.

C'è un modo migliore per elencare le dipendenze? Sicuramente si . Controlla di seguito per ulteriori informazioni su questo.

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)

Il comando add_executable()funziona esattamente allo stesso modo add_library(), tranne, ovviamente, genererà invece un eseguibile. È ora possibile fare riferimento a questo eseguibile come obiettivo per cose come target_link_libraries(). Poiché tutorial.cxx utilizza il codice trovato nella libreria TestLib, lo fai notare a CMake come mostrato.

Allo stesso modo, qualsiasi file .h #incluso da qualsiasi sorgente add_executable()che non si trova nella stessa directory della sorgente deve essere aggiunto in qualche modo. Se non fosse per il target_include_directories()comando, lib/TestLib.hnon verrebbe trovato durante la compilazione del Tutorial, quindi l'intera lib/cartella viene aggiunta alle directory di inclusione in cui cercare #includes. Potresti anche vedere il comando include_directories()che agisce in modo simile, tranne per il fatto che non è necessario che tu specifichi un obiettivo poiché lo imposta completamente a livello globale, per tutti gli eseguibili. Ancora una volta, spiegherò in seguito CMAKE_SOURCE_DIR.

simple/src/tutorial.cxx:

#include <stdio.h>
#include "TestLib.h"
int main (int argc, char *argv[])
{
  test();
  fprintf(stdout, "Main\n");
  return 0;
}

Notare come è incluso il file "TestLib.h". Non c'è bisogno di includere il percorso completo: CMake si prende cura di tutto ciò che sta dietro le quinte grazie a target_include_directories().

Tecnicamente parlando, in un semplice albero dei sorgenti come questo puoi fare a meno della CMakeLists.txts sotto lib/e src/e aggiungere semplicemente qualcosa di simile add_executable(Tutorial src/tutorial.cxx)a simple/CMakeLists.txt. Dipende da te e dalle esigenze del tuo progetto.

Cos'altro dovrei sapere per utilizzare correttamente CMake?

(Argomenti AKA rilevanti per la tua comprensione)

Trovare e utilizzare i pacchetti : la risposta a questa domanda lo spiega meglio di quanto avrei mai potuto.

Dichiarazione di variabili e funzioni, utilizzo del flusso di controllo, ecc .: Dai un'occhiata a questo tutorial che spiega le basi di ciò che CMake ha da offrire, oltre ad essere una buona introduzione in generale.

Variabili CMake : ce ne sono molte, quindi quello che segue è un corso accelerato per portarti sulla strada giusta. Il wiki di CMake è un buon posto per ottenere informazioni più approfondite sulle variabili e apparentemente anche su altre cose.

Potresti voler modificare alcune variabili senza ricostruire l'albero di compilazione. Usa ccmake per questo (modifica il CMakeCache.txtfile). Ricordarsi di configure una volta terminate le modifiche e quindi generare i makefile con la configurazione aggiornata.

Leggi il tutorial a cui si fa riferimento in precedenza per imparare a usare le variabili, ma per farla breve: set(<variable name> value)per modificare o creare una variabile. ${<variable name>}per usarlo.

  • CMAKE_SOURCE_DIR: La directory principale di source. Nell'esempio precedente, questo è sempre uguale a/simple
  • CMAKE_BINARY_DIR: La directory principale della build. Nell'esempio precedente, questo è uguale a simple/build/, ma se si esegue cmake simple/da una cartella come foo/bar/etc/, tutti i riferimenti a CMAKE_BINARY_DIRin quell'albero di compilazione diventerebbero /foo/bar/etc.
  • CMAKE_CURRENT_SOURCE_DIR: La directory in cui si trova la corrente CMakeLists.txt. Ciò significa che cambia in tutto: stampandola da simple/CMakeLists.txtyield /simplee stampandola da simple/src/CMakeLists.txtyield /simple/src.
  • CMAKE_CURRENT_BINARY_DIR: Hai avuto l'idea. Questo percorso dipenderà non solo dalla cartella in cui si trova la build, ma anche dalla posizione dello CMakeLists.txtscript corrente .

Perché sono importanti? I file sorgente ovviamente non saranno nell'albero di compilazione. Se provi qualcosa di simile target_include_directories(Tutorial PUBLIC ../lib)nell'esempio precedente, quel percorso sarà relativo all'albero di costruzione, vale a dire che sarà come scrivere ${CMAKE_BINARY_DIR}/lib, che guarderà all'interno simple/build/lib/. Non ci sono file .h lì; al massimo troverai libTestLib.a. Tu ${CMAKE_SOURCE_DIR}/libinvece vuoi .

  • CMAKE_CXX_FLAGS: Flag da passare al compilatore, in questo caso il compilatore C ++. Vale anche la pena notare CMAKE_CXX_FLAGS_DEBUGche verrà utilizzato invece se CMAKE_BUILD_TYPEè impostato su DEBUG. Ce ne sono altri come questi; controlla il wiki di CMake .
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY: Indica a CMake dove mettere tutti gli eseguibili quando vengono creati. Questa è un'ambientazione globale. Ad esempio, puoi impostarlo su bin/e posizionare tutto in modo ordinato lì. EXECUTABLE_OUTPUT_PATHè simile, ma deprecato, nel caso in cui ci si imbatta.
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY: Allo stesso modo, un'impostazione globale per dire a CMake dove mettere tutti i file di libreria.

Proprietà target : puoi impostare proprietà che interessano solo un target, sia esso un eseguibile o una libreria (o un archivio ... hai capito). Ecco un buon esempio di come usarlo (con set_target_properties().

Esiste un modo semplice per aggiungere automaticamente sorgenti a una destinazione? Usa GLOB per elencare tutto in una data directory sotto la stessa variabile. La sintassi di esempio è FILE(GLOB <variable name> <directory>/*.cxx).

Potete specificare diversi tipi di build? Sì, anche se non sono sicuro di come funzioni o dei limiti di questo. Probabilmente richiede un po 'di if / then'ning, ma CMake offre un supporto di base senza configurare nulla, come i valori predefiniti per CMAKE_CXX_FLAGS_DEBUG, ad esempio. È possibile impostare il tipo di build dall'interno del CMakeLists.txtfile tramite set(CMAKE_BUILD_TYPE <type>)o chiamando CMake dalla console con i flag appropriati, ad esempio cmake -DCMAKE_BUILD_TYPE=Debug.

Qualche buon esempio di progetti che utilizzano CMake? Wikipedia ha un elenco di progetti open source che utilizzano CMake, se vuoi esaminarlo. I tutorial online non sono stati altro che una delusione per me finora a questo proposito, tuttavia questa domanda di Stack Overflow ha una configurazione CMake piuttosto interessante e di facile comprensione. Vale la pena dare un'occhiata.

Utilizzo delle variabili di CMake nel codice : ecco un esempio rapido e sporco (adattato da qualche altro 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@

Il file risultante generato da CMake, simple/src/TutorialConfig.h:

// Configured options and settings
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 1

Con un uso intelligente di questi puoi fare cose interessanti come spegnere una libreria e simili. Consiglio di dare un'occhiata a quel tutorial in quanto ci sono alcune cose leggermente più avanzate che sono destinate ad essere molto utili su progetti più grandi, prima o poi.

Per tutto il resto, Stack Overflow è pieno di domande specifiche e risposte concise, il che è ottimo per tutti tranne che per chi non lo sapesse.


2
Accetto questa risposta, ma se qualcun altro scrive una risposta migliore e più breve, la scelgo io. Lo farei da solo, ma ho già sofferto abbastanza per CMake.
leinaD_natipaC

6
Grazie per questo fantastico lavoro. Spero che questo ottenga più voti! :)
LET

4
Il punto piuttosto sottovalutato lì dentro: con CMake, non è più necessario mantenere impostazioni separate specifiche per il compilatore / ambiente di compilazione. Hai una configurazione e funziona per (varie versioni di) Visual Studio, blocchi di codice, semplici Makefile Unix e molti altri ambienti. Questo è il motivo per cui stavo guardando CMake in primo luogo: è diventato un grosso problema mantenere sincronizzate le configurazioni Autoconf, MSVC 2005 / 32bit e MSVC 2010 / 64bit. E una volta capito, ho aggiunto molte altre cose come la generazione di documenti, i test, il packaging ecc., Tutte in modo multipiattaforma.
DevSolar

1
@DevSolar True. Mi piace quella linea più di quella di Wikipedia, quindi la aggiungerò alla risposta se non ti dispiace. Tuttavia, non mi adatterò a nulla su come utilizzare CMake correttamente in tal senso. Si tratta più che altro di essere in grado di dire cosa sta succedendo.
leinaD_natipaC

1
@RichieHH Seguendo l'esempio, per prima cosa configuri CMake per usare Makefile, poi usi CMake per generare i file che il tuo progetto deve essere costruito e infine smetti di preoccuparti di CMake perché il suo lavoro qui è finito. Quindi ti "preoccupi" del Makefile nel senso che d'ora in poi dovrai usarlo makein bash per, beh, realizzare il tuo progetto.
leinaD_natipaC
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.