Jak jest używany CMake? [Zamknięte]

100

Jako nowicjuszowi niezwykle trudno jest uzyskać przydatne informacje na temat CMake. Jak dotąd widziałem kilka samouczków na temat konfigurowania bardzo podstawowego projektu lub innego. Jednak żadna z nich nie wyjaśnia przyczyny czegokolwiek , co jest w nich pokazane, zawsze pozostawiając wiele dziur do wypełnienia.

Co robi nazywając CMake a CMakeLists na myśli ? Czy ma być wywoływany raz na drzewo kompilacji, czy co? Jak używać różnych ustawień dla każdej kompilacji, jeśli wszystkie używają tego samego pliku CMakeLists.txt z tego samego źródła? Dlaczego każdy podkatalog potrzebuje własnego pliku CMakeLists? Czy miałoby sens użycie CMake na pliku CMakeLists.txt innym niż ten w katalogu głównym projektu? Jeśli tak, w jakich przypadkach? Jaka jest różnica między określeniem sposobu tworzenia pliku wykonywalnego lub biblioteki z pliku CMakeLists w ich własnym podkatalogu a zrobieniem tego w pliku CMakeLists w katalogu głównym wszystkich źródeł? Czy mogę utworzyć projekt dla Eclipse i inny dla programu Visual Studio, po prostu zmieniając -Gopcję podczas wywoływania CMake? Czy w ogóle tak jest używany?

Żaden z samouczków, stron dokumentacji ani pytań / odpowiedzi, które widziałem do tej pory, nie daje żadnego użytecznego wglądu w zrozumienie, jak używać CMake. Przykłady po prostu nie są dokładne. Bez względu na to, jakie tutoriale czytam, czuję, że brakuje mi czegoś ważnego.

Jest wiele pytań zadawanych przez początkujących użytkowników CMake, takich jak ja, które nie zadają tego wprost, ale pokazują to, że jako nowicjusze nie mamy pojęcia, jak radzić sobie z CMake i co z tym zrobić.

leinaD_natipaC
źródło
8
Może zamiast tego powinieneś napisać post na blogu.
steveire
2
Kuszę się, by zagłosować za „zbyt szerokim”… To bardziej przypomina osobisty esej na temat CMake niż dobre dopasowanie do przepełnienia stosu. Dlaczego nie umieściłeś wszystkich tych pytań w osobnych pytaniach? A czym Twoje pytanie + odpowiedź różni się od wielu innych, np. Stackoverflow.com/q/20884380/417197 , stackoverflow.com/q/4745996/417197 , stackoverflow.com/q/4506193/417197 ?
André
14
@Andre Dlaczego to pytanie: Spędziłem tydzień próbując zrozumieć CMake i żaden samouczek, który znalazłem, nie był kompletny. Te linki, które wskazałeś, są dobre, ale dla kogoś, kto znalazł CMake, ponieważ został do niego rzucony projekt z kilkoma CMakeLists w środku, nie rzucają one zbyt wiele światła na to, jak działa CMake. Szczerze mówiąc, starałem się napisać odpowiedź, którą chciałbym wysłać do siebie w czasie, kiedy zacząłem uczyć się CMake. A pytania podrzędne mają pokazać, że tak naprawdę próbuję zapytać, co powinienem wiedzieć, aby nie mieć tych wątpliwości. Zadanie właściwego pytania jest TRUDNE.
leinaD_natipaC
2
Nie jest to odpowiedź, ale zapraszam do zajrzenia na jaws.rootdirectory.de . Natomiast dokumentation CMake (i zaakceptowanych odpowiedź ...) pokazuje wiele zgryz wielkości fragmentów, co mogłoby zrobić, napisałem do góry (bynajmniej nie „pełną” lub „doskonałe”) podstawową konfigurację, w tym komentarze przez cały czas, że IMHO pokazuje, co CMake (oraz CTest, CPack i CDash ...) może dla Ciebie zrobić. Tworzy się od razu po wyjęciu z pudełka i można go łatwo dostosować / rozszerzyć do potrzeb projektu.
DevSolar
10
Szkoda, że ​​takie pytania są dziś zamknięte. Myślę, że obszerne pytania, które wymagają samouczków, takich jak odpowiedzi, ale dotyczą słabo / niejasno udokumentowanych tematów (innym przykładem może być interfejs Torch7 C api) i pytań na poziomie badawczym, powinny pozostać otwarte. Są o wiele bardziej interesujące niż typowe „hej, mam awarię podczas robienia jakiegoś projektu zabawki”, które nęka witrynę.
Ash

