CMake un C++ — brāļi uz mūžu

CMake un C++ — brāļi uz mūžu

Izstrādes procesā man patīk mainīt kompilatorus, kompilišanas režīmus, atkarību versijas, veikt statisko analīzi, mērīt veiktspēju, vākt pārklājumu, ģenerēt dokumentāciju utt. Un es ļoti mīlu CMake, jo tas ļauj man darīt visu, ko vēlos.

Daudzi kritizē CMake, un bieži vien pamatoti, bet, ja padziļināti aplūko, situācija nav tik smaga, un pēdējā laikā pat ļoti laba., un attīstības virziens ir diezgan pozitīvs.

Šajā piezīmē es vēlos pastāstīt, kā samērā vienkārši organizēt C++ galvenes bibliotēku CMake sistēmā, lai iegūtu šādu funkcionalitāti:

  1. Komplēšana;
  2. Testu automātiskā palaišana;
  3. Koda pārklājuma mērīšana;
  4. Instalācija;
  5. Automatizēta dokumentācija;
  6. Online smilšu kaste ģenerācija;
  7. Statiskā analīze.

Tie, kas saprot C++ un CMake, var vienkārši lejupielādēt projekta veidni un sākt to izmantot.


Satelīte

  1. Projekts no iekšpuses
    1. Projekta struktūra
    2. Galvenais CMake fails (./CMakeLists.txt)
      1. Informācija par projektu
      2. Projekta opcijas
      3. Kompilēšanas opcijas
      4. Galvenais mērķis
      5. Uzstādīšana
      6. Testi
      7. Dokumentācija
      8. Online smilšu kaste
    3. Tests skripts (test/CMakeLists.txt)
      1. Testēšana
      2. Pārklājums
    4. Dokumentācijas skripts (doc/CMakeLists.txt)
    5. Online smilšu kastes skripts (online/CMakeLists.txt)
  2. Projekts no ārpuses
    1. Montāža
      1. Ģenerēšana
      2. Montāža
    2. Opcijas
      1. MYLIB_COVERAGE
      2. MYLIB_TESTING
      3. MYLIB_DOXYGEN_LANGUAGE
    3. Komplēšanas mērķi
      1. Pa noklusējumu
      2. mylib-unit-tests
      3. check
      4. coverage
      5. doc
      6. wandbox
    4. Piemēri
  3. Rīki
  4. Statiskā analīze
  5. Pēcvārds

Projekts no iekšpuses

Projekta struktūra

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

Galvenokārt tiks runāts par to, kā organizēt CMake skriptus, tāpēc tie tiks detalizēti apskatīti. Pārējos failus ikviens var aplūkot tieši projekta veidnes lapā.

Galvenais CMake fails (./CMakeLists.txt)

Informācija par projektu

Vispirms ir jānorāda vajadzīgā CMake sistēmas versija. CMake attīstās, mainās komandu paraksti, uzvedība dažādās situācijās. Lai CMake nekavējoties saprastu, ko mēs no tā vēlamies, ir jāpieraksta mūsu prasības.

cmake_minimum_required(VERSION 3.13)

Pēc tam jānorāda mūsu projekts, tā nosaukums, versija, izmantotās valodas un citas īpašības (skat. komandu project).

Šajā gadījumā norādām valodu CXX (tātad C++), lai CMake neuztrauktos un meklētu C valodas kompilatoru (pa noklusējumu CMake ir iekļautas divas valodas: C un C++).

project(Mylib VERSION 1.0 LANGUAGES CXX)

Šeit var tūlīt pārbaudīt, vai mūsu projekts ir iekļauts citā projektā kā apakšprojekts. Tas ļoti palīdzēs turpmāk.

get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)

Projekta opcijas

Apsvērsim divas iespējas.

Pirmā iespēja — MYLIB_TESTING — atslēgt moduļa testus. Tas var būt nepieciešams, ja esam pārliecināti, ka testi ir kārtībā, bet, piemēram, vēlamies tikai uzstādīt vai sapakot mūsu projektu. Vai arī mūsu projekts ir iekļauts kā apakšprojekts — šajā gadījumā mūsu projekta lietotājam nav interesanti palaist mūsu testus. Jūs taču netestējat atkarības, ko lietojat?

option(MYLIB_TESTING "Iekļaut moduļa testēšanu" ON)

Turklāt mēs izveidosim atsevišķu iespēju MYLIB_COVERAGE koda pārklājuma mērījumiem testos, bet tā prasīs papildu rīkus, tāpēc to būs jāieslēdz skaidri.

option(MYLIB_COVERAGE "Iekļaut koda pārklājuma mērījumus testos" OFF)

Kompilēšanas opcijas

Protams, mēs esam forši C++ programmētāji, tāpēc vēlamies maksimālas kompilatora diagnosticēšanas iespējas kompilācijas laikā. Neviena pele neizlīdīs garām.

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
)

Paplašinājumus mēs arī atslēgsim, lai pilnībā atbilstu C++ valodas standartam. CMake pēc noklusējuma tos iekļauj.

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

Galvenais mērķis

Mūsu bibliotēka sastāv tikai no galvenes failiem, tāpēc mums nav neviena iznākuma statisku vai dinamisku bibliotēku veidā. No otras puses, lai izmantotu mūsu bibliotēku no ārpuses, to ir jāsaraksta un jānodrošina, lai tā būtu atklājama sistēmā un saistīta ar savu projektu, kā arī, iespējams, dažām papildu īpašībām.

Šim mērķim izveidojam saskarnes bibliotēku.

add_library(mylib INTERFACE)

Saistām galvenes failus ar mūsu saskarnes bibliotēku.

Mūsdienīga, stilīga, jaunatnes izmantota CMake nozīmē, ka galvenes, īpašības utt. tiek nodotas caur vienu mērķi. Tādējādi ir pietiekami pateikt target_link_libraries(target PRIVATE dependency), un visas galvenes, kuras ir saistītas ar mērķi dependency, būs pieejamas avota failiem, kas pieder mērķim target. Un nav nepieciešamas nekādas [target_]include_directoriesTas tiks parādīts zemāk, analizējot CMake skriptu modulāriem testiem.

Tāpat jāņem vērā, tā dēvētie izteiksmes ģeneratori: $.

Šī komanda asociē vajadzīgos galvene ar mūsu interfeisa bibliotēku; ja mūsu bibliotēka tiks piesaistīta kādai mērķim vienā CMake hierarhijā, tad ar to tiks asociētas galvenes no direktorijas ${CMAKE_CURRENT_SOURCE_DIR}/include, un ja mūsu bibliotēka ir instalēta sistēmā un piesaistīta citā projektā ar komandu find_package, tad ar to tiks asociētas galvenes no direktorijas include attiecībā uz instalācijas direktoriju.

target_include_directories(mylib INTERFACE
    $
    $
)

Iestādīsim valodas standartu. Protams, pašu jaunāko. Tajā pašā laikā mēs ne tikai iekļaujam standartu, bet arī padarām to pieejamu tiem, kas izmantos mūsu bibliotēku. To panāk, izmantojot īpašību, kurai ir kategorija INTERFACE (skat. komandu target_compile_features).

target_compile_features(mylib INTERFACE cxx_std_17)

Izveidosim pseidonīmu mūsu bibliotēkai. Turklāt, lai tas izskatītos labāk, tas būs īpašā "nosaukumu telpā". Tas būs noderīgi, kad mūsu bibliotēkā parādīsies dažādi moduļi, un mēs varēsim tos pievienot neatkarīgi vienam no otra. Kā, piemēram, Boostā.

add_library(Mylib::mylib ALIAS mylib)

Uzstādīšana

Mūsu galvju instalēšana sistēmā. Te viss ir vienkārši. Sakām, ka mappa ar visām galvenēm jānonāk direktorijā include attiecībā uz instalācijas vietu.

install(DIRECTORY include/mylib DESTINATION include)

Pēc tam informējam būves sistēmu, ka vēlamies iespēju trešo pušu projektos izsist komandu find_package(Mylib) un saņemt mērķi Mylib::mylib.

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

Nākamais burvju vārds jāsaprot šādi. Kad trešā pušu projektā izsauksim komandu find_package(Mylib 1.2.3 REQUIRED), un ja reālā uzstādītās bibliotēkas versija izrādīsies nesavienojama ar versiju 1.2.3, CMake automātiski ģenerēs kļūdu. Tas nozīmē, ka nav nepieciešams manuāli sekot līdzi versijām.

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)

Testi

