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 -G
opcję 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ć.
Odpowiedzi:
Do czego służy CMake?
Według Wikipedii:
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:
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ścmake
w 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
cmake
na innym niż rootCMakeLists.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órymCMakeLists.txt
znajduje się katalog główny .Domyślnie to ustawia wszystko przy użyciu Makefiles, jak powiedziałem. Oto, jak powinien teraz wyglądać folder kompilacji:
Co to za wszystkie te pliki? Jedyne, o co musisz się martwić, to plik Makefile i foldery projektu .
Zwróć uwagę na foldery
src/
ilib/
. Zostały one utworzone, ponieważsimple/CMakeLists.txt
wskazuje na nie za pomocą poleceniaadd_subdirectory(<folder>)
. To polecenie mówi CMake, aby poszukał we wspomnianym folderze innegoCMakeLists.txt
pliku i wykonał ten skrypt, więc każdy dodany w ten sposób podkatalog musi zawieraćCMakeLists.txt
plik. W tym projekciesimple/src/CMakeLists.txt
opisano, jak zbudować rzeczywisty plik wykonywalny isimple/lib/CMakeLists.txt
opisano, jak zbudować bibliotekę. Każdy cel, któryCMakeLists.txt
opisuje, zostanie domyślnie umieszczony w swoim podkatalogu w drzewie budowania. Więc po krótkiej chwiliw konsoli zrobionej z
build/
, dodawane są pliki: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
: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ł elementCMakeLists.txt
wewną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 innychCMakeLists.txt
katalogó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
: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.cxx
do 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 katalogachadd_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
:simple/lib/TestLib.h
:simple/src/CMakeLists.txt
:Polecenie
add_executable()
działa dokładnie tak samo, jakadd_library()
, oczywiście, zamiast tego wygeneruje plik wykonywalny. Ten plik wykonywalny może być teraz przywoływany jako cel dla rzeczy takich jaktarget_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 nietarget_include_directories()
polecenie,lib/TestLib.h
nie zostałoby znalezione podczas kompilowania samouczka, więc całylib/
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
: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.txt
s podlib/
asrc/
i po prostu dodać cośadd_executable(Tutorial src/tutorial.cxx)
dosimple/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.txt
plik). Pamiętaj, aby skonfigurowaćc
po wprowadzeniu zmian, a następnieg
wygenerować 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ównesimple/build/
, ale jeśli uruchomionocmake simple/
z folderu takiego jakfoo/bar/etc/
, wszystkie odniesienia doCMAKE_BINARY_DIR
w tym drzewie kompilacji stałyby się/foo/bar/etc
.CMAKE_CURRENT_SOURCE_DIR
: Katalog, w którymCMakeLists.txt
znajduje się prąd . Oznacza to, że zmienia się on w trakcie: drukowanie tego zsimple/CMakeLists.txt
plonów/simple
i drukowanie zsimple/src/CMakeLists.txt
plonó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 odCMakeLists.txt
lokalizacji 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 środkasimple/build/lib/
. Nie ma tam plików .h; co najwyżej znajdzieszlibTestLib.a
. Chcesz${CMAKE_SOURCE_DIR}/lib
zamiast tego.CMAKE_CXX_FLAGS
: Flagi do przekazania do kompilatora, w tym przypadku kompilatora C ++. Warto również zauważyć,CMAKE_CXX_FLAGS_DEBUG
który będzie używany zamiast tego, jeśliCMAKE_BUILD_TYPE
jest 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ć tobin/
i mieć wszystko w porządku.EXECUTABLE_OUTPUT_PATH
jest 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 poziomuCMakeLists.txt
pliku za pośrednictwemset(CMAKE_BUILD_TYPE <type>)
lub wywołując CMake z konsoli, na przykład z odpowiednimi flagamicmake -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
:simple/TutorialConfig.h.in
:Plik wynikowy wygenerowany przez CMake
simple/src/TutorialConfig.h
: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.
źródło
make
w bash, żeby stworzyć swój projekt.