Odpowiedzi:

144

Do czego służy CMake?

Według Wikipedii:

CMake to [...] oprogramowanie do zarządzania procesem tworzenia oprogramowania przy użyciu metody niezależnej od kompilatora. Jest przeznaczony do obsługi hierarchii katalogów i aplikacji zależnych od wielu bibliotek. Jest używany w połączeniu z natywnymi środowiskami kompilacji, takimi jak make, Xcode firmy Apple i Microsoft Visual Studio.

Dzięki CMake nie musisz już utrzymywać oddzielnych ustawień specyficznych dla Twojego środowiska kompilatora / kompilacji. Masz jedną konfigurację, która działa w wielu środowiskach.

CMake może wygenerować rozwiązanie Microsoft Visual Studio, projekt Eclipse lub labirynt Makefile z tych samych plików, nie zmieniając niczego w nich.

Mając kilka katalogów z kodem, CMake zarządza wszystkimi zależnościami, buduje zamówienia i innymi zadaniami, które wymaga wykonania projektu, zanim będzie można go skompilować. W rzeczywistości niczego nie kompiluje. Aby użyć CMake, musisz mu powiedzieć (używając plików konfiguracyjnych o nazwie CMakeLists.txt), jakie pliki wykonywalne potrzebujesz skompilować, do jakich bibliotek prowadzą linki, jakie katalogi są w twoim projekcie i co jest w nich, a także wszelkie szczegóły, takie jak flagi lub cokolwiek innego, czego potrzebujesz (CMake jest dość potężny).

Jeśli jest to poprawnie skonfigurowane, możesz użyć CMake do utworzenia wszystkich plików, których wybrane przez „natywne środowisko budowania” potrzebuje do wykonania swojej pracy. W Linuksie domyślnie oznacza to Makefiles. Więc po uruchomieniu CMake utworzy kilka plików do własnego użytku oraz kilka plików Makefile. Wszystko, co musisz potem zrobić, to wpisać "make" w konsoli z folderu głównego za każdym razem, gdy skończysz edytować swój kod, i bam, tworzony jest skompilowany i połączony plik wykonywalny.

Jak działa CMake? Co to robi?

Oto przykładowa konfiguracja projektu, której będę używać przez cały czas:

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

Zawartość każdego pliku jest pokazana i omówiona później.

CMake ustawia twój projekt zgodnie z katalogiem głównym CMakeLists.txt twojego projektu i robi to w dowolnym katalogu, z którego wykonałeś cmakew konsoli. Wykonanie tego z folderu, który nie jest katalogiem głównym twojego projektu, tworzy coś, co nazywa się kompilacją poza źródłem , co oznacza, że ​​pliki utworzone podczas kompilacji (pliki obj, pliki lib, pliki wykonywalne, wiesz) zostaną umieszczone we wspomnianym folderze , oddzielone od rzeczywistego kodu. Pomaga zmniejszyć bałagan i jest preferowany również z innych powodów, których nie będę omawiać.

Nie wiem, co się stanie, jeśli wykonasz cmakena innym niż root CMakeLists.txt.

W tym przykładzie, ponieważ chcę, aby wszystko zostało umieszczone w build/folderze, najpierw muszę tam przejść, a następnie przekazać CMake katalog, w którym CMakeLists.txtznajduje się katalog główny .

cd build
cmake ..

Domyślnie to ustawia wszystko przy użyciu Makefiles, jak powiedziałem. Oto, jak powinien teraz wyglądać folder kompilacji:

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

Co to za wszystkie te pliki? Jedyne, o co musisz się martwić, to plik Makefile i foldery projektu .

Zwróć uwagę na foldery src/i lib/. Zostały one utworzone, ponieważ simple/CMakeLists.txtwskazuje na nie za pomocą polecenia add_subdirectory(<folder>). To polecenie mówi CMake, aby poszukał we wspomnianym folderze innego CMakeLists.txtpliku i wykonał ten skrypt, więc każdy dodany w ten sposób podkatalog musi zawierać CMakeLists.txtplik. W tym projekcie simple/src/CMakeLists.txtopisano, jak zbudować rzeczywisty plik wykonywalny i simple/lib/CMakeLists.txtopisano, jak zbudować bibliotekę. Każdy cel, który CMakeLists.txtopisuje, zostanie domyślnie umieszczony w swoim podkatalogu w drzewie budowania. Więc po krótkiej chwili