Ja testi ir izslēgti skaidri, izmantojot atbilstošo opciju vai mūsu projekts ir apakšprojekts, tas ir, pievienots citam CMake projektam, izmantojot komandu add_subdirectory, mēs nevirzāmies tālāk pa hierarhiju, un skripts, kurā aprakstītas komandas testu ģenerēšanai un palaišanai, vienkārši netiek izpildīts.

if(NOT MYLIB_TESTING)
    message(STATUS "Mylib projekta testēšana ir izslēgta")
elseif(IS_SUBPROJECT)
    message(STATUS "Mylib netiek testēts apakšmoduļa režīmā")
else()
    add_subdirectory(test)
endif()

Dokumentācija

Dokumentācija arī netiks ģenerēta, ja tas ir apakšprojekts.

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

Online smilšu kaste

Līdzīgi, tiešsaistes smilšu kastes apakšprojektā arī nebūs.

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

Tests skripts (test/CMakeLists.txt)

Testēšana

Pirmkārt, atrodam paketi ar nepieciešamo testēšanas ietvaru (aizstājiet ar savu iecienītāko).

find_package(doctest 2.3.3 REQUIRED)

Izveidojam mūsu izpildāmo failu ar testiem. Parasti tieši izpildāmā binārā faila iekšienē es pievienoju tikai failu, kurā būs funkcija main.

add_executable(mylib-unit-tests test_main.cpp)

Savukārt failus, kuros aprakstīti paši testi, pievienoju vēlāk. Bet tas nav obligāti.

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

Pievienojam atkarības. Ņemiet vērā, ka mūsu binārās iekārtas ir piesaistītas tikai nepieciešamajām CMake mērķiem, un nav izsauktas komandas target_include_directories. Testēšanas ietvara galvenes un no mūsu Mylib::mylib, kā arī būvniecības parametri (mūsu gadījumā tas ir C++ valodas standarts) tika iekļauti kopā ar šiem mērķiem.

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

Visbeidzot, izveidojam viltus mērķi, kura „būvēšana” ir ekvivalenta testu palaišanai, un pievienojam šo mērķi noklusējuma būvniecībai (to nodrošina atribūts ALL). Tas nozīmē, ka noklusējuma būvniecība iniciē testu palaišanu, tas ir, mēs nekad neaizmirsīsim tos palaist.

add_custom_target(check ALL COMMAND mylib-unit-tests)

Pārklājums

Tālāk ieslēdzam kodu pārklājuma mērīšanu, ja ir norādīta attiecīgā opcija. Iedziļināties detaļās neplānoju, jo tās vairāk attiecas uz rīku pārklājuma mērīšanai, nekā uz CMake. Svarīgi tikai atzīmēt, ka saskaņā ar rezultātiem tiks izveidots mērķis coverage, ar kuru ērti veikt pārklājuma mērījumus.

find_program(GCOVR_EXECUTABLE gcovr)
if(MYLIB_COVERAGE AND GCOVR_EXECUTABLE)
    message(STATUS "Testu nodrošināšana iekļauta")

    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 "Testu nodrošināšanai nepieciešams rīks gcovr")
endif()

Dokumentācijas skripts (doc/CMakeLists.txt)

Atrasts Doxygen.

find_package(Doxygen)

Pārbaudām, vai lietotājs ir norādījis valodu. Ja jā, tad atstājam to, ja nē, tad ņemam krievu. Pēc tam konfigurējam Doxygen sistēmas failus. Visi nepieciešamie mainīgie, tostarp valoda, nonāk tur konfigurācijas procesā (skat. komandu configure_file).

Pēc tam izveidojam mērķi doc, kas uzsāks dokumentācijas ģenerēšanu. Ņemot vērā, ka dokumentācijas ģenerēšana nav vissvarīgākā attīstības procesa daļa, pēc noklusējuma mērķis nebūs iekļauts, to vajadzēs iedarbināt manuāli.

