CMake ja C++ on igavesti vennad

CMake ja C++ on igavesti vennad

Mulle meeldib arenduse käigus vahetada kompilaatoreid, koostada režiime, sõltuvusversioone, teha staatilist analüüsi, mõõta jõudlust, koguda katvust, genereerida dokumentatsiooni jne. Ja ma armastan CMake'i väga, sest see võimaldab mul teha kõike, mida tahan.

Paljud kiruvad CMake'i ja sageli ka teenitult, aga kui vaadata, siis pole see nii hull, aga viimasel ajal pole üldse paha, ja arengusuund on üsna positiivne.

Selles märkuses tahan teile öelda, kui lihtne on korraldada C++ päiseteegi CMake süsteemis, et saada järgmised funktsioonid:

  1. kokkupanek;
  2. automaatkäivitustestid;
  3. Koodi katvuse mõõtmine;
  4. paigaldus;
  5. Autodokumentatsioon;
  6. Interneti-liivakasti genereerimine;
  7. Staatiline analüüs.

Kes juba mõistab plusse ja si-make saab lihtsalt laadige alla projekti mall ja hakka seda kasutama.


Sisu

  1. Projekt seestpoolt
    1. Projekti struktuur
    2. Peamine CMake fail (./CMakeLists.txt)
      1. Projekti teave
      2. Projekti valikud
      3. Koostamise valikud
      4. Peamine eesmärk
      5. Paigaldamine
      6. Testid
      7. Документация
      8. Interneti-liivakast
    3. Testide skript (test/CMakeLists.txt)
      1. Katsetamine
      2. Coverage
    4. Dokumentatsiooni skript (doc/CMakeLists.txt)
    5. Skript võrguliivakasti jaoks (online/CMakeLists.txt)
  2. projekt väljaspool
    1. Assamblee
      1. Põlvkond
      2. Assamblee
    2. Valikud
      1. MYLIB_COVERAGE
      2. MYLIB_TESTING
      3. MYLIB_DOXYGEN_LANGUAGE
    3. kokkupaneku sihtmärgid
      1. Vaikimisi
      2. mylib-ühiku testid
      3. kontrollima
      4. katmine
      5. doc
      6. võlukast
    4. näited
  3. Töövahendid
  4. Staatiline analüüs
  5. järelsõna

Projekt seestpoolt

Projekti struktuur

.
├── 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

See keskendub peamiselt CMake skriptide korraldamisele, nii et neid analüüsitakse üksikasjalikult. Ülejäänud faile saab igaüks otse vaadata. malliprojekti lehel.

Peamine CMake fail (./CMakeLists.txt)

Projekti teave

Kõigepealt peate taotlema CMake süsteemi soovitud versiooni. CMake areneb, käsusignatuurid muutuvad, käitumine erinevates tingimustes. Selleks, et CMake saaks kohe aru, mida me sellelt tahame, peame kohe oma nõuded sellele paika panema.

cmake_minimum_required(VERSION 3.13)

Seejärel tähistame oma projekti, selle nime, versiooni, kasutatavaid keeli jne. команду project).

Sel juhul määrake keel CXX (mis tähendab C++), et CMake ei viitsiks C-kompilaatorit otsida (vaikimisi on CMake'is kaks keelt: C ja C++).

project(Mylib VERSION 1.0 LANGUAGES CXX)

Siin saate koheselt kontrollida, kas meie projekt on alamprojektina kaasatud mõnda teise projekti. Sellest on tulevikus palju abi.

get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)

Projekti valikud

Vaatleme kahte võimalust.

Esimene variant on MYLIB_TESTING - ühikutestide keelamiseks. Seda võib vaja minna, kui oleme kindlad, et testidega on kõik korras ja tahame näiteks oma projekti ainult installida või pakkida. Või on meie projekt kaasatud alamprojektina – sellisel juhul pole meie projekti kasutaja meie testide läbiviimisest huvitatud. Sa ei testi kasutatavaid sõltuvusi, eks?

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

Lisaks teeme eraldi valiku MYLIB_COVERAGE koodi katvuse mõõtmiseks testidega, kuid see nõuab täiendavaid tööriistu, seega peate selle selgesõnaliselt lubama.

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

Koostamise valikud

Muidugi oleme lahedad positiivsed programmeerijad, seega tahame kompilaatorilt maksimaalset kompileerimisaegset diagnostikat. Ükski hiir ei pääse läbi.

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
)

Keelame ka laiendused, et need järgiksid täielikult C ++ keelestandardit. Need on CMake'is vaikimisi lubatud.

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

Peamine eesmärk

Meie teek koosneb ainult päisefailidest, mis tähendab, et meil pole staatiliste või dünaamiliste teekide kujul väljundit. Teisest küljest, et kasutada meie teeki väljastpoolt, peate selle installima, leidma selle süsteemist ja ühendama selle oma projektiga ning samal ajal ka need päised, kui võimalik, et mõned lisaomadused.

Selleks loome liidese raamatukogu.

add_library(mylib INTERFACE)

Seome päised oma liidese teegiga.

Kaasaegne, trendikas, nooruslik CMake kasutamine tähendab, et päised, omadused jne. edastatakse ühe sihtmärgi kaudu. Nii et piisab, kui öelda target_link_libraries(target PRIVATE dependency)ja kõik päised, mis on sihtmärgiga seotud dependency, on sihtmärgile kuuluvate allikate jaoks saadaval target. Ja sa ei vaja ühtegi [target_]include_directories. Seda näidatakse allpool sõelumisel CMake skript ühikutestide jaoks.

Tähelepanu tasub pöörata ka nn. выражения-генераторы: $<...>.

See käsk seob meile vajalikud päised meie liidese teegiga ja kui meie teek on ühendatud mis tahes sihtmärgiga samas CMake'i hierarhias, seostatakse kataloogi päised sellega ${CMAKE_CURRENT_SOURCE_DIR}/include, ja kui meie teek on süsteemi installitud ja ühendatud teise projektiga käsu abil find_package, siis seostatakse sellega kataloogi päised include installikataloogi suhtes.

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

Määrake keelestandard. Muidugi kõige uuem. Samal ajal me mitte ainult ei lisa standardit, vaid levitame seda ka neile, kes meie raamatukogu kasutama hakkavad. See saavutatakse, kui seatud atribuudil on kategooria INTERFACE (cm. target_compile_features käsk).

target_compile_features(mylib INTERFACE cxx_std_17)

Saame oma raamatukogule varjunime. Ja ilu jaoks on see spetsiaalses "nimeruumis". See on kasulik, kui meie teeki ilmuvad erinevad moodulid ja me hakkame neid üksteisest sõltumatult ühendama. Nagu näiteks Bustal.

add_library(Mylib::mylib ALIAS mylib)

Paigaldamine

Meie päiste installimine süsteemi. Siin on kõik lihtne. Me ütleme, et kaust koos kõigi päistega peaks langema kataloogi include paigalduskoha kohta.

install(DIRECTORY include/mylib DESTINATION include)

Järgmisena ütleme ehitussüsteemile, et tahame kolmanda osapoole projektides käsu kutsuda find_package(Mylib) ja saada sihtmärk Mylib::mylib.

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

Järgmist loitsu tuleb mõista nii. Kui kõrvalprojektis kutsume käsku find_package(Mylib 1.2.3 REQUIRED), ja samal ajal ei ühildu installitud teegi tegelik versioon versiooniga 1.2.3, CMake genereerib automaatselt vea. See tähendab, et te ei pea versioone käsitsi jälgima.

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)

Testid

Kui testid on selgesõnaliselt keelatud vastav variant või meie projekt on alamprojekt, see tähendab, et see on käsu abil ühendatud teise CMake projektiga add_subdirectory, me ei lähe hierarhias allapoole ja skript, mis kirjeldab testide genereerimise ja käitamise käske, lihtsalt ei käivitu.

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

Документация

Dokumentatsiooni ei genereerita ka alamprojekti puhul.

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

Interneti-liivakast

Samamoodi ei ole alamprojektil ka veebiliivakasti.

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

Testide skript (test/CMakeLists.txt)

Katsetamine

Kõigepealt leiame soovitud testraamistikuga paketi (asenda see oma lemmikuga).

find_package(doctest 2.3.3 REQUIRED)

Loome oma käivitatava faili testidega. Tavaliselt lisan otse käivitatavasse kahendfaili ainult faili, milles funktsioon asub main.

add_executable(mylib-unit-tests test_main.cpp)

Ja failid, milles testid ise on kirjeldatud, lisan hiljem. Kuid seda pole vaja teha.

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

Me ühendame sõltuvused. Pange tähele, et lisasime oma binaarfailile ainult vajalikud CMake'i sihtmärgid ega kutsunud käsku target_include_directories. Testraamistiku ja meie päised Mylib::mylib, aga ka koostamise valikud (meie puhul C++ keelestandard) roomasid koos nende sihtmärkidega.

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

Lõpuks loome näiva sihtmärgi, mille ehitamine on samaväärne testide käitamisega ja lisame selle sihtmärgi vaikeehitusele (selle eest vastutab atribuut ALL). See tähendab, et vaikejärg käivitab testid, mis tähendab, et me ei unusta kunagi neid käivitada.

add_custom_target(check ALL COMMAND mylib-unit-tests)

Coverage

Järgmiseks lülitage sisse koodi katvuse mõõtmine, kui vastav valik on määratud. Ma ei hakka detailidesse laskuma, sest need on rohkem seotud katvuse mõõtmise tööriistaga kui CMake'iga. Oluline on ainult märkida, et tulemused loovad eesmärgi coverage, millega on mugav alustada katvuse mõõtmist.

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()

Dokumentatsiooni skript (doc/CMakeLists.txt)

Leiti Doxygen.

find_package(Doxygen)

Järgmisena kontrollime, kas keelega muutuja on kasutaja poolt määratud. Kui jah, siis me seda ei puuduta, kui ei, siis võtame vene keele. Seejärel konfigureerime Doxygeni süsteemifailid. Kõik vajalikud muutujad, sealhulgas keel, jõuavad sinna konfiguratsiooniprotsessi käigus (vt. команду configure_file).

Seejärel loome eesmärgi doc, mis hakkab dokumentatsiooni looma. Kuna dokumentatsiooni genereerimine pole arendusprotsessis kõige suurem vajadus, siis sihtmärki vaikimisi ei kaasata, see tuleb eksplitsiitselt käivitada.

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 ()

Skript võrguliivakasti jaoks (online/CMakeLists.txt)

Siit leiame kolmanda Pythoni ja loome sihtmärgi wandbox, mis genereerib teenuse API-le vastava päringu Wandboxja saadab selle. Vastuseks tuleb link valmis liivakastile.

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()

projekt väljaspool

Nüüd vaatame, kuidas seda kõike kasutada.

Assamblee

Selle projekti, nagu iga teise CMake ehitussüsteemi projekti, ehitamine koosneb kahest etapist:

Põlvkond

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

Kui ülaltoodud käsk CMake'i vanema versiooni tõttu ei töötanud, proovige see ära jätta -S:

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

Lisateavet valikute kohta.

Projekti ehitamine

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

Lisateavet kokkupaneku eesmärkide kohta.

Valikud

MYLIB_COVERAGE

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

Sisaldab sihtmärki coverage, millega saab hakata testidega koodikatvust mõõtma.

MYLIB_TESTING

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