make

w konsoli zrobionej z build/, dodawane są pliki:

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

Projekt jest zbudowany, a plik wykonywalny gotowy do wykonania. Co robisz, jeśli chcesz, aby pliki wykonywalne były umieszczone w określonym folderze? Ustaw odpowiednią zmienną CMake lub zmień właściwości określonego celu . Więcej o zmiennych CMake później.

Jak powiedzieć CMake, jak zbudować projekt?

Oto wyjaśniona zawartość każdego pliku w katalogu źródłowym:

simple/CMakeLists.txt:

cmake_minimum_required(VERSION 2.6)

project(Tutorial)

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

Minimalna wymagana wersja powinna być zawsze ustawiona, zgodnie z ostrzeżeniem wyświetlanym przez CMake, gdy nie. Użyj dowolnej wersji CMake.

Nazwa projektu może być użyta później i wskazuje, że możesz zarządzać więcej niż jednym projektem z tych samych plików CMake. Jednak nie będę się w to zagłębiał.

Jak wspomniano wcześniej, add_subdirectory()dodaje folder do projektu, co oznacza, że ​​CMake oczekuje, że będzie on zawierał element CMakeLists.txtwewnątrz, który następnie uruchomi przed kontynuowaniem. Nawiasem mówiąc, jeśli zdarzy ci się mieć zdefiniowaną funkcję CMake, możesz jej użyć z innych CMakeLists.txtkatalogów w podkatalogach, ale musisz ją zdefiniować przed użyciem, add_subdirectory()inaczej jej nie znajdzie. CMake jest jednak mądrzejszy w zakresie bibliotek, więc jest to prawdopodobnie jedyny raz, kiedy napotkasz tego rodzaju problem.

simple/lib/CMakeLists.txt:

add_library(TestLib TestLib.cxx)

Aby stworzyć własną bibliotekę, nadaj jej nazwę, a następnie wyświetl listę wszystkich plików, z których została zbudowana. Bezpośredni. Gdyby potrzebował innego pliku foo.cxxdo skompilowania, zamiast tego mógłbyś napisać add_library(TestLib TestLib.cxx foo.cxx). Działa to również na przykład w przypadku plików w innych katalogach add_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx). Więcej o zmiennej CMAKE_SOURCE_DIR później.

Inną rzeczą, którą możesz z tym zrobić, jest określenie, że chcesz udostępnić bibliotekę. Przykład: add_library(TestLib SHARED TestLib.cxx). Nie bój się, tutaj CMake zaczyna ułatwiać Ci życie. Niezależnie od tego, czy jest udostępniana, czy nie, teraz wszystko, co musisz zrobić, aby użyć utworzonej w ten sposób biblioteki, to nazwa, którą jej tu nadałeś. Nazwa tej biblioteki to teraz TestLib i możesz odwoływać się do niej z dowolnego miejsca w projekcie. CMake to znajdzie.

Czy jest lepszy sposób na wyszczególnienie zależności? Zdecydowanie tak . Sprawdź poniżej, aby uzyskać więcej informacji na ten temat.

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)

Polecenie add_executable()działa dokładnie tak samo, jak add_library(), oczywiście, zamiast tego wygeneruje plik wykonywalny. Ten plik wykonywalny może być teraz przywoływany jako cel dla rzeczy takich jak target_link_libraries(). Ponieważ tutorial.cxx używa kodu znalezionego w bibliotece TestLib, należy wskazać to CMake, jak pokazano.

Podobnie, wszystkie pliki .h zawarte w źródłach add_executable(), które nie znajdują się w tym samym katalogu, co źródło, muszą zostać w jakiś sposób dodane. Gdyby nie target_include_directories()polecenie, lib/TestLib.hnie zostałoby znalezione podczas kompilowania samouczka, więc cały lib/folder jest dodawany do katalogów include, w celu wyszukania #includes. Możesz również zobaczyć polecenie, include_directories()które działa w podobny sposób, z wyjątkiem tego, że nie wymaga określania celu, ponieważ wprost ustawia go globalnie dla wszystkich plików wykonywalnych. Jeszcze raz wyjaśnię później 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;
}

