
Në procesin e zhvillimit, unë pëlqej të ndryshoj kompilatorët, modet e ndërtimit, versionet e varësive, të kryej analiza statike, të mas performancën, të grumbulloj mbulimin, të gjeneroj dokumentacion etj. Dhe më pëlqen shumë CMake, sepse më lejon të bëj gjithçka që kam dëshirë.
Shumë e kritikojnë CMake, dhe shpesh me të drejtë, por nëse e analizojmë, nuk është aq keq, dhe në kohët e fundit madje shumë mirë, dhe drejtimi i zhvillimit është krejt pozitiv.
Në këtë shënim, dëshiroj të tregoj se si është mjaft e thjeshtë të organizosh një bibliotekë titujsh në gjuhën C++ në sistemin CMake, për të marrë funksionalitetin e mëposhtëm:
- Ndërtimin;
- Autopërplasjen e testeve;
- Matjen e mbulimit të kodit;
- Instalimin;
- Autodokumentimin;
- Gjenerimin e një sandBox online;
- Analizën statike.
Kush e di tashmë për avantajet e CMake mund thjesht dhe të fillojë ta përdorë atë.
Përmbajtja
.
âââ 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.cppKryesisht do tĂ« flasim pĂ«r mĂ«nyrĂ«n se si tĂ« organizojmĂ« skenaret CMake, prandaj do tĂ« shqyrtohen nĂ« detaje. Dosjet e tjera tĂ« gjithĂ« mund t'i shohin drejtpĂ«rdrejt .
Në radhë të parë, duhet të kërkoni versionin e duhur të sistemit CMake. CMake po zhvillohet, nënshkrimet e komandave po ndryshojnë, si dhe sjellja në kushte të ndryshme. Për që CMake të kuptojë menjëherë se çfarë duam prej tij, duhet të specifikojmë kërkesat tona prej tij që në fillim.
cmake_minimum_required(VERSION 3.13)Pastaj do të shënojmë projektin tonë, emrin e tij, versionin, gjuhët e përdorura dhe të tjera (shih ).
Në këtë rast tregojmë gjuhën CXX (do të thotë C++), për të mos e shqetësuar CMake dhe për të mos kërkuar kompilatorin për gjuhën C (në CMake, nga e drejta, janë të përfshira dy gjuhë: C dhe C++).
project(Mylib VERSION 1.0 LANGUAGES CXX)Këtu mund të kontrolloni nëse projekti ynë është përfshirë si një nënprojekt në një projekt tjetër. Kjo do të ndihmojë shumë në vazhdim.
get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)
Do të parashikojmë dy opsione.
Opsioni i parĂ« â â pĂ«r tĂ« çactivizuar testet modulare. Kjo mund tĂ« jetĂ« e nevojshme nĂ«se jemi tĂ« sigurt se testet janĂ« nĂ« rregull, dhe duam, pĂ«r shembull, thjesht tĂ« instalojmĂ« ose tĂ« paketojmĂ« projektin tonĂ«. Ose projekti ynĂ« Ă«shtĂ« pĂ«rfshirĂ« si njĂ« nĂ«nprojekt â nĂ« kĂ«tĂ« rast, pĂ«rdoruesi i projektit tonĂ« nuk Ă«shtĂ« i interesuar tĂ« ekzekutojĂ« testet tona. A nuk i testoni varĂ«sitĂ« qĂ« pĂ«rdorni?
option(MYLIB_TESTING "Aktivizo testimin modular" ON)Për më tepër, do të krijojmë një opsion të veçantë për matjen e mbulimit të kodit nga testet, por kjo do të kërkojë mjete të tjera, për këtë arsye duhen aktivizuar shprehimisht.
option(MYLIB_COVERAGE "Aktivizo matjen e mbulimit të kodit nga testet" OFF)
Natyrisht, ne jemi programues të shkëlqyer në C++, prandaj duam nga kompajleri nivelin maksimal të diagnostikave gjatë kompilimit. Asnjë mushkonjë nuk do të kalojë.
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
)Do të ç`aktivizojmë zgjerimet gjithashtu, për të përmbushur plotësisht standardin e gjuhës C++. Në mënyrë të paracaktuar, ato janë të aktivizuara në CMake.
if(NOT CMAKE_CXX_EXTENSIONS)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
Biblioteka jonë përbëhet vetëm nga skedarë titulli, që do thotë se ne nuk kemi ndonjë shpërndarje në formë bibliotekash statike ose dinamike. Nga ana tjetër, për ta përdorur bibliotekën tonë nga jashtë, duhet ta instalojmë, duhet që të jetë e zbuluar në sistem dhe të mund të lidhet me projektin tuaj, dhe për këtë duhet të jenë të lidhura gjithashtu këto skedarë titulli, si dhe ndoshta disa pronësi shtesë.
Për këtë qëllim krijojmë një bibliotekë ndërfaqe.
add_library(mylib INTERFACE)Lidhim skedarët titulli me bibliotekën tonë ndërfaqe.
Përdorimi modern, trendy dhe rinor i CMake nënkupton që skedarët titulli, pronësitë etj. kalohen përmes një qëllimi të vetëm. Prandaj, mjafton të thuhet , dhe të gjitha titujt që lidhen me qëllimin dependency, do të jenë të disponueshëm për burimet që i përkasin qëllimit target. Dhe nuk kërkohet asgjë [target_]include_directories. Kjo do të demonstrohet më poshtë gjatë analizës .
Vlen të theksohet gjithashtu koncepti i .
Kjo komandë lidh titujt që na nevojiten me bibliotekën tonë të ndërfaqes, duke pasur parasysh se, nëse biblioteka jonë lidhet me ndonjë qëllim brenda një hierarkie CMake, atëherë titujt nga direktoria ${CMAKE_CURRENT_SOURCE_DIR}/include, dhe nëse biblioteka jonë është instaluar në sistem dhe lidhet në një projekt tjetër përmes komandës , atëherë titujt nga direktoria do të lidhen include në lidhje me direktorinë e instalimit.
target_include_directories(mylib INTERFACE
$
$
)Do të caktomë standardin e gjuhës. Natyrisht, më të fundit. Në këtë rast, jo vetëm që aktivizojmë standardin, por e shpërndajmë edhe tek ata që do të përdorin bibliotekën tonë. Kjo arrihet përmes të dhënave të vendosura që kanë kategorinë INTERFACE (shiko. ).
target_compile_features(mylib INTERFACE cxx_std_17)Krijojmë një pseudonim për bibliotekën tonë. Për më tepër, për qëllime estetike do të vendoset në një «hapësirë emri» speciale. Kjo do të jetë e dobishme kur biblioteka jonë të ketë modules të ndryshme, dhe ne duam t'i lidhim ato në mënyrë të pavarur nga njëra-tjetra. .
add_library(Mylib::mylib ALIAS mylib)
Vendosja e titujve tanë në sistem. Këtu është mjaft e thjeshtë. Njoftojmë se folders me të gjithë titujt duhet të shkojë në drejtorinë include në lidhje me vendndodhjen e instalimit.
install(DIRECTORY include/mylib DESTINATION include)Më pas, njoftojmë sistemin e ndërtimit se duam të kemi mundësinë të thërrasim komandën find_package(Mylib) dhe të marrim objektin Mylib::mylib.
install(TARGETS mylib EXPORT MylibConfig)
install(EXPORT MylibConfig NAMESPACE Mylib:: DESTINATION share/Mylib/cmake)Formula e ardhshme duhet të kuptohet si më poshtë. Kur në një projekt të tretë thërrasim komandën find_package(Mylib 1.2.3 REQUIRED), dhe në të njëjtën kohë versioni real i bibliotekës së instaluar është i paefektshëm me versionin 1.2.3, CMake automatikisht do të gjenerojë një gabim. Nuk do të jetë e nevojshme të kontrolloni manualisht versionet.
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)
Nëse testet janë çaktivizuar në mënyrë të qartë me ose projekti ynë është një nënprojekt, pra është lidhur në një projekt tjetër CMake me komandën , ne nuk kalojmë më tej në hierarki, dhe skenari, në të cilin janë përshkruar komandat për gjenerimin dhe ekzekutimin e testeve, thjesht nuk ekzekutohet.
if(NOT MYLIB_TESTING)
message(STATUS "Testimi i projektit Mylib është çaktivizuar")
elseif(IS_SUBPROJECT)
message(STATUS "Mylib nuk testohen në modalitetin nënmoduli")
else()
add_subdirectory(test)
endif()
Dokumentacioni gjithashtu nuk do të gjenerohet në rastin e një nënprojekti.
if(NOT IS_SUBPROJECT)
add_subdirectory(doc)
endif()
Në mënyrë të ngjashme, sandboxes online për nënprojektin gjithashtu nuk do të ekzistojnë.
if(NOT IS_SUBPROJECT)
add_subdirectory(online)
endif()
Së pari, gjejmë paketën me kornizën e testit të nevojshme (zëvendësoni me të preferuarën tuaj).
find_package(doctest 2.3.3 REQUIRED)Krijojmë skedarin tonë ekzekutiv me testet. Zakonisht në skedarin ekzekutiv unë shtoj vetëm skedarin ku do të jetë funksioni main.
add_executable(mylib-unit-tests test_main.cpp)Dhe skedarët ku përshkruhen testet do t'i shtoj më vonë. Por nuk është e nevojshme ta bëni këtë.
target_sources(mylib-unit-tests PRIVATE mylib/myfeature.cpp)Po lidhen varësitë. Vini re se lidhëm vetëm objektet CMake që na duhen në binarin tonë dhe nuk thirrëm komandën target_include_directories. Titujt nga framework-u i testit dhe nga Mylib::mylib, si dhe parametrat e ndërtimit (në rastin tonë standardi i gjuhës C++) kaluan së bashku me këto qëllime.
target_link_libraries(mylib-unit-tests
PRIVATE
Mylib::mylib
doctest::doctest
)Finalmente, krijojmë një objekt të rremë, 'ndërtimi' i së cilës është ekuivalent me ekzekutimin e testeve, dhe e shtojmë këtë objekt në ndërtimin default (kjo është përgjegjësia e atributit Tà GJITHA). Kjo do të thotë se ndërtimi default iniciaton ekzekutimin e testeve, që do të thotë se kurrë nuk do t'i harrojmë ato.
add_custom_target(check ALL COMMAND mylib-unit-tests)
Më pas aktivizojmë matjen e mbulimit të kodit, nëse është përcaktuar një opsion i tillë. Nuk do të hyj në detaje, sepse ato përfshihen më shumë në një mjet për matjen e mbulimit sesa në CMake. E rëndësishme është të theksohet se nga rezultatet do të krijohet një objekt , me të cilin është e lehtë të ekzektohet matja e mbulimit.
find_program(GCOVR_EXECUTABLE gcovr)
if(MYLIB_COVERAGE AND GCOVR_EXECUTABLE)
message(STATUS "Matja e mbulimit të kodit me teste është e aktivizuar")
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 "Programi gcovr është i nevojshëm për matjen e mbulimit të kodit me teste")
endif()
.
find_package(Doxygen)Pastaj kontrollojmë nëse përdoruesi ka vendosur variablën për gjuhën. Nëse po, nuk e prekim, nëse jo, e marrim në rusisht. Më pas konfiguroni skedarët e sistemit Doxygen. Të gjitha variablat e nevojshme, përfshirë gjuhën, do të shkojnë aty gjatë procesit të konfigurimit (shih ).
Pas kësaj krijojmë një qëllim , i cili do të nxisë gjenerimin e dokumentacionit. Duke qenë se gjenerimi i dokumentacionit nuk është një kërkesë e madhe gjatë procesit të zhvillimit, qëllimi nuk do të jetë aktivizuar nga defekti, do të duhet ta nxisni atë drejtpërdrejt.
if (Doxygen_FOUND)
if (NOT MYLIB_DOXYGEN_LANGUAGE)
set(MYLIB_DOXYGEN_LANGUAGE Albanian)
endif()
message(STATUS "Dokumentacioni Doxygen do të gjenerohet në ${MYLIB_DOXYGEN_LANGUAGE}")
configure_file(Doxyfile.in Doxyfile)
add_custom_target(doc COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
endif ()
Këtu gjejmë Pythoni e tretë dhe krijojmë qëllimin , i cili gjeneron një kërkesë që i përgjigjet API-së së shërbimit , dhe e dërgon atë. Në përgjigje, merret një lidhje për sandboxin e përfunduar.
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 "Për krijimin e një sandboxi online nevojitet interpretor i gjuhës së programimit python versioni 3")
endif()
Tani le të shohim se si mund ta përdorim këtë të gjithë.
Ndërtimi i këtij projekti, si çdo projekt tjetër në sistemin e ndërtimit CMake, përbëhet nga dy faza:
cmake -S rruga/tek/bazat -B rruga/tek/direktorin/e/ndërtimit [opsionet ...]Nëse komanda e mësipërme nuk funksionoi për shkak të një versioni të vjetër të CMake, provo ta shkurtoheshe
-S:cmake rruga/tek/bazat -B rruga/tek/direktorin/e/ndërtimit [opsionet ...]
.
cmake --build rrugën/në/ndërtimin/[--target target].
cmake -S ... -B ... -DMYLIB_COVERAGE=ON [opsione të tjera ...]Aktivizon qëllimin , me të cilin mund të filloni matjen e përmbushjes së kodit me teste.
cmake -S ... -B ... -DMYLIB_TESTING=OFF [opsione të tjera ...]Ofron mundësinë për të çaktivizuar ndërtimin e testeve moduluese dhe qëllimin . Si rezultat, matja e përmbushjes së kodit me teste çaktivizohet (shih. ).
Gjithashtu, testimi automatikisht çaktivizohet nëse projekti lidhet në një projekt tjetër si nënpërgjegjës përmes komandës .
cmake -S ... -B ... -DMYLIB_DOXYGEN_LANGUAGE=English [opsione të tjera ...]Ndërron gjuhën e dokumentacionit që gjeneron qëllimi në të caktuarin. Lista e gjuhëve të disponueshme mund të shihet në .
Për default është e aktivizuar rusa.
cmake --build rrugën/në/ndërtimin/
cmake --build rrugën/në/ndërtimin/ --target allNëse qëllimi nuk është caktuar (e cila është ekuivalente me qëllimin all), ndërtuon gjithçka që është e mundur, dhe gjithashtu thërret qëllimin .
cmake --build rrugën/në/ndërtimin/ --target mylib-unit-testsKom hilon testet moduluese. I aktivizuar për default.
cmake --build rrugën/në/ndërtimin/ --target checkNis testet e ndërtuara (ndërton, nëse ende nuk është) moduluese. I aktivizuar për default.
Shih gjithashtu .
cmake --build rrugë/në/direktorinë/e/ndërtimit --target coverageAnalizon testet modulare të ekzekutuara (i nis nëse ende nuk janë) për mbulimin e kodit me ndihmën e programit .
Të dhënat e mbulimit do të duken pak a shumë kështu:
------------------------------------------------------------------------------
Raporti i Mbulimit të Kodit GCC
Directory: /path/to/cmakecpptemplate/include/
------------------------------------------------------------------------------
File Lines Exec Cover Missing
------------------------------------------------------------------------------
mylib/myfeature.hpp 2 2 100%
------------------------------------------------------------------------------
TOTAL 2 2 100%
------------------------------------------------------------------------------Qëllimi është i disponueshëm vetëm kur opsioni është aktivizuar .
Shih gjithashtu .
cmake --build rrugë/në/direktorinë/e/ndërtimit --target docNis gjenerimin e dokumentacionit për kodin përmes sistemit .
cmake --build rrugë/në/direktorinë/e/ndërtimit --target wandboxPërgjigjja nga shërbimi duket pak a shumë kështu:
{
"permlink" : "QElvxuMzHgL9fqci",
"status" : "0",
"url" : "https://wandbox.org/permlink/QElvxuMzHgL9fqci"
}Për këtë, përdoret shërbimi . Nuk e di sa janë serverët e tyre elastikë, por mendoj se nuk duhet abuzuar me këtë mundësi.
Ndërtimi i projektit në mënyrë debug me matjen e mbulimit
cmake -S rrugë/te/burimeve -B rrugë/te/direktorisë/ndërtimit -DCMAKE_BUILD_TYPE=Debug -DMYLIB_COVERAGE=ON
cmake --build rrugë/te/direktorisë/ndërtimit --target coverage --parallel 16Instalimi i projektit pa ndërtim dhe testim paraprak
cmake -S rrugë/te/burimeve -B rrugë/te/direktorisë/ndërtimit -DMYLIB_TESTING=OFF -DCMAKE_INSTALL_PREFIX=rrugë/te/direktorisë/instalimit
cmake --build rrugë/te/direktorisë/ndërtimit --target installNdërtesa në mënyrë rrelease me kompilatorin e caktuar
cmake -S rrugë/te/burimeve -B rrugë/te/direktorisë/ndërtimit -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_PREFIX_PATH=rrugë/te/direktorisë/ku/janë/instalimet
cmake --build rrugë/te/direktorisë/ndërtimit --parallel 4Generimi i dokumentacionit në anglisht
cmake -S rrugë/te/burimeve -B rrugë/te/direktorisë/ndërtimit -DCMAKE_BUILD_TYPE=Release -DMYLIB_DOXYGEN_LANGUAGE=English
cmake --build rrugë/te/direktorisë/ndërtimit --target doc
3.13
Në të vërtetë, versione CMake 3.13 kërkohet vetëm për të ekzekutuar disa komanda konsolë që përshkruhen në këtë dokument. Nga pikëpamja e sintaksës së skripteve CMake, versioni 3.8 është i mjaftueshëm nëse gjenerimi thirret përmes mënyrave të tjera.
Biblioteka e testimit
Testimi mund të çaktivizohet (shih ).
Për të ndryshuar gjuhën në të cilën do të gjenerohet dokumentacioni, është e parashikuar një opsion .
Interpretuesi i GJ
Për gjenerim automatike .
Me CMake dhe disa mjete të mira, mund të sigurosh analizën statike me minimumin e mundimeve.
Cppcheck
CMake ka mbështetje të integruar për mjete për analizë statike .
Për këtë, duhet të përdorësh opsionin :
cmake -S rruga/tek/bazat -B rruga/tek/directory/ndertimit -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_CPPCHECK="cppcheck;--enable=all;-Irruga/tek/bazat/include"Pas kësaj, analiza statike do të nisë automatikisht çdo herë gjatë kompilimit dhe ripompilimit të burimeve. Nuk ka nevojë për asgjë tjetër.
Clang
Me ndihmën e një mjeti të shkëlqyer mund të nisesh analizën statike shpejt:
scan-build cmake -S rruga/tek/bazat -B rruga/tek/directory/ndertimit -DCMAKE_BUILD_TYPE=Debug
scan-build cmake --build rruga/tek/directory/ndertimitKëtu, ndryshe nga rasti me Cppcheck, kërkohet që çdo herë të nisesh ndërtimin përmes scan-build.
CMake â njĂ« sistem shumĂ« i fuqishĂ«m dhe fleksibĂ«l, qĂ« lejon implementimin e funksionaliteteve sipas dĂ«shirĂ«s. Edhe pse sintaksa ndonjĂ«herĂ« lĂ« pĂ«r tĂ« dĂ«shiruar, nuk Ă«shtĂ« aq e frikshme sa duket. PĂ«rdorni sistemin e ndĂ«rtimit CMake pĂ«r dobi tĂ« shoqĂ«risĂ« dhe pĂ«r tĂ« mirĂ«n tuaj.
â
Burimi: habr.com
