Մշակման ընթացքում ես սիրում եմ փոխել կոմպիլյատորները, կառուցել ռեժիմներ, կախվածության տարբերակներ, կատարել ստատիկ վերլուծություն, չափել կատարումը, հավաքել ծածկույթը, ստեղծել փաստաթղթեր և այլն: Եվ ես իսկապես սիրում եմ CMake-ը, քանի որ այն թույլ է տալիս ինձ անել այն ամենը, ինչ ուզում եմ:
Շատերը քննադատում են CMake-ին, և հաճախ դա արժանիորեն, բայց եթե նայեք, ամեն ինչ այնքան էլ վատ չէ, և վերջերս. ամենևին էլ վատ չէ, իսկ զարգացման ուղղությունը բավականին դրական է։
Այս գրառման մեջ ես ուզում եմ ձեզ պատմել, թե ինչպես պարզապես կազմակերպել վերնագրի գրադարան C++-ով CMake համակարգում՝ ստանալու հետևյալ ֆունկցիոնալությունը.
ժողով;
Autorun թեստեր;
Կոդի ծածկույթի չափում;
Տեղադրում;
Ավտոմատ փաստաթղթեր;
Առցանց ավազատուփի սերունդ;
Ստատիկ վերլուծություն.
Յուրաքանչյուր ոք, ով արդեն հասկանում է առավելություններն ու C-make-ը, կարող է պարզապես Ներբեռնեք նախագծի ձևանմուշը և սկսեք օգտագործել այն:
Մենք հիմնականում կխոսենք այն մասին, թե ինչպես կազմակերպել CMake սկրիպտները, ուստի դրանք մանրամասն կքննարկվեն։ Բոլորը կարող են ուղղակիորեն դիտել մնացած ֆայլերը կաղապարի նախագծի էջում.
Նախևառաջ անհրաժեշտ է պահանջել CMake համակարգի անհրաժեշտ տարբերակը: CMake-ը զարգանում է, փոխվում են հրամանների ստորագրությունները և վարքագիծը տարբեր պայմաններում: Որպեսզի CMake-ն անմիջապես հասկանա, թե ինչ ենք մենք ուզում նրանից, մենք պետք է անմիջապես գրանցենք դրա նկատմամբ մեր պահանջները:
cmake_minimum_required(VERSION 3.13)
Այնուհետև մենք կնշանակենք մեր նախագիծը, դրա անվանումը, տարբերակը, օգտագործվող լեզուները և այլն (տես. команду project).
Այս դեպքում մենք նշում ենք լեզուն CXX (իսկ սա նշանակում է C++), որպեսզի CMake-ը չլարվի և չփնտրի C լեզվի կոմպիլյատոր (լռելյայն, CMake-ը ներառում է երկու լեզու՝ C և C++):
project(Mylib VERSION 1.0 LANGUAGES CXX)
Այստեղ դուք կարող եք անմիջապես ստուգել՝ արդյոք մեր նախագիծը ներառված է մեկ այլ նախագծում՝ որպես ենթանախագծ: Սա ապագայում շատ կօգնի։
Առաջին տարբերակն է MYLIB_TESTING — անջատել միավորի թեստերը: Սա կարող է անհրաժեշտ լինել, եթե վստահ լինենք, որ թեստերի հետ կապված ամեն ինչ կարգին է, բայց մենք միայն ցանկանում ենք, օրինակ, տեղադրել կամ փաթեթավորել մեր նախագիծը: Կամ մեր նախագիծը ներառված է որպես ենթածրագիր. այս դեպքում մեր նախագծի օգտատերը շահագրգռված չէ մեր թեստերն իրականացնելով: Դուք չեք փորձարկում ձեր օգտագործած կախվածությունները, այնպես չէ՞:
Բացի այդ, մենք առանձին տարբերակ կանենք MYLIB_COVERAGE կոդի ծածկույթը թեստերով չափելու համար, սակայն դա կպահանջի լրացուցիչ գործիքներ, ուստի այն պետք է հստակորեն միացվի:
Իհարկե, մենք հիանալի պլյուս ծրագրավորողներ ենք, ուստի մենք ցանկանում ենք կոմպիլյացիայի ժամանակի ախտորոշման առավելագույն մակարդակը կոմպիլյատորից: Ոչ մի մուկ չի սայթաքի միջով:
Մեր գրադարանը բաղկացած է միայն վերնագրի ֆայլերից, ինչը նշանակում է, որ մենք չունենք արտանետումներ ստատիկ կամ դինամիկ գրադարանների տեսքով: Մյուս կողմից, մեր գրադարանը արտաքինից օգտագործելու համար այն պետք է տեղադրվի, այն պետք է լինի համակարգում հայտնաբերելի և միացված լինի ձեր նախագծին, և միևնույն ժամանակ այս նույն վերնագրերը, ինչպես նաև, հնարավոր է, որոշ լրացուցիչներ, դրան կցվում են հատկություններ.
Այդ նպատակով մենք ստեղծում ենք ինտերֆեյսի գրադարան:
add_library(mylib INTERFACE)
Մենք կապում ենք վերնագրերը մեր ինտերֆեյսի գրադարանին:
CMake-ի ժամանակակից, նորաձև, երիտասարդական օգտագործումը ենթադրում է, որ վերնագրերը, հատկությունները և այլն: փոխանցվում է մեկ թիրախի միջոցով: Այնպես որ, բավական է ասել target_link_libraries(target PRIVATE dependency)և բոլոր վերնագրերը, որոնք կապված են թիրախի հետ dependency, հասանելի կլինի թիրախին պատկանող աղբյուրների համար target. Եվ ձեզ հարկավոր չէ [target_]include_directories. Սա կցուցադրվի ստորև վերլուծության մեջ CMake սցենար միավորի թեստերի համար.
Այս հրամանը կապում է մեզ անհրաժեշտ վերնագրերը մեր ինտերֆեյսի գրադարանի հետ, և եթե մեր գրադարանը կապված է որևէ թիրախի հետ նույն CMake հիերարխիայում, ապա գրացուցակի վերնագրերը կկապվեն դրա հետ: ${CMAKE_CURRENT_SOURCE_DIR}/include, և եթե մեր գրադարանը տեղադրված է համակարգում և միացված է մեկ այլ նախագծի՝ օգտագործելով հրամանը find_package, ապա գրացուցակի վերնագրերը կկապվեն դրա հետ include տեղադրման գրացուցակի համեմատ:
Եկեք սահմանենք լեզվի չափանիշ: Իհարկե, ամենավերջինը։ Միևնույն ժամանակ, մենք ոչ միայն ներառում ենք ստանդարտը, այլև այն տարածում ենք նրանց վրա, ովքեր կօգտվեն մեր գրադարանից: Սա ձեռք է բերվում այն պատճառով, որ սահմանված գույքն ունի կատեգորիա INTERFACE (տես target_compile_features հրամանը).
Եկեք մեր գրադարանի համար ստեղծենք այլանուն: Ավելին, գեղեցկության համար այն կլինի հատուկ «անվան տարածությունում»: Սա օգտակար կլինի, երբ մեր գրադարանում հայտնվեն տարբեր մոդուլներ, և մենք գնում ենք դրանք միմյանցից անկախ միացնելու: Ինչպես օրինակ Բուստայում.
Տեղադրելով մեր վերնագրերը համակարգում: Այստեղ ամեն ինչ պարզ է. Մենք ասում ենք, որ բոլոր վերնագրերով թղթապանակը պետք է մտնի գրացուցակ include տեղադրման վայրի համեմատ:
Հաջորդը, մենք տեղեկացնում ենք build համակարգին, որ մենք ցանկանում ենք, որպեսզի կարողանանք կանչել հրամանը երրորդ կողմի նախագծերում find_package(Mylib) և ստացիր գոլ Mylib::mylib.
Հաջորդ ուղղագրությունը պետք է հասկանալ այսպես. Երբ երրորդ կողմի նախագծում մենք կանչում ենք հրամանը find_package(Mylib 1.2.3 REQUIRED), և տեղադրված գրադարանի իրական տարբերակը անհամատեղելի կլինի տարբերակի հետ 1.2.3CMake-ն ավտոմատ կերպով սխալ կառաջացնի: Այսինքն, ձեզ հարկավոր չի լինի ձեռքով հետևել տարբերակներին:
Եթե թեստերն անջատված են բացահայտորեն օգտագործելով համապատասխան տարբերակ կամ մեր նախագիծը ենթանախագծ է, այսինքն՝ հրամանի միջոցով միացված է մեկ այլ CMake նախագծին add_subdirectory, մենք ավելի առաջ չենք շարժվում հիերարխիայի երկայնքով, և սցենարը, որը նկարագրում է թեստեր ստեղծելու և գործարկելու հրամանները, պարզապես չի աշխատում:
if(NOT MYLIB_TESTING)
message(STATUS "Тестирование проекта Mylib выключено")
elseif(IS_SUBPROJECT)
message(STATUS "Mylib не тестируется в режиме подмодуля")
else()
add_subdirectory(test)
endif()
Նախևառաջ, մենք գտնում ենք անհրաժեշտ թեստային շրջանակով փաթեթ (փոխարինեք ձեր նախընտրածով):
find_package(doctest 2.3.3 REQUIRED)
Եկեք ստեղծենք մեր գործարկվող ֆայլը թեստերով: Սովորաբար ես ուղղակիորեն գործարկվող երկուականին ավելացնում եմ միայն այն ֆայլը, որը կպարունակի ֆունկցիան main.
add_executable(mylib-unit-tests test_main.cpp)
Եվ ես ավելացնում եմ ֆայլեր, որոնցում թեստերը նկարագրված են ավելի ուշ: Բայց դուք չպետք է դա անեք:
Մենք կապում ենք կախվածությունները: Խնդրում ենք նկատի ունենալ, որ մենք կապել ենք միայն մեզ անհրաժեշտ CMake թիրախները մեր երկուականին և չենք կանչել հրամանը target_include_directories. Վերնագրեր թեստի շրջանակից և մերից Mylib::mylib, ինչպես նաև կառուցման պարամետրերը (մեր դեպքում սա C++ լեզվի ստանդարտն է) ի հայտ են եկել այս նպատակների հետ մեկտեղ:
Վերջապես, մենք ստեղծում ենք կեղծ թիրախ, որի «կառուցումը» համարժեք է կատարվող թեստերին և ավելացնում ենք այս թիրախը լռելյայն կառուցմանը (հատկանիշը պատասխանատու է դրա համար. ALL) Սա նշանակում է, որ լռելյայն կառուցումը գործարկում է թեստերը, ինչը նշանակում է, որ մենք երբեք չենք մոռանա դրանք գործարկել:
add_custom_target(check ALL COMMAND mylib-unit-tests)
Հաջորդը, մենք միացնում ենք կոդի ծածկույթի չափումը, եթե նշված է համապատասխան տարբերակը: Ես չեմ մանրամասնի, քանի որ դրանք ավելի շատ վերաբերում են ծածկույթի չափման գործիքին, քան CMake-ին: Կարևոր է միայն նշել, որ արդյունքների հիման վրա կստեղծվի նպատակ coverage, որով հարմար է սկսել ծածկույթի չափումը։
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()
Հաջորդը, մենք ստուգում ենք, թե արդյոք օգտագործողը սահմանել է լեզվի փոփոխականը: Եթե այո, ուրեմն ձեռք չենք տալիս, եթե ոչ, ուրեմն ռուսերեն ենք ընդունում։ Այնուհետև մենք կարգավորում ենք Doxygen համակարգի ֆայլերը: Բոլոր անհրաժեշտ փոփոխականները, ներառյալ լեզուն, գնում են այնտեղ կազմաձևման գործընթացում (տես. команду configure_file).
Այնուհետև մենք նպատակ ենք ստեղծում doc, որը կսկսի փաստաթղթեր ստեղծել: Քանի որ փաստաթղթերի ստեղծումը զարգացման գործընթացում ամենամեծ կարիքը չէ, թիրախը լռելյայնորեն միացված չի լինի, այն պետք է հստակորեն գործարկվի:
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 ()
Այստեղ մենք գտնում ենք երրորդ Python-ը և ստեղծում թիրախ wandbox, որը առաջացնում է ծառայության API-ին համապատասխան հարցում Գավազանների տուփ, և ուղարկում է նրան։ Պատասխանը գալիս է պատրաստի ավազատուփի հղումով:
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()
Ապահովում է միավորի փորձարկման կառուցումը և թիրախավորումը անջատելու հնարավորություն check. Արդյունքում, թեստերով կոդի ծածկույթի չափումն անջատված է (տես. MYLIB_COVERAGE).
Փորձարկումը նույնպես ավտոմատ կերպով անջատվում է, եթե նախագիծը հրամանի միջոցով միացված է մեկ այլ նախագծի՝ որպես ենթանախագծ add_subdirectory.
Ծառայությունն օգտագործվում է դրա համար Գավազանների տուփ. Ես չգիտեմ, թե որքան ճկուն են նրանց սերվերները, բայց կարծում եմ, որ այս հնարավորությունը չպետք է չարաշահվի.
Փաստորեն, CMake 3.13 տարբերակը պահանջվում է միայն այս օգնության մեջ նկարագրված վահանակի հրամաններից մի քանիսը գործարկելու համար: CMake սկրիպտների շարահյուսության տեսանկյունից 3.8 տարբերակը բավարար է, եթե գեներացումն այլ կերպ է կոչվում։
Դրանից հետո ստատիկ վերլուծությունը ավտոմատ կերպով կգործարկվի ամեն անգամ, երբ աղբյուրը կազմվում և վերակոմպիլացվում է: Լրացուցիչ որևէ բան անելու կարիք չկա։
Ngնգոց
Հրաշալի գործիքի օգնությամբ scan-build Դուք կարող եք նաև կարճ ժամանակում կատարել ստատիկ վերլուծություն.
CMake-ը շատ հզոր և ճկուն համակարգ է, որը թույլ է տալիս իրականացնել ֆունկցիոնալություն յուրաքանչյուր ճաշակի և գույնի համար: Եվ, թեև շարահյուսությունը երբեմն շատ բան է թողնում, սատանան դեռ այնքան սարսափելի չէ, որքան նրան նկարում են: Օգտագործեք CMake build համակարգը՝ ի շահ հասարակության և առողջության: