Under udvikling kan jeg godt lide at ændre compilere, bygge modes, afhængighedsversioner, udføre statisk analyse, måle ydeevne, indsamle dækning, generere dokumentation og så videre. Og jeg elsker virkelig CMake, fordi det giver mig mulighed for at gøre, hvad jeg vil.
Mange skælder ud på CMake, og ofte fortjent, men hvis man ser efter, er det ikke så slemt, men på det seneste slet ikke dårligt, og udviklingsretningen er ret positiv.
I denne note vil jeg fortælle dig, hvor nemt det er at organisere et C++ header-bibliotek i et CMake-system for at få følgende funktionalitet:
montage;
Autorun tests;
Kodedækningsmåling;
installation;
Autodokumentation;
Online sandkassegenerering;
Statisk analyse.
Hvem allerede forstår fordelene og si-make kan bare download projekt skabelon og begynde at bruge det.
Vi vil hovedsageligt tale om, hvordan man organiserer CMake-scripts, så de vil blive analyseret i detaljer. Resten af filerne kan ses direkte af alle. på skabelonprojektsiden.
Først og fremmest skal du anmode om den ønskede version af CMake-systemet. CMake udvikler sig, kommandosignaturer ændres, adfærd under forskellige forhold. For at CMake umiddelbart kan forstå, hvad vi vil have ud af det, skal vi straks rette vores krav til det.
cmake_minimum_required(VERSION 3.13)
Derefter angiver vi vores projekt, dets navn, version, anvendte sprog osv. команду project).
I dette tilfælde skal du angive sproget CXX (hvilket betyder C++), så CMake ikke gider lede efter en C-compiler (som standard er to sprog inkluderet i CMake: C og C++).
project(Mylib VERSION 1.0 LANGUAGES CXX)
Her kan du med det samme tjekke, om vores projekt indgår i et andet projekt som delprojekt. Dette vil hjælpe meget i fremtiden.
Den første mulighed er MYLIB_TESTING - at deaktivere enhedstest. Det kan være nødvendigt, hvis vi er sikre på, at alt er i orden med testene, og vi for eksempel kun vil installere eller pakke vores projekt. Eller vores projekt indgår som et delprojekt - i dette tilfælde er brugeren af vores projekt ikke interesseret i at køre vores tests. Du tester ikke de afhængigheder, du bruger, vel?
Derudover vil vi lave en separat mulighed MYLIB_COVERAGE til måling af kodedækning med test, men det vil kræve yderligere værktøjer, så du bliver nødt til at aktivere det eksplicit.
Selvfølgelig er vi seje programmører-positive, så vi vil have det maksimale niveau af kompileringstidsdiagnostik fra compileren. Ikke en eneste mus kommer igennem.
Vores bibliotek består kun af header-filer, hvilket betyder, at vi ikke har noget output i form af statiske eller dynamiske biblioteker. På den anden side, for at bruge vores bibliotek udefra, skal du installere det, du skal være i stand til at finde det i systemet og forbinde det til dit projekt, og samtidig også disse headere som muligvis nogle yderligere egenskaber.
Til dette formål opretter vi et grænsefladebibliotek.
add_library(mylib INTERFACE)
Vi binder overskrifterne til vores grænsefladebibliotek.
Moderne, trendy, ungdommelig brug af CMake betyder, at headere, egenskaber mv. transmitteret gennem et enkelt mål. Så det er nok at sige target_link_libraries(target PRIVATE dependency), og alle overskrifter, der er knyttet til målet dependency, vil være tilgængelig for kilder, der tilhører målet target. Og du behøver ikke nogen [target_]include_directories. Dette vil blive demonstreret nedenfor under parsing CLave script til enhedstests.
Denne kommando forbinder de overskrifter, vi har brug for, med vores grænsefladebibliotek, og hvis vores bibliotek er forbundet til ethvert mål inden for det samme CMake-hierarki, vil overskrifterne fra biblioteket blive knyttet til det ${CMAKE_CURRENT_SOURCE_DIR}/include, og hvis vores bibliotek er installeret på systemet og forbundet til et andet projekt ved hjælp af kommandoen find_package, så vil overskrifterne fra mappen blive knyttet til den include i forhold til installationsmappen.
Indstil sprogstandarden. Selvfølgelig den seneste. Samtidig medtager vi ikke kun standarden, men distribuerer den også til dem, der skal bruge vores bibliotek. Dette opnås ved, at den indstillede egenskab har en kategori INTERFACE (See. target_compile_features kommando).
Vi får et alias til vores bibliotek. Og for skønheden vil det være i et særligt "navneområde". Dette vil være nyttigt, når forskellige moduler vises i vores bibliotek, og vi går for at forbinde dem uafhængigt af hinanden. Som i Busta f.eks.
Installation af vores headere i systemet. Alt er enkelt her. Vi siger, at mappen med alle overskrifterne skal falde ind i mappen include vedrørende installationsstedet.
Følgende besværgelse skal forstås således. Når vi er i et sideprojekt, kalder vi kommandoen find_package(Mylib 1.2.3 REQUIRED), og samtidig vil den rigtige version af det installerede bibliotek være inkompatibel med versionen 1.2.3, vil CMake automatisk generere en fejl. Det vil sige, at du ikke behøver at holde styr på versioner manuelt.
Hvis tests eksplicit er deaktiveret med tilsvarende mulighed eller vores projekt er et underprojekt, det vil sige, det er forbundet med et andet CMake-projekt ved hjælp af kommandoen add_subdirectory, vi går ikke længere ned i hierarkiet, og scriptet, som beskriver kommandoerne til at generere og køre test, starter simpelthen ikke.
if(NOT MYLIB_TESTING)
message(STATUS "Тестирование проекта Mylib выключено")
elseif(IS_SUBPROJECT)
message(STATUS "Mylib не тестируется в режиме подмодуля")
else()
add_subdirectory(test)
endif()
Først og fremmest finder vi en pakke med den ønskede testramme (erstat den med din favorit).
find_package(doctest 2.3.3 REQUIRED)
Vi opretter vores eksekverbare fil med tests. Normalt tilføjer jeg direkte til den eksekverbare binære fil kun den fil, hvori funktionen vil være main.
add_executable(mylib-unit-tests test_main.cpp)
Og de filer, hvori selve testene er beskrevet, tilføjer jeg senere. Men det er ikke nødvendigt at gøre det.
Vi forbinder afhængigheder. Bemærk venligst, at vi kun vedhæftede de CMake-mål, vi havde brug for, til vores binære, og kaldte ikke kommandoen target_include_directories. Overskrifter fra testrammen og fra vores Mylib::mylib, samt byggemuligheder (i vores tilfælde C++ sprogstandarden) kravlede sammen med disse mål.
Til sidst opretter vi et dummy-mål, hvis "build" svarer til at køre test, og tilføjer dette mål til standardbuilden (dette er attributtens ansvar ALL). Det betyder, at standardbuilden vil udløse testene til at køre, hvilket betyder, at vi aldrig glemmer at køre dem.
add_custom_target(check ALL COMMAND mylib-unit-tests)
Slå derefter kodedækningsmåling til, hvis den tilsvarende indstilling er indstillet. Jeg vil ikke gå ind i detaljerne, for de er mere relateret til dækningsmålingsværktøjet end til CMake. Det er kun vigtigt at bemærke, at resultaterne vil skabe et mål coverage, hvormed det er praktisk at begynde at måle dækning.
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()
Dernæst tjekker vi om variablen med sproget er indstillet af brugeren. Hvis ja, så rører vi det ikke, hvis ikke, så tager vi russisk. Derefter konfigurerer vi Doxygen-systemfilerne. Alle de nødvendige variabler, inklusive sproget, kommer dertil under konfigurationsprocessen (se. команду configure_file).
Så laver vi et mål doc, som vil begynde at generere dokumentation. Da generering af dokumentation ikke er det største behov i udviklingsprocessen, vil målet ikke være inkluderet som standard, det skal køres eksplicit.
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 ()
Her finder vi den tredje Python og laver et mål wandbox, som genererer en anmodning svarende til tjenestens API tryllestavskasse, og sender den. Som svar kommer et link til den færdige sandkasse.
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()
Faktisk kræves CMake version 3.13 kun for at køre nogle af de konsolkommandoer, der er beskrevet i denne hjælp. Ud fra syntaksen af CMake-scripts er version 3.8 tilstrækkelig, hvis genereringen kaldes på andre måder.
CMake er et meget kraftfuldt og fleksibelt system, der giver dig mulighed for at implementere funktionalitet for enhver smag og farve. Og selvom syntaksen nogle gange lader meget tilbage at ønske, er djævelen stadig ikke så forfærdelig, som han er malet. Brug CMake build-systemet til gavn for samfundet og sundheden.