
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:
- Komplēšana;
- Testu automātiskā palaišana;
- Koda pārklājuma mērīšana;
- Instalācija;
- Automatizēta dokumentācija;
- Online smilšu kaste ģenerācija;
- Statiskā analīze.
Tie, kas saprot C++ un CMake, var vienkārši un sākt to izmantot.
Satelīte
.
├── 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.cppGalvenokā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 .
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. ).
Š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)
Apsvērsim divas iespējas.
Pirmā iespēja — — 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 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)
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()
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 , 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 .
Tāpat jāņem vērā, tā dēvētie .
Šī 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 , 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. ).
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. .
add_library(Mylib::mylib ALIAS mylib)
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)
Ja testi ir izslēgti skaidri, izmantojot vai mūsu projekts ir apakšprojekts, tas ir, pievienots citam CMake projektam, izmantojot komandu , 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 arī netiks ģenerēta, ja tas ir apakšprojekts.
if(NOT IS_SUBPROJECT)
add_subdirectory(doc)
endif()
Līdzīgi, tiešsaistes smilšu kastes apakšprojektā arī nebūs.
if(NOT IS_SUBPROJECT)
add_subdirectory(online)
endif()
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)
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 , 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()
.
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. ).
Pēc tam izveidojam mērķi , 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 ()
Šeit meklējam trešo Python un izveidojam mērķi , kas ģenerēs pieprasījumu, kas atbilst pakalpojuma API , 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()
Tagad aplūkosim, kā to visu izmantot.
Šī projekta kompilācija, tāpat kā jebkura cita projekta kompilācija CMake sistēmā, sastāv no diviem posmiem:
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 ...]
.
cmake --build ceļš/uz/kompilācijas/ direktoriju [--target target].
cmake -S ... -B ... -DMYLIB_COVERAGE=ON [citas opcijas ...]Iekļauj mērķi , ar kuru var uzsākt koda seguma testēšanu.
cmake -S ... -B ... -DMYLIB_TESTING=OFF [citi opcijas ...]Piedāvā iespēju izslēgt moduļu testu kompilāciju un mērķi . Līdz ar to tiek izslēgta koda seguma testēšana (skat. ).
Testēšana automātiski tiek izslēgta, ja projekts tiek pievienots citam projektam kā apakšprojekts ar komandu .
cmake -S ... -B ... -DMYLIB_DOXYGEN_LANGUAGE=English [citi opcijas ...]Pārslēdz dokumentācijas valodu, ko ģenerē mērķis uz norādīto. Pieejamo valodu sarakstu skatiet .
Noklusējuma valoda ir krievu.
cmake --build ceļš/to/savākšanas/direktoriju
cmake --build ceļš/to/savākšanas/direktoriju --target allJa mērķis nav norādīts (kas ir ekvivalents mērķim all), izveido visu, ko var, un arī izpilda mērķi .
cmake --build ceļš/to/savākšanas/direktoriju --target mylib-unit-testsKompilē moduļu testus. Noklusējumā iekļauts.
cmake --build ceļš/k/savākšanas/direktoriju --target checkIzpilda esošos (izveido, ja vēl nav) moduļu testus. Noklusējumā iekļauts.
Skatīt arī .
cmake --build ceļš/k/savākšanas/direktoriju --target coverageAnalizē izpildītos (izveido, ja vēl nav) moduļu testus koda seguma testēšanai, izmantojot programmu .
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 .
Skatīt arī .
cmake --build ceļš/k/savākšanas/direktoriju --target docUzsāk dokumentācijas ģenerēšanu koda piekārtošanai, izmantojot sistēmu .
cmake --build ceļš/k/savākšanas/direktoriju --target wandboxAtbilde no pakalpojuma izskatīsies aptuveni tā:
{
"permlink" : "QElvxuMzHgL9fqci",
"status" : "0",
"url" : "https://wandbox.org/permlink/QElvxuMzHgL9fqci"
}Tam tiek izmantots pakalpojums . Nezinu, cik elastīgi ir viņu serveri, bet domāju, ka šo iespēju nevajadzētu ļaunprātīgi izmantot.
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 16Projekta 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 installKompilā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 4Dokumentā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
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.
Testēšanas bibliotēka
Testēšanu var atslēgt (skat. ).
Lai mainītu valodu, kurā tiks ģenerēta dokumentācija, ir paredzēta iespēja .
Programmas interpretators
Automātiskai ģenerēšanai .
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 .
Tam nepieciešams izmantot iespēju :
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 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.
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.
→
Avots: habr.com
