Kuten edellisessä viestissäni selitettiin, jokaisen CMake-pohjaisen projektin on sisällettävä komentosarja nimeltä CMakeLists.txt. Tämä skripti määrittelee kohteet, mutta se voi tehdä myös monia muita asioita, kuten etsiä kolmannen osapuolen kirjastoja tai luoda C++-otsikkotiedostoja. CMake-skripteissä on paljon joustavuutta.

Joka kerta, kun integroit ulkoisen kirjaston, ja usein, kun lisäät tukea toiselle alustalle, sinun täytyy muokata skriptiä. Vietin pitkän aikaa CMake-skriptien muokkaamisessa ymmärtämättä kieltä kunnolla, sillä dokumentaatio on melko hajanaista, mutta lopulta asiat napsahtivat. Tämän postauksen tavoitteena on saada sinut samaan pisteeseen mahdollisimman nopeasti.

Tämä postaus ei kata kaikkia CMaken sisäänrakennettuja komentoja, sillä niitä on satoja, mutta se on melko kattava opas kielen syntaksista ja ohjelmointimallista.

Hello World

Jos luot tiedoston nimeltä hello.txt, jonka sisältö on seuraava:

message("Hello world!") # A message to print

…voit ajaa sen komentoriviltä käyttämällä cmake -P hello.txt. (Vaihtoehto -P ajaa annetun komentosarjan, mutta ei luo rakennusputkea). Odotetusti se tulostaa ”Hello world!”.

$ cmake -P hello.txtHello world!

All Variables Are Strings

Cmakessa jokainen muuttuja on merkkijono. Voit korvata muuttujan merkkijonokirjaimen sisällä ympäröimällä sen ${}:llä. Tätä kutsutaan muuttujaviittaukseksi. Muokkaa hello.txt seuraavasti:

message("Hello ${NAME}!") # Substitute a variable into the message

Nyt jos määrittelemme NAME komentorivillä cmake käyttäen -D-optiota, skripti käyttää sitä:

$ cmake -DNAME=Newman -P hello.txtHello Newman!

Kun muuttuja on määrittelemätön, sen oletusarvona on tyhjä merkkijono:

$ cmake -P hello.txtHello !

Muuttujan määrittelemiseksi skriptin sisälle käytetään set-komentoa. Ensimmäinen argumentti on määritettävän muuttujan nimi ja toinen argumentti on sen arvo:

set(THING "funk")message("We want the ${THING}!")

Lausekkeet argumenttien ympärillä ovat valinnaisia, kunhan argumentissa ei ole välilyöntejä tai muuttujaviittauksia. Olisin esimerkiksi voinut kirjoittaa yllä olevalle ensimmäiselle riville set("THING" funk) – se olisi ollut vastaava. Useimmissa CMake-komennoissa (paitsi if ja while, jotka kuvataan jäljempänä) valinta siitä, lainataanko tällaiset argumentit, on yksinkertaisesti tyylikysymys. Kun argumentti on muuttujan nimi, minulla on tapana olla käyttämättä lainausmerkkejä.

Voit simuloida tietorakennetta käyttämällä etuliitteitä

CMakessa ei ole luokkia, mutta voit simuloida tietorakennetta määrittelemällä ryhmän muuttujia, joiden nimet alkavat samalla etuliitteellä. Voit sitten etsiä muuttujia tuosta ryhmästä käyttämällä sisäkkäisiä ${}-muuttujaviittauksia. Esimerkiksi seuraava skripti tulostaa ”John Smith asuu osoitteessa 123 Fake St.”:

set(JOHN_NAME "John Smith")set(JOHN_ADDRESS "123 Fake St")set(PERSON "JOHN")message("${${PERSON}_NAME} lives at ${${PERSON}_ADDRESS}.")