Zwróć uwagę, jak dołączono plik „TestLib.h”. Nie ma potrzeby uwzględniania pełnej ścieżki: CMake zajmuje się tym wszystkim za kulisami dzięki target_include_directories().

Technicznie rzecz biorąc, w drzewie źródłowym proste jak to można zrobić bez CMakeLists.txts pod lib/a src/i po prostu dodać coś add_executable(Tutorial src/tutorial.cxx)do simple/CMakeLists.txt. To zależy od Ciebie i potrzeb Twojego projektu.

Co jeszcze powinienem wiedzieć, aby prawidłowo używać CMake?

(Tematy AKA istotne dla twojego rozumienia)

Znajdowanie i używanie pakietów : odpowiedź na to pytanie wyjaśnia to lepiej niż kiedykolwiek.

Deklarowanie zmiennych i funkcji, używanie przepływu sterowania itp . : zapoznaj się z tym samouczkiem, który wyjaśnia podstawy tego, co ma do zaoferowania CMake, a także jest dobrym wprowadzeniem w ogóle.

Zmienne CMake : jest ich wiele, więc poniżej znajduje się szybki kurs, który poprowadzi Cię na właściwy tor. Wiki CMake to dobre miejsce, aby uzyskać bardziej szczegółowe informacje na temat zmiennych, a także rzekomo innych rzeczy.

Możesz chcieć edytować niektóre zmienne bez przebudowywania drzewa budowania. Użyj do tego ccmake (edytuje CMakeCache.txtplik). Pamiętaj, aby skonfigurować cpo wprowadzeniu zmian, a następnie gwygenerować pliki makefile ze zaktualizowaną konfiguracją.

Przeczytaj poprzednio przywoływany samouczek, aby dowiedzieć się o używaniu zmiennych, ale w skrócie: set(<variable name> value)aby zmienić lub utworzyć zmienną. ${<variable name>}używać go.

  • CMAKE_SOURCE_DIR: Katalog główny źródła. W poprzednim przykładzie jest to zawsze równe/simple
  • CMAKE_BINARY_DIR: Katalog główny kompilacji. W poprzednim przykładzie jest to równe simple/build/, ale jeśli uruchomiono cmake simple/z folderu takiego jak foo/bar/etc/, wszystkie odniesienia do CMAKE_BINARY_DIRw tym drzewie kompilacji stałyby się /foo/bar/etc.
  • CMAKE_CURRENT_SOURCE_DIR: Katalog, w którym CMakeLists.txtznajduje się prąd . Oznacza to, że zmienia się on w trakcie: drukowanie tego z simple/CMakeLists.txtplonów /simplei drukowanie z simple/src/CMakeLists.txtplonów /simple/src.
  • CMAKE_CURRENT_BINARY_DIR: Masz pomysł. Ta ścieżka zależy nie tylko od folderu, w którym znajduje się kompilacja, ale także od CMakeLists.txtlokalizacji bieżącego skryptu.

Dlaczego te są ważne? Pliki źródłowe oczywiście nie będą znajdować się w drzewie kompilacji. Jeśli spróbujesz czegoś takiego jak target_include_directories(Tutorial PUBLIC ../lib)w poprzednim przykładzie, ta ścieżka będzie zależna od drzewa budowania, to znaczy będzie przypominać pisanie ${CMAKE_BINARY_DIR}/lib, które zajrzy do środka simple/build/lib/. Nie ma tam plików .h; co najwyżej znajdziesz libTestLib.a. Chcesz ${CMAKE_SOURCE_DIR}/libzamiast tego.

  • CMAKE_CXX_FLAGS: Flagi do przekazania do kompilatora, w tym przypadku kompilatora C ++. Warto również zauważyć, CMAKE_CXX_FLAGS_DEBUGktóry będzie używany zamiast tego, jeśli CMAKE_BUILD_TYPEjest ustawiony na DEBUG. Jest ich więcej; sprawdź wiki CMake .
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY: Powiedz CMake, gdzie umieścić wszystkie pliki wykonywalne po zbudowaniu. To jest ustawienie globalne. Możesz na przykład ustawić to bin/i mieć wszystko w porządku. EXECUTABLE_OUTPUT_PATHjest podobny, ale przestarzały, na wypadek gdybyś się na niego natknął.
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY: Podobnie, globalne ustawienie, które mówi CMake, gdzie umieścić wszystkie pliki bibliotek.

