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ś 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ż 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.txt
znajduje 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.txt
wskazuje na nie za pomocą polecenia add_subdirectory(<folder>)
. To polecenie mówi CMake, aby poszukał we wspomnianym folderze innego CMakeLists.txt
pliku i wykonał ten skrypt, więc każdy dodany w ten sposób podkatalog musi zawierać CMakeLists.txt
plik. W tym projekcie simple/src/CMakeLists.txt
opisano, jak zbudować rzeczywisty plik wykonywalny i simple/lib/CMakeLists.txt
opisano, jak zbudować bibliotekę. Każdy cel, który CMakeLists.txt
opisuje, 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.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 innych CMakeLists.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
:
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.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 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.h
nie 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.txt
s 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.txt
plik). Pamiętaj, aby skonfigurować c
po wprowadzeniu zmian, a następnie g
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ówne simple/build/
, ale jeśli uruchomiono cmake simple/
z folderu takiego jak foo/bar/etc/
, wszystkie odniesienia do CMAKE_BINARY_DIR
w tym drzewie kompilacji stałyby się /foo/bar/etc
.
CMAKE_CURRENT_SOURCE_DIR
: Katalog, w którym CMakeLists.txt
znajduje się prąd . Oznacza to, że zmienia się on w trakcie: drukowanie tego z simple/CMakeLists.txt
plonów /simple
i drukowanie z simple/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 od CMakeLists.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 środka simple/build/lib/
. Nie ma tam plików .h; co najwyżej znajdziesz libTestLib.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śli CMAKE_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ć to bin/
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 poziomu CMakeLists.txt
pliku 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.