Pakub võimalust üksuse testi koostamine ja sihtimine välja lülitada check. Selle tulemusena lülitatakse koodi katvuse mõõtmine testidega välja (vt MYLIB_COVERAGE).

Samuti keelatakse testimine automaatselt, kui projekt on käsu abil alamprojektina ühendatud teise projektiga add_subdirectory.

MYLIB_DOXYGEN_LANGUAGE

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

Vahetab sihtmärgi loodud dokumentatsiooni keelt doc antud ühele. Saadaolevate keelte loendi vaatamiseks vt Doxygeni veebisait.

Vene keel on vaikimisi lubatud.

kokkupaneku sihtmärgid

Vaikimisi

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

Kui sihtmärki pole määratud (mis on samaväärne sihtmärgiga all), kogub kõik võimaliku ja helistab ka sihtmärgile check.

mylib-ühiku testid

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

Koostab ühikteste. Vaikimisi lubatud.

kontrollima

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

Käivitab ehitatud (ehitab, kui mitte juba) ühikuteste. Vaikimisi lubatud.

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

katmine

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

Analüüsib jooksvaid (käitab, kui mitte veel) ühikuteste, et leida programmi abil testides koodi katvus gcovr.

Katvuse väljund näeb välja umbes selline:

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

Sihtmärk on saadaval ainult siis, kui valik on lubatud. MYLIB_COVERAGE.

См. также check.

doc

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

Käivitab süsteemi abil koodi dokumentatsiooni genereerimise Doksüd.

võlukast

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

Teenuse vastus näeb välja umbes selline:

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

Teenust kasutatakse selleks. Wandbox. Ma ei tea, kui kummist serverid neil on, kuid arvan, et te ei tohiks seda funktsiooni kuritarvitada.

näited

Ehitage projekt silumisrežiimis koos katvuse mõõtmisega

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

Projekti paigaldamine ilma eelehituse ja testimiseta

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

Väljalaskerežiimis ehitatud antud kompilaatori poolt

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

Ingliskeelse dokumentatsiooni genereerimine

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

Töövahendid

  1. CMake 3.13

    Tegelikult on CMake'i versioon 3.13 vajalik vaid mõne selles spikris kirjeldatud konsoolikäskude käitamiseks. CMake skriptide süntaksi seisukohalt piisab versioonist 3.8, kui genereerimist kutsutakse välja muul viisil.

  2. Testimine raamatukogu doktorant

    Testimise saab keelata (vt опцию MYLIB_TESTING).

  3. Doksüd

    Dokumentatsiooni loomise keele vahetamiseks on võimalus MYLIB_DOXYGEN_LANGUAGE.

  4. PL tõlk Python 3

    Automaatseks genereerimiseks Interneti-liivakastid.

Staatiline analüüs

CMake'i ja paari hea tööriista abil saate minimaalse askeldamisega staatilist analüüsi teha.

Cppcheck

CMake'i sisseehitatud staatilise analüüsi tööriista tugi Cppcheck.

Selleks kasutage valikut CMAKE_CXX_CPPCHECK:

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

Pärast seda käivitatakse staatiline analüüs automaatselt iga kord allikate koostamise ja uuesti kompileerimise ajal. Midagi ekstra pole vaja teha.

Klähvima

Imelise tööriistaga scan-build Samuti saate kiiresti käivitada staatilise analüüsi:

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

Erinevalt Cppchecki puhul tuleb siin ehitamine iga kord läbi viia scan-build.

järelsõna

CMake on väga võimas ja paindlik süsteem, mis võimaldab rakendada funktsionaalsust igale maitsele ja värvile. Ja kuigi süntaks jätab mõnikord soovida, pole kurat ikkagi nii kohutav, kui teda maalitakse. Kasutage CMake ehitussüsteemi ühiskonna ja tervise huvides.

Laadige alla projekti mall

Allikas: www.habr.com

Lisa kommentaar