Właściwości celu : możesz ustawić właściwości, które dotyczą tylko jednego celu, czy to pliku wykonywalnego, czy biblioteki (lub archiwum ... masz pomysł). Oto dobry przykład, jak go używać (z set_target_properties().

Czy istnieje łatwy sposób automatycznego dodawania źródeł do celu? Użyj GLOB, aby wyświetlić wszystko w danym katalogu w tej samej zmiennej. Przykładowa składnia to FILE(GLOB <variable name> <directory>/*.cxx).

Czy możesz określić różne typy kompilacji? Tak, chociaż nie jestem pewien, jak to działa ani jakie są tego ograniczenia. Prawdopodobnie wymaga to trochę `` jeśli / wtedy '', ale CMake oferuje podstawowe wsparcie bez konfigurowania czegokolwiek, na przykład ustawienia domyślne dla CMAKE_CXX_FLAGS_DEBUG. Możesz ustawić typ kompilacji z poziomu CMakeLists.txtpliku za pośrednictwem set(CMAKE_BUILD_TYPE <type>)lub wywołując CMake z konsoli, na przykład z odpowiednimi flagami cmake -DCMAKE_BUILD_TYPE=Debug.

Jakieś dobre przykłady projektów wykorzystujących CMake? Wikipedia ma listę projektów open source, które używają CMake, jeśli chcesz się temu przyjrzeć. Samouczki online były dla mnie niczym innym jak rozczarowaniem w tym względzie, jednak to pytanie o przepełnienie stosu ma całkiem fajną i łatwą do zrozumienia konfigurację CMake. Warto zobaczyć.

Używanie zmiennych z CMake w kodzie : Oto szybki i brudny przykład (zaadaptowany z innego samouczka ):

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@

Plik wynikowy wygenerowany przez CMake simple/src/TutorialConfig.h:

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

Z ich sprytnym wykorzystaniem możesz robić fajne rzeczy, takie jak wyłączanie biblioteki i tym podobne. Polecam przyjrzeć się temu samouczkowi, ponieważ jest kilka nieco bardziej zaawansowanych rzeczy, które wcześniej czy później z pewnością będą bardzo przydatne w większych projektach.

Jeśli chodzi o wszystko inne, Stack Overflow jest pełen konkretnych pytań i zwięzłych odpowiedzi, co jest świetne dla wszystkich, z wyjątkiem niewtajemniczonych.

leinaD_natipaC
źródło
2
Przyjmuję tę odpowiedź, ale jeśli ktoś inny napisze lepszą, krótszą odpowiedź, wybiorę ją. Zrobiłbym to sam, ale już wystarczająco wycierpiałem z powodu CMake.
leinaD_natipaC
7
Dzięki za tę niesamowitą pracę. Mam nadzieję, że to zyska więcej głosów! :)
LETs
4
Raczej zaniżony punkt: dzięki CMake nie musisz już utrzymywać oddzielnych ustawień specyficznych dla twojego środowiska kompilatora / kompilacji. Masz jedną konfigurację, która działa dla (różnych wersji) Visual Studio, bloków kodu, zwykłych plików Makefile Unix i wielu innych środowisk. Dlatego właśnie patrzyłem na CMake w pierwszej kolejności: utrzymanie synchronizacji konfiguracji Autoconf, MSVC 2005 / 32bit i MSVC 2010 / 64bit stało się dużym problemem. A kiedy już to zrozumiałem, dodałem wiele innych rzeczy, takich jak generowanie dokumentów, testowanie, pakowanie itp., Wszystkie w sposób wieloplatformowy.
DevSolar
1
@DevSolar True. Ten wiersz podoba mi się bardziej niż w Wikipedii, więc dodam go do odpowiedzi, jeśli nie masz nic przeciwko. Nie będę jednak pasował do niczego o tym, jak poprawnie używać CMake w tym celu. Chodzi bardziej o to, aby móc powiedzieć, co się dzieje.
leinaD_natipaC
1
@RichieHH Postępując zgodnie z przykładem, najpierw konfigurujesz CMake, aby używał Makefiles, następnie używasz CMake do generowania plików, które chcesz zbudować, i na końcu przestajesz martwić się o CMake, ponieważ jego praca tutaj jest wykonana. Więc "martwisz się" o Makefile w tym sensie, że od teraz będziesz musiał używać go makew bash, żeby stworzyć swój projekt.
leinaD_natipaC