if (Doxygen_FOUND)
    if (NOT MYLIB_DOXYGEN_LANGUAGE)
        set(MYLIB_DOXYGEN_LANGUAGE Latvian)
    endif()
    message(STATUS "Doxygen dokumentācija tiks ģenerēta ${MYLIB_DOXYGEN_LANGUAGE} valodā")
    configure_file(Doxyfile.in Doxyfile)
    add_custom_target(doc COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
endif ()

Online smilšu kastes skripts (online/CMakeLists.txt)

Šeit meklējam trešo Python un izveidojam mērķi wandbox, kas ģenerēs pieprasījumu, kas atbilst pakalpojuma API Wandbox, un nosūtām to. Atbildē tiek saņemta saite uz gatavo smilškasti.

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 "Tiešsaistes smilškastes izveidei nepieciešams Python 3 interpretators")
endif()

Projekts no ārpuses

Tagad aplūkosim, kā to visu izmantot.

Montāža

Šī projekta kompilācija, tāpat kā jebkura cita projekta kompilācija CMake sistēmā, sastāv no diviem posmiem:

Ģenerēšana

cmake -S ceļš/uz/avotiem -B ceļš/uz/kompilācijas/ direktoriju [opcijas ...]

Ja iepriekšējā komanda neizdevās vecas CMake versijas dēļ, mēģiniet izlaist -S:

cmake ceļš/uz/avotiem -B ceļš/uz/kompilācijas/ direktoriju [opcijas ...]

Vairāk par opcijām.

Projekta kompilācija

cmake --build ceļš/uz/kompilācijas/ direktoriju [--target target]

Vairāk par kompilācijas mērķiem.

Opcijas

MYLIB_COVERAGE

cmake -S ... -B ... -DMYLIB_COVERAGE=ON [citas opcijas ...]

Iekļauj mērķi coverage, ar kuru var uzsākt koda seguma testēšanu.

MYLIB_TESTING

cmake -S ... -B ... -DMYLIB_TESTING=OFF [citi opcijas ...]

Piedāvā iespēju izslēgt moduļu testu kompilāciju un mērķi check. Līdz ar to tiek izslēgta koda seguma testēšana (skat. MYLIB_COVERAGE).

Testēšana automātiski tiek izslēgta, ja projekts tiek pievienots citam projektam kā apakšprojekts ar komandu add_subdirectory.

MYLIB_DOXYGEN_LANGUAGE

cmake -S ... -B ... -DMYLIB_DOXYGEN_LANGUAGE=English [citi opcijas ...]

Pārslēdz dokumentācijas valodu, ko ģenerē mērķis doc uz norādīto. Pieejamo valodu sarakstu skatiet Doxygen sistēmas tīmekļa vietnē.

Noklusējuma valoda ir krievu.

Komplēšanas mērķi

Pa noklusējumu

cmake --build ceļš/to/savākšanas/direktoriju
cmake --build ceļš/to/savākšanas/direktoriju --target all

Ja mērķis nav norādīts (kas ir ekvivalents mērķim all), izveido visu, ko var, un arī izpilda mērķi check.

mylib-unit-tests

cmake --build ceļš/to/savākšanas/direktoriju --target mylib-unit-tests

Kompilē moduļu testus. Noklusējumā iekļauts.

check

cmake --build ceļš/k/savākšanas/direktoriju --target check

Izpilda esošos (izveido, ja vēl nav) moduļu testus. Noklusējumā iekļauts.

Skatīt arī mylib-unit-tests.

coverage

cmake --build ceļš/k/savākšanas/direktoriju --target coverage

Analizē izpildītos (izveido, ja vēl nav) moduļu testus koda seguma testēšanai, izmantojot programmu gcovr.

Seguma iznākums izskatīsies aptuveni šādi:

------------------------------------------------------------------------------
                           GCC Code Coverage Report
Direktorija: /path/to/cmakecpptemplate/include/
------------------------------------------------------------------------------
Fails                                        Līnijas    Izpildīta  Segums   Trūkst
------------------------------------------------------------------------------
mylib/myfeature.hpp                            2       2   100%   
------------------------------------------------------------------------------
KOPĀ                                          2       2   100%
------------------------------------------------------------------------------

Mērķis ir pieejams tikai, ja opcija ir ieslēgta MYLIB_COVERAGE.

Skatīt arī check.

doc

cmake --build ceļš/k/savākšanas/direktoriju --target doc

Uzsāk dokumentācijas ģenerēšanu koda piekārtošanai, izmantojot sistēmu Doxygen.

wandbox

cmake --build ceļš/k/savākšanas/direktoriju --target wandbox

Atbilde no pakalpojuma izskatīsies aptuveni tā:

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