Voit jopa käyttää muuttujaviittauksia asetettavan muuttujan nimessä. Jos esimerkiksi PERSON:n arvo on edelleen ”JOHN”, seuraava asettaa muuttujan JOHN_NAME arvoksi ”John Goodman”:

set(${PERSON}_NAME "John Goodman")

Jokainen lauseke on komento

CMakessa jokainen lauseke on komento, joka ottaa listan merkkijonoargumentteja eikä sillä ole paluuarvoa. Argumentit erotetaan toisistaan (ilman lainausmerkkejä) välilyönneillä. Kuten olemme jo nähneet, komento set määrittelee muuttujan tiedostoalueelle.

Muuna esimerkkinä CMakessa on komento math, joka suorittaa aritmeettisia laskutoimituksia. Ensimmäisen argumentin on oltava EXPR, toinen argumentti on osoitettavan muuttujan nimi ja kolmas argumentti on arvioitava lauseke – kaikki merkkijonoja. Huomaa, että alla olevalla kolmannella rivillä CMake korvaa merkkijonon MY_SUM arvon ympäröivään argumenttiin ennen argumentin välittämistä math:lle.

math(EXPR MY_SUM "1 + 1") # Evaluate 1 + 1; store result in MY_SUMmessage("The sum is ${MY_SUM}.")math(EXPR DOUBLE_SUM "${MY_SUM} * 2") # Multiply by 2; store result in DOUBLE_SUMmessage("Double that is ${DOUBLE_SUM}.")

Tässä on CMake-komento melkein kaikkeen, mitä sinun tarvitsee tehdä. string-komennolla voit suorittaa edistynyttä merkkijonojen käsittelyä, mukaan lukien säännöllisten lausekkeiden korvaaminen. Komennolla file voi lukea tai kirjoittaa tiedostoja tai käsitellä tiedostojärjestelmän polkuja.

Virtauksenohjauskomennot

Jopa virtauksenohjauslausekkeet ovat komentoja. Komennot if/endif suorittavat liitteenä olevat komennot ehdollisesti. Välilyönneillä ei ole väliä, mutta on tavallista, että suljetut komennot sisennetään luettavuuden vuoksi. Seuraavassa tarkistetaan, onko CMaken sisäänrakennettu muuttuja WIN32 asetettu:

if(WIN32) message("You're running CMake on Windows.")endif()

CMakella on myös while/endwhile-komennot, jotka, kuten arvata saattaa, toistavat suljettuja komentoja niin kauan kuin ehto on tosi. Tässä on silmukka, joka tulostaa kaikki Fibonaccin luvut miljoonaan asti:

set(A "1")set(B "1")while(A LESS "1000000") message("${A}") # Print A math(EXPR T "${A} + ${B}") # Add the numeric values of A and B; store result in T set(A "${B}") # Assign the value of B to A set(B "${T}") # Assign the value of T to Bendwhile()

CMaken if– ja while-ehtoja ei kirjoiteta samalla tavalla kuin muissa kielissä. Jos haluat esimerkiksi suorittaa numeerisen vertailun, sinun on määritettävä LESS merkkijonoargumenttina, kuten yllä on esitetty. Dokumentaatiossa kerrotaan, miten kelvollinen ehto kirjoitetaan.

if ja while eroavat muista CMake-komennoista siinä, että jos muuttujan nimi annetaan ilman lainausmerkkejä, komento käyttää muuttujan arvoa. Yllä olevassa koodissa hyödynsin tätä käyttäytymistä kirjoittamalla while(A LESS "1000000") while("${A}" LESS "1000000"):n sijasta – molemmat muodot vastaavat toisiaan. Muut CMake-komennot eivät tee näin.

Luettelot ovat vain puolipisteellä rajattuja merkkijonoja

CMakella on erityinen korvaussääntö lainausmerkeillä rajoittamattomille argumenteille. Jos koko argumentti on muuttujaviittaus ilman lainausmerkkejä ja muuttujan arvo sisältää puolipisteitä, CMake jakaa arvon puolipisteiden kohdalla ja välittää useita argumentteja ympäröivälle komennolle. Esimerkiksi seuraavassa välitetään kolme argumenttia komennolle math:

