Minimal version
Här är den första raden i varje CMakeLists.txt
, som är det obligatoriska namnet på den fil som CMake letar efter:
cmake_minimum_required(VERSION 3.1)
Låt oss nämna lite CMake-syntax. Kommandonamnetcmake_minimum_required
är okänsligt för versaler, så det vanliga är att använda små bokstäver. 1 VERSION
är ett speciellt nyckelord för den här funktionen. Och värdet för versionen följer efter nyckelordet. Som överallt i den här boken är det bara att klicka på kommandonamnet för att se den officiella dokumentationen och använda rullgardinsmenyn för att byta dokumentation mellan CMake-versioner.
Den här raden är speciell! 2 Versionen av CMake dikterar också policyerna,som definierar beteendeförändringar. Så om du ställer in minimum_required
till VERSION2.8
kommer du att få fel länkningsbeteende på macOS, till exempel, även i de senaste CMake-versionerna. Om du ställer in den på 3.3 eller lägre får du fel beteende när det gäller dolda symboler, osv. En lista över policys och versioner finns tillgänglig påpolicies.
Från och med CMake 3.12 stöder detta ett intervall, t.ex. VERSION 3.1...3.15
; detta innebär att du stöder så lågt som 3.1 men att du också har testat det med de nya policyinställningarna upp till 3.15. Detta är mycket trevligare för användare som behöver de bättre inställningarna, och på grund av ett trick i syntaxen är det bakåtkompatibelt med äldre versioner av CMake (även om det faktum att köra CMake 3.1-3.11 endast kommer att ställa in 3.1-versionen av policyerna i det här exemplet, eftersom de versionerna inte behandlade detta speciellt). Nya versioner av policys tenderar att vara viktigast för macOS- ochWindows-användare, som också vanligtvis har en mycket ny version av CMake.
Detta är vad nya projekt bör göra:
cmake_minimum_required(VERSION 3.7...3.20)if(${CMAKE_VERSION} VERSION_LESS 3.12) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})endif()
Om CMake-versionen är mindre än 3.12 kommer if-blocket att vara sant och policyn kommer att ställas in på den aktuella CMake-versionen. Om CMake är 3.12 eller högre kommer if-blocket att vara falskt, men den nya syntaxen i cmake_minimum_required
kommer att respekteras och detta kommer att fortsätta att fungera korrekt!
VARNING: MSVC:s CMake-serverläge hade ursprungligen problem med att läsa det här formatet, så om du behöver stöd för Windows-byggen utan kommandorad för äldre MSVC-versioner bör du göra så här i stället:
cmake_minimum_required(VERSION 3.7)if(${CMAKE_VERSION} VERSION_LESS 3.20) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})else() cmake_policy(VERSION 3.20)endif()
Om du verkligen behöver ställa in ett lågt värde här kan du användacmake_policy
för att villkorligt öka policynivån eller ställa in en specifik policy. Gör åtminstone detta för dina macOS-användare!
Sätt ett projekt
Nu kommer varje CMake-fil på högsta nivå att ha nästa rad:
project(MyProject VERSION 1.0 DESCRIPTION "Very nice project" LANGUAGES CXX)
Nu ser vi ännu mer syntax. Strängar är citerade, vitrymder spelar ingen roll och projektets namn är det första argumentet (positionellt). Alla nyckelordsargument här är valfria. Versionen ställer in ett gäng variabler, somMyProject_VERSION
och PROJECT_VERSION
. Språken är C
, CXX
,Fortran
, ASM
, CUDA
(CMake 3.8+), CSharp
(3.8+) och SWIFT
(CMake3.15+ experimentell). C CXX
är standard. I CMake 3.9 lades DESCRIPTION
till för att ställa in en projektbeskrivning. Dokumentationen förproject
kan vara till hjälp.
Du kan lägga till kommentarer med tecknet #
. CMake har också en inline-syntax för kommentarer, men den används sällan.
Det finns egentligen inget speciellt med projektnamnet. Inga mål läggs till vid den här tidpunkten.
Makning av en körbar dator
Men även om bibliotek är mycket mer intressanta, och vi kommer att tillbringa större delen av vår tid med dem, så låt oss börja med en enkel körbar dator.
add_executable(one two.cpp three.h)
Det finns flera saker att packa upp här. one
är både namnet på den genererade körbara filen och namnet på det skapade CMake-målet (du kommer att få höra mycket mer om mål snart, jag lovar). Källfilslistan kommer härnäst, och du kan lista så många du vill. CMake är smart och kommer endast att kompilera källfilsextensioner. Huvudet kommer, för de flesta ändamål, att ignoreras; den enda anledningen till att lista dem är att få dem att dyka upp i IDE:er. Targets visas som mappar i många IDE:er. Mer om det allmänna byggsystemet och targets finns på buildsystem.
Makning av ett bibliotek
Makning av ett bibliotek görs med add_library
, och är ungefär lika enkelt:
add_library(one STATIC two.cpp three.h)
Du får välja en typ av bibliotek, STATIC, SHARED, eller MODULE. Om du inte väljer detta val kommer värdet av BUILD_SHARED_LIBS
att användas för att välja mellan STATIC och SHARED.
Som du kommer att se i de följande avsnitten behöver du ofta göra ett fiktivt mål, det vill säga ett där ingenting behöver kompileras, till exempel för ett bibliotek med enbart rubriker. Det kallas ett INTERFACE-bibliotek och är ett annat val; den enda skillnaden är att det inte kan följas av filnamn.
Du kan också göra ett ALIAS
-bibliotek med ett befintligt bibliotek, vilket helt enkelt ger dig ett nytt namn för ett mål. Den enda fördelen med detta är att du kan göra bibliotek med ::
i namnet (vilket du kommer att se senare). 3
Mål är din vän
Nu har vi specificerat ett mål, hur lägger vi till information om det? Till exempel kanske den behöver en include-katalog:
target_include_directories(one PUBLIC include)
target_include_directories
lägger till en include-katalog till ett mål. PUBLIC
betyder inte mycket för en körbar fil; för ett bibliotek låter det CMake veta att alla mål som länkar till detta mål måste också behöva den inkluderande katalogen. Andra alternativ är PRIVATE
(påverkar bara det aktuella målet, inte beroenden) och INTERFACE
(behövs bara för beroenden).
Vi kan sedan kedja mål:
add_library(another STATIC another.cpp another.h)target_link_libraries(another PUBLIC one)
target_link_libraries
är förmodligen det mest användbara och förvirrande kommandot i CMake. Det tar ett mål (another
) och lägger till ett beroende om ett mål anges. Om inget mål med det namnet (one
) finns, lägger det till en länk till ett bibliotek som heter one
på din sökväg (därav namnet på kommandot). Eller så kan du ge den en fullständig sökväg till ett bibliotek. Eller en länkarflagga. Bara för att lägga till en sista bit av förvirring, så lät det klassiska CMake dig hoppa över nyckelordsvalet av PUBLIC
, osv. Om detta gjordes på ett mål får du ett fel om du försöker blanda stilar längre ner i kedjan.
Fokusera på att använda mål överallt, och nyckelord överallt, så går det bra.
Mål kan ha include-kataloger, länkade bibliotek (eller länkade mål), kompileringsalternativ, kompileringsdefinitioner, kompileringsfunktioner (se kapitlet om C++11), med mera. Som du kommer att se i de två inkluderande projektkapitlen kan du ofta få targets (och alltid göra targets) för att representera alla bibliotek du använder. Även saker som inte är riktiga bibliotek, som OpenMP, kan representeras med targets. Det är därför Modern CMake är så bra!
Dyk in
Se om du kan följa följande fil. Den gör ett enkelt C++11-bibliotek och ett program som använder det. Inga beroenden. Jag kommer att diskutera fler C++-standardalternativ senare, och använder CMake 3.8-systemet för tillfället.
cmake_minimum_required(VERSION 3.8)project(Calculator LANGUAGES CXX)add_library(calclib STATIC src/calclib.cpp include/calc/lib.hpp)target_include_directories(calclib PUBLIC include)target_compile_features(calclib PUBLIC cxx_std_11)add_executable(calc apps/calc.cpp)target_link_libraries(calc PUBLIC calclib)
1. I den här boken undviker jag mestadels att visa dig fel sätt att göra saker och ting; du kan hitta gott om exempel på det på nätet. Jag kommer att nämna alternativ ibland, men dessa rekommenderas inte om de inte är absolut nödvändiga; ofta finns de bara där för att hjälpa dig att läsa äldre CMake-kod.
2. Du kommer ibland att se
FATAL_ERROR
här, det behövdes för att stödja trevliga fel när du körde detta i CMake <2.6, vilket inte längre borde vara ett problem.
3. Syntaxen
::
var ursprungligen avsedd förINTERFACE IMPORTED
-bibliotek, som uttryckligen skulle vara bibliotek som definierats utanför det aktuella projektet. Men på grund av detta fungerar de flestatarget_*
-kommandon inte påIMPORTED
-bibliotek, vilket gör det svårt att konfigurera dem själv. Så använd inte nyckelordetIMPORTED
för tillfället och använd istället ettALIAS
-mål; det kommer att fungera bra tills du börjar exportera mål. Denna begränsning har rättats i CMake 3.11.