Izstrādes laikā man patīk mainīt kompilatorus, veidot režīmus, atkarības 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 es vēlos.
Daudzi cilvēki kritizē CMake, un bieži vien pelnīti, bet, ja paskatās, tad ne viss ir tik slikti, un nesen vispār nav slikti, un attīstības virziens ir diezgan pozitīvs.
Šajā piezīmē es vēlos jums pastāstīt, kā vienkārši organizēt galvenes bibliotēku programmā C++ sistēmā CMake, lai iegūtu šādu funkcionalitāti:
Montāža;
automātiskās palaišanas testi;
Koda pārklājuma mērīšana;
Uzstādīšana;
automātiskā dokumentācija;
Tiešsaistes smilškastes ģenerēšana;
Statiskā analīze.
Ikviens, kurš jau saprot priekšrocības un C-make, var vienkārši lejupielādēt projekta veidni un sāciet to lietot.
Mēs galvenokārt runāsim par to, kā organizēt CMake skriptus, tāpēc tie tiks apspriesti detalizēti. Ikviens var tieši skatīt pārējos failus veidnes projekta lapā.
Pirmkārt, jums ir jāpieprasa nepieciešamā CMake sistēmas versija. CMake attīstās, komandu paraksti un uzvedība dažādos apstākļos mainās. Lai CMake uzreiz saprastu, ko mēs no tā vēlamies, mums nekavējoties jāreģistrē savas prasības tai.
cmake_minimum_required(VERSION 3.13)
Pēc tam mēs norādīsim mūsu projektu, tā nosaukumu, versiju, izmantotās valodas utt. (sk. команду project).
Šajā gadījumā mēs norādām valodu CXX (un tas nozīmē C++), lai CMake nenoslogotos un nemeklētu C valodas kompilatoru (pēc noklusējuma CMake ietver divas valodas: C un C++).
project(Mylib VERSION 1.0 LANGUAGES CXX)
Šeit jūs varat uzreiz pārbaudīt, vai mūsu projekts ir iekļauts kādā citā projektā kā apakšprojekts. Tas ļoti palīdzēs nākotnē.
Pirmais variants ir MYLIB_TESTING — lai atspējotu vienību testus. Tas var būt nepieciešams, ja esam pārliecināti, ka ar testiem viss ir kārtībā, bet mēs vēlamies tikai, piemēram, instalēt vai pakotēt mūsu projektu. Vai arī mūsu projekts ir iekļauts kā apakšprojekts - šajā gadījumā mūsu projekta lietotājs nav ieinteresēts veikt mūsu testus. Jūs taču nepārbaudāt izmantotās atkarības, vai ne?
Turklāt mēs izveidosim atsevišķu iespēju MYLIB_COVERAGE koda pārklājuma mērīšanai ar testiem, taču tam būs nepieciešami papildu rīki, tāpēc tas būs skaidri jāiespējo.
Protams, mēs esam forši plus programmētāji, tāpēc vēlamies no kompilatora nodrošināt maksimālu kompilēšanas laika diagnostiku. Neviena pele neizslīdēs cauri.
Mūsu bibliotēkā ir tikai galvenes faili, kas nozīmē, ka mums nav nekādu izplūdumu statisku vai dinamisku bibliotēku veidā. No otras puses, lai izmantotu mūsu bibliotēku ārēji, tai ir jābūt instalētai, tai jābūt nosakāmai sistēmā un jāsavieno ar jūsu projektu, un tajā pašā laikā šīm pašām galvenes, kā arī, iespējams, dažas papildu, tam ir pievienotas īpašības.
Šim nolūkam mēs izveidojam interfeisa bibliotēku.
add_library(mylib INTERFACE)
Mēs saistām galvenes ar mūsu interfeisa bibliotēku.
Mūsdienīgs, moderns, jauniešu lietojums CMake nozīmē, ka galvenes, rekvizīti utt. pārraida caur vienu mērķi. Tāpēc pietiks pateikt target_link_libraries(target PRIVATE dependency)un visas galvenes, kas ir saistītas ar mērķi dependency, būs pieejami avotiem, kas pieder mērķim target. Un jums tas nav vajadzīgs [target_]include_directories. Tas tiks parādīts zemāk analīzē CMake skripts vienību testiem.
Šī komanda saista mums nepieciešamās galvenes ar mūsu interfeisa bibliotēku, un, ja mūsu bibliotēka ir savienota ar jebkuru mērķi tajā pašā CMake hierarhijā, tad direktorija galvenes tiks saistītas ar to. ${CMAKE_CURRENT_SOURCE_DIR}/include, un ja mūsu bibliotēka ir instalēta sistēmā un savienota ar citu projektu, izmantojot komandu find_package, tad ar to tiks saistītas direktorija galvenes include attiecībā pret instalācijas direktoriju.
Noteiksim valodas standartu. Protams, pats pēdējais. Tajā pašā laikā mēs ne tikai iekļaujam standartu, bet arī attiecinām to uz tiem, kas izmantos mūsu bibliotēku. Tas tiek panākts, jo iestatītajam īpašumam ir kategorija INTERFACE (Sk. komandu target_compile_features).
Izveidosim aizstājvārdu mūsu bibliotēkai. Turklāt skaistuma labad tas atradīsies īpašā “vārdu telpā”. Tas būs noderīgi, ja mūsu bibliotēkā parādās dažādi moduļi un mēs tos savienosim neatkarīgi viens no otra. Kā, piemēram, Bustā.
Mūsu galvenes instalēšana sistēmā. Šeit viss ir vienkārši. Mēs sakām, ka mapei ar visām galvenēm jāiet direktorijā include attiecībā pret uzstādīšanas vietu.
Nākamā burvestība ir jāsaprot šādi. Kad trešās puses projektā mēs izsaucam komandu find_package(Mylib 1.2.3 REQUIRED), un instalētās bibliotēkas reālā versija nebūs saderīga ar versiju 1.2.3CMake automātiski ģenerēs kļūdu. Tas ir, jums nebūs manuāli jāizseko versijas.
Ja testi ir atspējoti, izmantojot atbilstošā opcija vai mūsu projekts ir apakšprojekts, tas ir, tas ir savienots ar citu CMake projektu, izmantojot komandu add_subdirectory, mēs nepārvietojamies tālāk pa hierarhiju, un skripts, kas apraksta komandas testu ģenerēšanai un izpildei, vienkārši nedarbojas.
if(NOT MYLIB_TESTING)
message(STATUS "Тестирование проекта Mylib выключено")
elseif(IS_SUBPROJECT)
message(STATUS "Mylib не тестируется в режиме подмодуля")
else()
add_subdirectory(test)
endif()
Mēs savienojam atkarības. Lūdzu, ņemiet vērā, ka mēs saistījām tikai vajadzīgos CMake mērķus ar mūsu bināro failu un neizsaucām komandu target_include_directories. Virsraksti no testa ietvara un no mūsu Mylib::mylib, kā arī uzbūves parametri (mūsu gadījumā tas ir C++ valodas standarts) tika sasniegti kopā ar šiem mērķiem.
Visbeidzot mēs izveidojam fiktīvu mērķi, kura “būvējums” ir līdzvērtīgs testu izpildei, un pievienojam šo mērķi noklusējuma būvējumam (par to atbild atribūts ALL). Tas nozīmē, ka noklusējuma versija aktivizē testu izpildi, kas nozīmē, ka mēs nekad neaizmirsīsim tos palaist.
add_custom_target(check ALL COMMAND mylib-unit-tests)
Pēc tam mēs iespējojam koda pārklājuma mērīšanu, ja ir norādīta atbilstošā opcija. Es neiedziļināšos detaļās, jo tie vairāk attiecas uz pārklājuma mērīšanas rīku, nevis ar CMake. Ir tikai svarīgi atzīmēt, ka, pamatojoties uz rezultātiem, tiks izveidots mērķis coverage, ar kuru ērti sākt pārklājuma mērīšanu.
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()
Tālāk mēs pārbaudām, vai lietotājs ir iestatījis valodas mainīgo. Ja jā, tad neaiztiekam, ja nē, tad ņemam krievu valodu. Pēc tam mēs konfigurējam Doxygen sistēmas failus. Visi nepieciešamie mainīgie, tostarp valoda, tiek parādīti konfigurācijas procesa laikā (sk. команду configure_file).
Tad mēs izveidojam mērķi doc, kas sāks ģenerēt dokumentāciju. Tā kā izstrādes procesā dokumentācijas ģenerēšana nav lielākā nepieciešamība, mērķis pēc noklusējuma netiks iespējots, tas būs skaidri jāpalaiž.
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 ()
Šeit mēs atrodam trešo Python un izveidojam mērķi wandbox, kas ģenerē pieprasījumu, kas atbilst pakalpojuma API Wandbox, un aizsūta viņu prom. Atbildei ir pievienota saite uz gatavo smilšu 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 "Для создания онлайн-песочницы требуется интерпретатор ЯП python 3-й версии")
endif()
Nodrošina iespēju atspējot vienības pārbaudes veidošanu un mērķauditorijas atlasi check. Rezultātā tiek izslēgta koda pārklājuma mērīšana ar testiem (sk. MYLIB_COVERAGE).
Testēšana tiek automātiski atspējota arī tad, ja projekts ir savienots ar citu projektu kā apakšprojektu, izmantojot komandu add_subdirectory.
Pakalpojums tiek izmantots šim nolūkam Wandbox. Es nezinu, cik elastīgi ir viņu serveri, bet es domāju, ka šo iespēju nevajadzētu izmantot ļaunprātīgi.
Faktiski CMake versija 3.13 ir nepieciešama tikai, lai palaistu dažas šajā palīdzībā aprakstītās konsoles komandas. No CMake skriptu sintakses viedokļa pietiek ar versiju 3.8, ja ģenerēšana tiek izsaukta citos veidos.
CMake ir ļoti jaudīga un elastīga sistēma, kas ļauj īstenot funkcionalitāti katrai gaumei un krāsai. Un, lai gan sintakse dažkārt atstāj daudz ko vēlēties, velns joprojām nav tik briesmīgs, kā viņš ir gleznots. Izmantojiet CMake build sistēmu sabiedrības un veselības labā.