set(ARGS "EXPR;T;1 + 1")math(${ARGS}) # Equivalent to calling math(EXPR T "1 + 1")

Toisaalta lainausmerkeillä varustettuja argumentteja ei koskaan jaeta useammaksi argumentiksi, ei edes korvaamisen jälkeen. CMake välittää lainausmerkkisen merkkijonon aina yhtenä argumenttina jättäen puolipisteet koskemattomiksi:

set(ARGS "EXPR;T;1 + 1")message("${ARGS}") # Prints: EXPR;T;1 + 1

Jos komennolle set välitetään enemmän kuin kaksi argumenttia, ne yhdistetään puolipisteillä ja osoitetaan sitten määritettyyn muuttujaan. Näin argumenteista luodaan käytännössä lista:

set(MY_LIST These are separate arguments)message("${MY_LIST}") # Prints: These;are;separate;arguments

Voit käsitellä tällaisia listoja komennolla list:

set(MY_LIST These are separate arguments)list(REMOVE_ITEM MY_LIST "separate") # Removes "separate" from the listmessage("${MY_LIST}") # Prints: These;are;arguments

Komento foreach/endforeach hyväksyy useita argumentteja. Se iteroi kaikki argumentit ensimmäistä lukuun ottamatta ja osoittaa jokaisen nimettyyn muuttujaan:

foreach(ARG These are separate arguments) message("${ARG}") # Prints each word on a separate lineendforeach()

Voit iteroida listaa välittämällä foreach:lle noteeraamattoman muuttujan viittauksen. Kuten minkä tahansa muun komennon kohdalla, CMake jakaa muuttujan arvon ja välittää komennolle useita argumentteja:

foreach(ARG ${MY_LIST}) # Splits the list; passes items as arguments message("${ARG}") # Prints each item on a separate lineendforeach()

Functions Run In Their Own Scope; Macros Don’t

Cmake:ssa voit käyttää komentoparia function/endfunction funktion määrittelyyn. Tässä on yksi, joka kaksinkertaistaa argumenttinsa numeerisen arvon ja tulostaa tuloksen:

function(doubleIt VALUE) math(EXPR RESULT "${VALUE} * 2") message("${RESULT}")endfunction()doubleIt("4") # Prints: 8

Funktiot toimivat omassa laajuudessaan. Mikään funktiossa määritellyistä muuttujista ei saastuta kutsujan vaikutusaluetta. Jos haluat palauttaa arvon, voit antaa funktiolle muuttujan nimen ja kutsua sitten komentoa set erikoisargumentilla PARENT_SCOPE:

function(doubleIt VARNAME VALUE) math(EXPR RESULT "${VALUE} * 2") set(${VARNAME} "${RESULT}" PARENT_SCOPE) # Set the named variable in caller's scopeendfunction()doubleIt(RESULT "4") # Tell the function to set the variable named RESULTmessage("${RESULT}") # Prints: 8

Vastaavasti komentopari macro/endmacro määrittelee makron. Toisin kuin funktiot, makrot toimivat samassa laajuudessa kuin niiden kutsuja. Siksi kaikki makron sisällä määritellyt muuttujat asetetaan kutsujan vaikutusalueelle. Voimme korvata edellisen funktion seuraavalla:

macro(doubleIt VARNAME VALUE) math(EXPR ${VARNAME} "${VALUE} * 2") # Set the named variable in caller's scopeendmacro()doubleIt(RESULT "4") # Tell the macro to set the variable named RESULTmessage("${RESULT}") # Prints: 8

Kumpikin funktio ja makro hyväksyvät mielivaltaisen määrän argumentteja. Nimettömät argumentit näytetään funktiolle listana erityisen muuttujan ARGN kautta. Tässä on funktio, joka tuplaa jokaisen saamansa argumentin ja tulostaa jokaisen argumentin erilliselle riville:

function(doubleEach) foreach(ARG ${ARGN}) # Iterate over each argument math(EXPR N "${ARG} * 2") # Double ARG's numeric value; store result in N message("${N}") # Print N endforeach()endfunction()doubleEach(5 6 7 8) # Prints 10, 12, 14, 16 on separate lines

Sisällyttää muita skriptejä

CMake-muuttujat määritellään tiedoston laajuudessa. Komento include suorittaa toisen CMake-skriptin samassa laajuudessa kuin kutsuva skripti. Se muistuttaa paljon #include-direktiiviä C/C++:ssa. Sitä käytetään yleensä määrittelemään yhteinen joukko funktioita tai makroja kutsuvassa komentosarjassa. Se käyttää muuttujaa CMAKE_MODULE_PATH hakupolkuna.

find_packageKomento find_package etsii skriptejä, jotka ovat muotoa Find*.cmake ja ajaa myös ne samassa laajuudessa. Tällaisia skriptejä käytetään usein apuna ulkoisten kirjastojen etsimisessä. Jos hakupolussa on esimerkiksi tiedosto nimeltä FindSDL2.cmake, find_package(SDL2) vastaa include(FindSDL2.cmake). (Huomaa, että komennon find_package käyttämiseen on useita tapoja – tämä on vain yksi niistä.)

CMaken komento add_subdirectory puolestaan luo uuden scope-alueen ja suorittaa sitten skriptin nimeltä CMakeLists.txt määritellystä hakemistosta tässä uudessa scope-alueessa. Sitä käytetään yleensä lisäämään toinen CMake-pohjainen aliprojekti, kuten kirjasto tai suoritettava tiedosto, kutsuvaan projektiin. Aliprojektin määrittelemät kohteet lisätään rakennusputkeen, ellei toisin määritetä. Mikään aliprojektin komentosarjassa määritetyistä muuttujista ei saastuta emoprojektin scope-aluetta, ellei komennon set PARENT_SCOPE-optiota käytetä.

Esimerkiksi seuraavassa on joitakin komentosarjoja, jotka ovat mukana, kun ajetaan CMakea Turf-projektissa:

Ominaisuuksien saaminen ja asettaminen

CMake-skripti määrittelee kohteet komennoilla add_executable, add_library tai add_custom_target. Kun kohde on luotu, sillä on ominaisuuksia, joita voit käsitellä get_property– ja set_property-komennoilla. Toisin kuin muuttujat, kohteet näkyvät jokaisessa laajuudessa, vaikka ne olisi määritelty alihakemistossa. Kaikki kohdeominaisuudet ovat merkkijonoja.

add_executable(MyApp "main.cpp") # Create a target named MyApp# Get the target's SOURCES property and assign it to MYAPP_SOURCESget_property(MYAPP_SOURCES TARGET MyApp PROPERTY SOURCES)message("${MYAPP_SOURCES}") # Prints: main.cpp

Muut kohdeominaisuudet ovat LINK_LIBRARIES, INCLUDE_DIRECTORIES ja COMPILE_DEFINITIONS. Näitä ominaisuuksia muutetaan epäsuorasti komennoilla target_link_libraries, target_include_directories ja target_compile_definitions. Skriptin lopussa CMake käyttää näitä kohdeominaisuuksia rakentamisputken luomiseen.

Ominaisuuksia on myös muille CMake-olioille. Jokaisella tiedostoalueella on joukko hakemisto-ominaisuuksia. On joukko globaaleja ominaisuuksia, joihin pääsee käsiksi kaikista skripteistä. Ja jokaiselle C/C++-lähdetiedostolle on joukko lähdetiedoston ominaisuuksia.

Articles

Vastaa

Sähköpostiosoitettasi ei julkaista.