Tam tiek izmantots pakalpojums Wandbox. Nezinu, cik elastīgi ir viņu serveri, bet domāju, ka šo iespēju nevajadzētu ļaunprātīgi izmantot.

Piemēri

Projekta kompilācija kļūdu režīmā ar koda seguma mērīšanu

cmake -S ceļš/k/avotiem -B ceļš/k/savākšanas/direktorijai -DCMAKE_BUILD_TYPE=Debug -DMYLIB_COVERAGE=ON
cmake --build ceļš/k/savākšanas/direktorijai --target coverage --parallel 16

Projekta uzstādīšana bez iepriekšējās kompilācijas un testēšanas

cmake -S ceļš/k/avotiem -B ceļš/k/kompilācijas/direktorijai -DMYLIB_TESTING=OFF -DCMAKE_INSTALL_PREFIX=ceļš/k/uzstādīšanas/direktorijai
cmake --build ceļš/k/kompilācijas/direktorijai --target install

Kompilācija izlaišanas režīmā ar norādīto kompilētāju

cmake -S ceļš/k/avotiem -B ceļš/k/kompilācijas/direktorijai -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_PREFIX_PATH=ceļš/k/direktorijai/kura/ir/instalētas/atkarības
cmake --build ceļš/k/kompilācijas/direktorijai --parallel 4

Dokumentācijas ģenerēšana angļu valodā

cmake -S ceļš/k/avotiem -B ceļš/k/kompilācijas/direktorijai -DCMAKE_BUILD_TYPE=Release -DMYLIB_DOXYGEN_LANGUAGE=English
cmake --build ceļš/k/kompilācijas/direktorijai --target doc

Rīki

  1. CMake 3.13

    Patiesībā CMake 3.13 versija ir nepieciešama tikai, lai izpildītu dažas konsoles komandas, kas aprakstītas šajā rokasgrāmatā. No CMake skriptu sintakses viedokļa pietiek ar 3.8 versiju, ja ģenerēšanu izsauc citos veidos.

  2. Testēšanas bibliotēka doctest

    Testēšanu var atslēgt (skat. opciju MYLIB_TESTING).

  3. Doxygen

    Lai mainītu valodu, kurā tiks ģenerēta dokumentācija, ir paredzēta iespēja MYLIB_DOXYGEN_LANGUAGE.

  4. Programmas interpretators Python 3

    Automātiskai ģenerēšanai tiešsaistes smilšu kaste.

Statiskā analīze

Ar CMake un pāris labiem rīkiem var nodrošināt statisko analīzi ar minimālu piepūli.

Cppcheck

CMake ir iebūvēta atbalsta sistēma, lai veiktu statisko analīzi Cppcheck.

Tam nepieciešams izmantot iespēju CMAKE_CXX_CPPCHECK:

cmake -S ceļš/k/avotiem -B ceļš/k/kompilācijas/direktorijai -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_CPPCHECK="cppcheck;--enable=all;-Iceļš/k/avotiem/include"

Pēc tam statiskā analīze tiks automātiski uzsākta katru reizi, kad notiks kompilācija un rekompilācija. Nekas papildus nav jādara.

Clang

Ar brīnišķīgu rīku scan-build tāpat var viegli uzsākt statisko analīzi:

scan-build cmake -S ceļš/k/avotiem -B ceļš/k/kompilācijas/direktorijai -DCMAKE_BUILD_TYPE=Debug
scan-build cmake --build ceļš/k/kompilācijas/direktorijai

Šeit, atšķirībā no Cppcheck gadījuma, katru reizi ir jāuzsāk kompilācija caur scan-build.

Pēcvārds

CMake ir ļoti jaudīga un elastīga sistēma, kas ļauj realizēt visu iespējamo funkcionalitāti. Un, lai gan sintakse dažreiz atstāj vēlmi pēc labākā, tomēr nekas nav tik slikti, kā to apraksta. Izmantojiet CMake kompilācijas sistēmu sabiedrības labā un savai labklājībai.

→ Lejupielādēt projekta veidni

Avots: habr.com

Iegādājieties uzticamu tīmekļa hostingu ar DDoS aizsardzību, VPS VDS serveriem 🔥 Iegādājieties uzticamu tīmekļa hostingu ar DDoS aizsardzību, VPS VDS serveriem | ProHoster