CMake ja C++ ovat ikuisia veljiä

CMake ja C++ ovat ikuisia veljiä

Kehityksen aikana tykkään vaihtaa kääntäjiä, rakentaa tiloja, riippuvuusversioita, tehdä staattista analyysiä, mitata suorituskykyä, kerätä kattavuutta, luoda dokumentaatiota jne. Ja rakastan todella CMakea, koska sen avulla voin tehdä kaiken mitä haluan.

Monet ihmiset arvostelevat CMakea, ja usein ansaitusti, mutta jos katsot sitä, kaikki ei ole niin huonosti, ja viime aikoina ei huonommin, ja kehityksen suunta on varsin myönteinen.

Tässä huomautuksessa haluan kertoa sinulle, kuinka yksinkertaisesti järjestää otsikkokirjasto C++:ssa CMake-järjestelmässä saadaksesi seuraavat toiminnot:

  1. Kokoonpano;
  2. automaattisen käynnistystestit;
  3. Koodipeittomittaus;
  4. Asennus;
  5. Automaattinen dokumentointi;
  6. Online hiekkalaatikko sukupolvi;
  7. Staattinen analyysi.

Jokainen, joka jo ymmärtää edut ja C-maken, voi yksinkertaisesti Lataa projektimalli ja alkaa käyttää sitä.


Pitoisuus

  1. Projekti sisältä
    1. Hankkeen rakenne
    2. CMake-päätiedosto (./CMakeLists.txt)
      1. Hankkeen tiedot
      2. Projektivaihtoehdot
      3. Kokoonpanovaihtoehdot
      4. Päätavoitteena
      5. Asennus
      6. testit
      7. Asiakirjat
      8. Online hiekkalaatikko
    3. Testiohjelma (test/CMakeLists.txt)
      1. Testaus
      2. Kattavuus
    4. Käsikirjoitus dokumentaatiota varten (doc/CMakeLists.txt)
    5. Komentosarja online-hiekkalaatikolle (online/CMakeLists.txt)
  2. Projekti ulkona
    1. kokoonpano
      1. sukupolvi
      2. kokoonpano
    2. Valinnat
      1. MYLIB_COVERAGE
      2. MYLIB_TESTAUS
      3. MYLIB_DOXYGEN_LANGUAGE
    3. Kokoonpanokohteet
      1. Oletuksena
      2. mylib-yksikkötestit
      3. tarkastaa
      4. kattavuus
      5. telakka
      6. sauvalaatikko
    4. Примеры
  3. Työkalut
  4. Staattinen analyysi
  5. loppusanat

Projekti sisältä

Hankkeen rakenne

.
├── CMakeLists.txt
├── README.en.md
├── README.md
├── doc
│   ├── CMakeLists.txt
│   └── Doxyfile.in
├── include
│   └── mylib
│       └── myfeature.hpp
├── online
│   ├── CMakeLists.txt
│   ├── mylib-example.cpp
│   └── wandbox.py
└── test
    ├── CMakeLists.txt
    ├── mylib
    │   └── myfeature.cpp
    └── test_main.cpp

Puhumme pääasiassa CMake-skriptien järjestämisestä, joten niistä keskustellaan yksityiskohtaisesti. Kuka tahansa voi tarkastella loput tiedostot suoraan malliprojektisivulla.

CMake-päätiedosto (./CMakeLists.txt)

Hankkeen tiedot

Ensinnäkin sinun on pyydettävä vaadittu versio CMake-järjestelmästä. CMake kehittyy, komentojen allekirjoitukset ja käyttäytyminen eri olosuhteissa muuttuvat. Jotta CMake ymmärtäisi heti, mitä haluamme siltä, ​​meidän on välittömästi tallennettava sitä koskevat vaatimukset.

cmake_minimum_required(VERSION 3.13)

Sitten määritämme projektimme, sen nimen, version, käytetyt kielet jne. (katso. команду project).

Tässä tapauksessa ilmoitamme kielen CXX (ja tämä tarkoittaa C++), jotta CMake ei rasita ja etsi C-kielen kääntäjää (oletusarvoisesti CMake sisältää kaksi kieltä: C ja C++).

project(Mylib VERSION 1.0 LANGUAGES CXX)

Täältä voit heti tarkistaa, onko projektimme mukana jossain muussa projektissa aliprojektina. Tämä auttaa paljon tulevaisuudessa.

get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)

Projektivaihtoehdot

Tarjoamme kaksi vaihtoehtoa.

Ensimmäinen vaihtoehto on MYLIB_TESTING — yksikkötestien poistaminen käytöstä. Tämä voi olla tarpeen, jos olemme varmoja, että kaikki on kunnossa testeissä, mutta haluamme vain esimerkiksi asentaa tai paketoida projektimme. Tai projektimme on mukana aliprojektina - tässä tapauksessa projektimme käyttäjä ei ole kiinnostunut suorittamaan testejämme. Ethän sinä testaa käyttämiäsi riippuvuuksia?

option(MYLIB_TESTING "Включить модульное тестирование" ON)

Lisäksi teemme erillisen vaihtoehdon MYLIB_COVERAGE koodin kattavuuden mittaamiseen testeillä, mutta se vaatii lisätyökaluja, joten se on otettava käyttöön erikseen.

option(MYLIB_COVERAGE "Включить измерение покрытия кода тестами" OFF)

Kokoonpanovaihtoehdot

Tietenkin olemme siistejä ja ohjelmoijia, joten haluamme kääntäjältä maksimaalisen käännösajan diagnosoinnin. Yksikään hiiri ei lipsahda läpi.

add_compile_options(
    -Werror

    -Wall
    -Wextra
    -Wpedantic

    -Wcast-align
    -Wcast-qual
    -Wconversion
    -Wctor-dtor-privacy
    -Wenum-compare
    -Wfloat-equal
    -Wnon-virtual-dtor
    -Wold-style-cast
    -Woverloaded-virtual
    -Wredundant-decls
    -Wsign-conversion
    -Wsign-promo
)

Poistamme myös laajennukset käytöstä noudattaaksemme täysin C++-kielistandardia. Ne ovat oletuksena käytössä CMakessa.

if(NOT CMAKE_CXX_EXTENSIONS)
    set(CMAKE_CXX_EXTENSIONS OFF)
endif()

Päätavoitteena

Kirjastomme koostuu vain otsikkotiedostoista, mikä tarkoittaa, että meillä ei ole mitään tyhjennystä staattisten tai dynaamisten kirjastojen muodossa. Toisaalta, jotta voisimme käyttää kirjastoamme ulkoisesti, se on asennettava, sen on oltava järjestelmässä havaittavissa ja liitettävä projektiisi, ja samalla nämä samat otsikot sekä mahdollisesti joitain muita, on liitetty siihen ominaisuuksia.

Tätä tarkoitusta varten luomme käyttöliittymäkirjaston.

add_library(mylib INTERFACE)

Yhdistämme otsikot käyttöliittymäkirjastoomme.

Moderni, muodikas, nuorten CMaken käyttö tarkoittaa, että otsikot, ominaisuudet jne. lähetetään yhden kohteen kautta. Joten riittää sanottava target_link_libraries(target PRIVATE dependency)ja kaikki otsikot, jotka liittyvät kohteeseen dependency, ovat saatavilla kohteeseen kuuluville lähteille target. Etkä tarvitse yhtään [target_]include_directories. Tämä osoitetaan alla analyysissä CMake skripti yksikkötestejä varten.

Kannattaa myös kiinnittää huomiota ns. выражения-генераторы: $<...>.

Tämä komento yhdistää tarvitsemamme otsikot käyttöliittymäkirjastoomme, ja jos kirjastomme on yhdistetty mihin tahansa kohteeseen samassa CMake-hierarkiassa, hakemiston otsikot liitetään siihen. ${CMAKE_CURRENT_SOURCE_DIR}/include, ja jos kirjastomme on asennettu järjestelmään ja yhdistetty toiseen projektiin komennolla find_package, niin hakemiston otsikot liitetään siihen include suhteessa asennushakemistoon.

target_include_directories(mylib INTERFACE
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

Asetetaan kielistandardi. Tietysti viimeinen. Samaan aikaan emme vain sisällytä standardia, vaan ulotamme sen myös kirjastomme käyttäjiin. Tämä saavutetaan, koska määritetyllä ominaisuudella on luokka INTERFACE (Ks. target_compile_features -komento).

target_compile_features(mylib INTERFACE cxx_std_17)

Luodaan kirjastollemme alias. Lisäksi kauneuden vuoksi se on erityisessä "nimitilassa". Tästä on hyötyä, kun eri moduuleita ilmestyy kirjastoomme ja yhdistämme ne toisistaan ​​riippumatta. Kuten esimerkiksi Bustassa.

add_library(Mylib::mylib ALIAS mylib)

Asennus

Otsikoiden asentaminen järjestelmään. Täällä kaikki on yksinkertaista. Sanomme, että kansion, jossa on kaikki otsikot, pitäisi mennä hakemistoon include suhteessa asennuspaikkaan.

install(DIRECTORY include/mylib DESTINATION include)

Seuraavaksi ilmoitamme rakennusjärjestelmälle, että haluamme pystyä kutsumaan komentoa kolmannen osapuolen projekteissa find_package(Mylib) ja saada maali Mylib::mylib.

install(TARGETS mylib EXPORT MylibConfig)
install(EXPORT MylibConfig NAMESPACE Mylib:: DESTINATION share/Mylib/cmake)

Seuraava loitsu tulisi ymmärtää näin. Kolmannen osapuolen projektissa kutsumme komentoa find_package(Mylib 1.2.3 REQUIRED), ja asennetun kirjaston todellinen versio ei ole yhteensopiva version kanssa 1.2.3CMake luo automaattisesti virheen. Toisin sanoen sinun ei tarvitse seurata versioita manuaalisesti.

include(CMakePackageConfigHelpers)
write_basic_package_version_file("${PROJECT_BINARY_DIR}/MylibConfigVersion.cmake"
    VERSION
        ${PROJECT_VERSION}
    COMPATIBILITY
        AnyNewerVersion
)
install(FILES "${PROJECT_BINARY_DIR}/MylibConfigVersion.cmake" DESTINATION share/Mylib/cmake)

testit

Jos testit on poistettu käytöstä eksplisiittisesti käyttämällä vastaava vaihtoehto tai projektimme on aliprojekti, eli se on yhdistetty toiseen CMake-projektiin komennolla add_subdirectory, emme liiku hierarkiassa pidemmälle, ja komentosarja, joka kuvaa komennot testien luomiseen ja suorittamiseen, ei yksinkertaisesti toimi.

if(NOT MYLIB_TESTING)
    message(STATUS "Тестирование проекта Mylib выключено")
elseif(IS_SUBPROJECT)
    message(STATUS "Mylib не тестируется в режиме подмодуля")
else()
    add_subdirectory(test)
endif()

Asiakirjat

Dokumentaatiota ei myöskään luoda osaprojektin tapauksessa.

if(NOT IS_SUBPROJECT)
    add_subdirectory(doc)
endif()

Online hiekkalaatikko

Samoin osaprojektissa ei ole myöskään online-hiekkalaatikkoa.

if(NOT IS_SUBPROJECT)
    add_subdirectory(online)
endif()

Testiohjelma (test/CMakeLists.txt)

Testaus

Ensinnäkin löydämme paketin, jossa on vaadittu testikehys (korvaa suosikkisi).

find_package(doctest 2.3.3 REQUIRED)

Luodaan suoritettava tiedostomme testeillä. Yleensä lisään suoraan suoritettavaan binaariin vain tiedoston, joka sisältää funktion main.

add_executable(mylib-unit-tests test_main.cpp)

Ja lisään tiedostoja, joissa itse testit kuvataan myöhemmin. Mutta sinun ei tarvitse tehdä sitä.

target_sources(mylib-unit-tests PRIVATE mylib/myfeature.cpp)

Yhdistämme riippuvuuksia. Huomaa, että linkitimme vain tarvitsemamme CMake-kohteet binaariimme, emmekä kutsuneet komentoa target_include_directories. Otsikot testikehyksestä ja meidän Mylib::mylib, sekä rakennusparametrit (meidän tapauksessamme tämä on C++-kielistandardi) onnistuivat näiden tavoitteiden myötä.

target_link_libraries(mylib-unit-tests
    PRIVATE
        Mylib::mylib
        doctest::doctest
)

Lopuksi luomme valekohteen, jonka "koontiversio" vastaa testien suorittamista, ja lisäämme tämän kohteen oletuskoontiversioon (attribuutti vastaa tästä ALL). Tämä tarkoittaa, että oletusversio käynnistää testit, joten emme koskaan unohda suorittaa niitä.

add_custom_target(check ALL COMMAND mylib-unit-tests)

Kattavuus

Seuraavaksi otamme käyttöön koodipeittomittauksen, jos sopiva vaihtoehto on määritetty. En mene yksityiskohtiin, koska ne liittyvät enemmän kattavuuden mittaustyökaluun kuin CMakeen. On vain tärkeää huomata, että tulosten perusteella luodaan tavoite coverage, jolla on kätevä aloittaa kattavuuden mittaaminen.

find_program(GCOVR_EXECUTABLE gcovr)
if(MYLIB_COVERAGE AND GCOVR_EXECUTABLE)
    message(STATUS "Измерение покрытия кода тестами включено")

    target_compile_options(mylib-unit-tests PRIVATE --coverage)
    target_link_libraries(mylib-unit-tests PRIVATE gcov)

    add_custom_target(coverage
        COMMAND
            ${GCOVR_EXECUTABLE}
                --root=${PROJECT_SOURCE_DIR}/include/
                --object-directory=${CMAKE_CURRENT_BINARY_DIR}
        DEPENDS
            check
    )
elseif(MYLIB_COVERAGE AND NOT GCOVR_EXECUTABLE)
    set(MYLIB_COVERAGE OFF)
    message(WARNING "Для замеров покрытия кода тестами требуется программа gcovr")
endif()

Käsikirjoitus dokumentaatiota varten (doc/CMakeLists.txt)

Doxygen löytyi.

find_package(Doxygen)

Seuraavaksi tarkistamme, onko käyttäjä asettanut kielimuuttujan. Jos kyllä, emme koske siihen, jos ei, niin otamme venäjän. Sitten määritämme Doxygen-järjestelmätiedostot. Kaikki tarvittavat muuttujat, mukaan lukien kieli, menevät sinne määritysprosessin aikana (katso. команду configure_file).

Sitten luomme tavoitteen doc, joka alkaa luoda asiakirjoja. Koska dokumentaation tuottaminen ei ole kehitysprosessin suurin tarve, kohde ei ole oletusarvoisesti käytössä, vaan se on käynnistettävä eksplisiittisesti.

if (Doxygen_FOUND)
    if (NOT MYLIB_DOXYGEN_LANGUAGE)
        set(MYLIB_DOXYGEN_LANGUAGE Russian)
    endif()
    message(STATUS "Doxygen documentation will be generated in ${MYLIB_DOXYGEN_LANGUAGE}")
    configure_file(Doxyfile.in Doxyfile)
    add_custom_target(doc COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
endif ()

Komentosarja online-hiekkalaatikolle (online/CMakeLists.txt)

Täältä löydämme kolmannen Pythonin ja luomme kohteen wandbox, joka luo palvelun API:ta vastaavan pyynnön Wandbox, ja lähettää hänet pois. Vastauksen mukana tulee linkki valmiiseen hiekkalaatikkoon.

find_program(PYTHON3_EXECUTABLE python3)
if(PYTHON3_EXECUTABLE)
    set(WANDBOX_URL "https://wandbox.org/api/compile.json")

    add_custom_target(wandbox
        COMMAND
            ${PYTHON3_EXECUTABLE} wandbox.py mylib-example.cpp "${PROJECT_SOURCE_DIR}" include |
            curl -H "Content-type: application/json" -d @- ${WANDBOX_URL}
        WORKING_DIRECTORY
            ${CMAKE_CURRENT_SOURCE_DIR}
        DEPENDS
            mylib-unit-tests
    )
else()
    message(WARNING "Для создания онлайн-песочницы требуется интерпретатор ЯП python 3-й версии")
endif()

Projekti ulkona

Katsotaan nyt, kuinka tätä kaikkea käytetään.

kokoonpano

Tämän projektin, kuten minkä tahansa muun CMake-rakennusjärjestelmän projektin, rakentaminen koostuu kahdesta vaiheesta:

sukupolvi

cmake -S путь/к/исходникам -B путь/к/сборочной/директории [опции ...]

Jos yllä oleva komento ei toiminut vanhan CMake-version vuoksi, yritä jättää se pois -S:

cmake путь/к/исходникам -B путь/к/сборочной/директории [опции ...]

Lisätietoa vaihtoehdoista.

Projektin rakentaminen

cmake --build путь/к/сборочной/директории [--target target]

Lisää kokoonpanotavoitteista.

Valinnat

MYLIB_COVERAGE

cmake -S ... -B ... -DMYLIB_COVERAGE=ON [прочие опции ...]

Sisältää kohteen coverage, jolla voit alkaa mittaamaan koodin kattavuutta testeillä.

MYLIB_TESTAUS

cmake -S ... -B ... -DMYLIB_TESTING=OFF [прочие опции ...]

Tarjoaa mahdollisuuden poistaa käytöstä yksikön testikoon ja kohdistuksen check. Tämän seurauksena koodin peiton mittaus testeillä on kytketty pois päältä (katso. MYLIB_COVERAGE).

Testaus poistetaan myös automaattisesti käytöstä, jos projekti liitetään toiseen projektiin aliprojektina komennolla add_subdirectory.

MYLIB_DOXYGEN_LANGUAGE

cmake -S ... -B ... -DMYLIB_DOXYGEN_LANGUAGE=English [прочие опции ...]

Vaihtaa kohteen luoman dokumentaation kielen doc annettuun. Katso luettelo käytettävissä olevista kielistä Doxygen-järjestelmän verkkosivusto.

Venäjä on oletuksena käytössä.

Kokoonpanokohteet

Oletuksena

cmake --build path/to/build/directory
cmake --build path/to/build/directory --target all

Jos kohdetta ei ole määritetty (joka vastaa kohdetta all), kerää kaiken voitavansa ja kutsuu myös kohdetta check.

mylib-yksikkötestit

cmake --build path/to/build/directory --target mylib-unit-tests

Kokoaa yksikkötestejä. Oletuksena käytössä.

tarkastaa

cmake --build путь/к/сборочной/директории --target check

Suorittaa kerätyt (kerätyt, jos ei jo) yksikkötestit. Oletuksena käytössä.

См. также mylib-unit-tests.

kattavuus

cmake --build путь/к/сборочной/директории --target coverage

Analysoi käynnissä olevia (suorittaa, jos ei jo) yksikkötestejä koodin kattavuuden varalta ohjelmaa käyttävillä testeillä gcovr.

Pinnoitteen pakoputki näyttää suunnilleen tältä:

------------------------------------------------------------------------------
                           GCC Code Coverage Report
Directory: /path/to/cmakecpptemplate/include/
------------------------------------------------------------------------------
File                                       Lines    Exec  Cover   Missing
------------------------------------------------------------------------------
mylib/myfeature.hpp                            2       2   100%   
------------------------------------------------------------------------------
TOTAL                                          2       2   100%
------------------------------------------------------------------------------

Kohde on käytettävissä vain, kun vaihtoehto on käytössä MYLIB_COVERAGE.

См. также check.

telakka

cmake --build путь/к/сборочной/директории --target doc

Aloittaa koodidokumentaation luomisen järjestelmän avulla Happea.

sauvalaatikko

cmake --build путь/к/сборочной/директории --target wandbox

Palvelun vastaus näyttää tältä:

{
    "permlink" :    "QElvxuMzHgL9fqci",
    "status" :  "0",
    "url" : "https://wandbox.org/permlink/QElvxuMzHgL9fqci"
}

Palvelua käytetään tähän tarkoitukseen Wandbox. En tiedä kuinka joustavia heidän palvelimensa ovat, mutta mielestäni tätä mahdollisuutta ei pidä käyttää väärin.

Примеры

Rakenna projekti debug-tilassa kattavuuden mittauksella

cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Debug -DMYLIB_COVERAGE=ON
cmake --build путь/к/сборочной/директории --target coverage --parallel 16

Projektin asennus ilman esikokoonpanoa ja testausta

cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DMYLIB_TESTING=OFF -DCMAKE_INSTALL_PREFIX=путь/к/установойной/директории
cmake --build путь/к/сборочной/директории --target install

Rakenna julkaisutilassa tietyllä kääntäjällä

cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_PREFIX_PATH=путь/к/директории/куда/установлены/зависимости
cmake --build путь/к/сборочной/директории --parallel 4

Luodaan dokumentaatiota englanniksi

cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Release -DMYLIB_DOXYGEN_LANGUAGE=English
cmake --build путь/к/сборочной/директории --target doc

Työkalut

  1. CTee 3.13

    Itse asiassa CMake-versio 3.13 vaaditaan vain joidenkin tässä ohjeessa kuvattujen konsolikomentojen suorittamiseen. CMake-skriptien syntaksin kannalta versio 3.8 riittää, jos generointia kutsutaan muilla tavoilla.

  2. Testauskirjasto tohtori

    Testaus voidaan poistaa käytöstä (katso опцию MYLIB_TESTING).

  3. Happea

    Voit vaihtaa kieltä, jolla dokumentaatio luodaan, tarjotaan vaihtoehto MYLIB_DOXYGEN_LANGUAGE.

  4. Kielen tulkki Python 3

    Automaattiseen tuotantoon online-hiekkalaatikot.

Staattinen analyysi

CMaken ja muutaman hyvän työkalun avulla voit tehdä staattista analyysiä pienellä vaivalla.

Cppcheck

CMakessa on sisäänrakennettu tuki staattiselle analyysityökalulle Cppcheck.

Tätä varten sinun on käytettävä vaihtoehtoa CMAKE_CXX_CPPCHECK:

cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_CPPCHECK="cppcheck;--enable=all;-Iпуть/к/исходникам/include"

Tämän jälkeen staattinen analyysi käynnistetään automaattisesti aina, kun lähde käännetään ja käännetään uudelleen. Mitään ylimääräistä ei tarvitse tehdä.

Kalahtaa

Upean työkalun avulla scan-build Voit myös suorittaa staattisen analyysin hetkessä:

scan-build cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Debug
scan-build cmake --build путь/к/сборочной/директории

Tässä, toisin kuin Cppcheckin tapauksessa, sinun on suoritettava koontiversio joka kerta scan-build.

loppusanat

CMake on erittäin tehokas ja joustava järjestelmä, jonka avulla voit toteuttaa toimintoja jokaiseen makuun ja väriin. Ja vaikka syntaksi jättää joskus paljon toivomisen varaa, paholainen ei silti ole niin kauhea kuin se on maalattu. Käytä CMake build -järjestelmää yhteiskunnan ja terveyden hyödyksi.

Lataa projektimalli

Lähde: will.com

Lisää kommentti