CMake ak C++ se frè pou tout tan

CMake ak C++ se frè pou tout tan

Pandan devlopman, mwen renmen chanje konpilateur, bati mòd, vèsyon depandans, fè analiz estatik, mezire pèfòmans, kolekte pwoteksyon, jenere dokiman, elatriye. E mwen vrèman renmen CMake paske li pèmèt mwen fè tout sa mwen vle.

Anpil moun kritike CMake, e souvan merite sa, men si w gade li, se pa tout bagay ki tèlman mal, epi dènyèman. pa mal ditou, ak direksyon devlopman se byen pozitif.

Nan nòt sa a, mwen vle di ou ki jan yo tou senpleman òganize yon bibliyotèk header nan C ++ nan sistèm nan CMake pou jwenn fonksyonalite sa a:

  1. Asanble;
  2. Tès Autorun;
  3. Mezi kouvèti kòd;
  4. Enstalasyon;
  5. Oto-dokimantasyon;
  6. Jenerasyon sandbox sou entènèt;
  7. Analiz estatik.

Nenpòt moun ki deja konprann avantaj yo ak C-fè kapab tou senpleman telechaje modèl pwojè a epi kòmanse sèvi ak li.


Content

  1. Pwojè soti anndan an
    1. Estrikti pwojè
    2. Fichye prensipal CMake (./CMakeLists.txt)
      1. Enfòmasyon sou pwojè
      2. Opsyon Pwojè
      3. Opsyon konpilasyon
      4. Objektif prensipal la
      5. Enstalasyon
      6. Tès yo
      7. Dokimantasyon
      8. Bwat sab sou entènèt
    3. Script tès (test/CMakeLists.txt)
      1. Tès
      2. Kouvèti
    4. Script pou dokimantasyon (doc/CMakeLists.txt)
    5. Script pou sandbox sou entènèt (sou entènèt/CMakeLists.txt)
  2. Pwojè deyò
    1. Asanble
      1. Jenerasyon
      2. Asanble
    2. Opsyon
      1. MYLIB_COVERAGE
      2. MYLIB_TESTING
      3. MYLIB_DOXYGEN_LANGUAGE
    3. Objektif asanble yo
      1. Pa default
      2. mylib-inite-tès
      3. tcheke
      4. pwoteksyon
      5. doc
      6. bwat ralonj
    4. egzanp
  3. Zouti
  4. Analiz estatik
  5. Apreword

Pwojè soti anndan an

Estrikti pwojè

.
├── 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.cpp

Nou pral sitou pale sou ki jan yo òganize scripts CMake, kidonk yo pral diskite an detay. Nenpòt moun ka wè rès fichye yo dirèkteman sou paj pwojè modèl la.

Fichye prensipal CMake (./CMakeLists.txt)

Enfòmasyon sou pwojè

Premye a tout, ou bezwen mande vèsyon ki nesesè nan sistèm CMake la. CMake ap evolye, siyati kòmand ak konpòtman nan diferan kondisyon yo ap chanje. Nan lòd pou CMake imedyatman konprann sa nou vle soti nan li, nou bezwen imedyatman anrejistre kondisyon nou yo pou li.

cmake_minimum_required(VERSION 3.13)

Lè sa a, nou pral deziyen pwojè nou an, non li, vèsyon, lang yo itilize, elatriye (gade. команду project).

Nan ka sa a nou endike lang lan CXX (e sa vle di C++) pou CMake pa souch epi chèche yon du lang C (pa default, CMake gen ladann de lang: C ak C++).

project(Mylib VERSION 1.0 LANGUAGES CXX)

Isit la ou ka imedyatman tcheke si pwojè nou an enkli nan yon lòt pwojè kòm yon sou-pwojè. Sa a pral ede anpil nan tan kap vini an.

get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)

Opsyon Pwojè

Nou pral bay de opsyon.

Premye opsyon a se MYLIB_TESTING — pou enfim tès inite yo. Sa a ka nesesè si nou sèten ke tout bagay an lòd ak tès yo, men nou sèlman vle, pou egzanp, enstale oswa pake pwojè nou an. Oswa pwojè nou an enkli kòm yon subproject - nan ka sa a, itilizatè a nan pwojè nou an pa enterese nan kouri tès nou yo. Ou pa teste depandans ou itilize yo, pa vre?

option(MYLIB_TESTING "Включить модульное тестирование" ON)

Anplis de sa, nou pral fè yon opsyon separe MYLIB_COVERAGE pou mezire kouvèti kòd pa tès yo, men li pral mande pou zouti adisyonèl, kidonk li pral bezwen aktive klèman.

option(MYLIB_COVERAGE "Включить измерение покрытия кода тестами" OFF)

Opsyon konpilasyon

Natirèlman, nou se fre plis pwogramasyon, kidonk nou vle nivo maksimòm dyagnostik konpile-tan soti nan du a. Pa yon sèl sourit pral glise nan.

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
)

Nou pral tou enfim ekstansyon yo nan lòd yo konplètman konfòme li avèk estanda lang C++ la. Yo aktive pa default nan CMake.

if(NOT CMAKE_CXX_EXTENSIONS)
    set(CMAKE_CXX_EXTENSIONS OFF)
endif()

Objektif prensipal la

Bibliyotèk nou an konsiste sèlman de dosye header, ki vle di nou pa gen okenn echapman sou fòm bibliyotèk estatik oswa dinamik. Nan lòt men an, yo nan lòd yo sèvi ak bibliyotèk nou an deyò, li bezwen yo dwe enstale, li bezwen yo dwe detekte nan sistèm nan ak konekte ak pwojè ou a, ak an menm tan an menm en-tête sa yo, osi byen ke pètèt kèk lòt adisyonèl, yo tache ak li pwopriyete.

Pou rezon sa a, nou kreye yon bibliyotèk koòdone.

add_library(mylib INTERFACE)

Nou mare headers nan bibliyotèk koòdone nou an.

Modèn, alamòd, itilizasyon jèn nan CMake implique ke headers, pwopriyete, elatriye. transmèt atravè yon sèl sib. Se konsa, li sifi pou di target_link_libraries(target PRIVATE dependency), ak tout headers ki asosye ak sib la dependency, yo pral disponib pou sous ki fè pati sib la target. Epi ou pa bezwen okenn [target_]include_directories. Sa a pral demontre anba a nan analiz la CMake script pou tès inite yo.

Li se tou vo peye atansyon sou sa yo rele an. выражения-генераторы: $<...>.

Kòmandman sa a asosye tèt nou bezwen yo ak bibliyotèk koòdone nou an, epi si bibliyotèk nou an konekte ak nenpòt sib ki nan menm yerachi CMake la, Lè sa a, tèt yo ki soti nan anyè a pral asosye avèk li. ${CMAKE_CURRENT_SOURCE_DIR}/include, epi si bibliyotèk nou an enstale sou sistèm nan epi konekte ak yon lòt pwojè lè l sèvi avèk lòd la find_package, Lè sa a, headers soti nan anyè a pral asosye ak li include parapò ak anyè enstalasyon an.

target_include_directories(mylib INTERFACE
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

Ann mete yon estanda lang. Natirèlman, yon sèl la trè dènye. An menm tan an, nou pa sèlman enkli estanda a, men tou, pwolonje li bay moun ki pral sèvi ak bibliyotèk nou an. Sa a se reyalize akòz lefèt ke pwopriyete a mete gen yon kategori INTERFACE (cm. target_compile_features kòmand).

target_compile_features(mylib INTERFACE cxx_std_17)

Ann kreye yon alyas pou bibliyotèk nou an. Anplis, pou bote, li pral nan yon espesyal "namespace". Sa a pral itil lè diferan modil parèt nan bibliyotèk nou an, epi nou ale nan konekte yo poukont youn ak lòt. Tankou nan Busta, pou egzanp.

add_library(Mylib::mylib ALIAS mylib)

Enstalasyon

Enstale headers nou yo nan sistèm lan. Tout bagay se senp isit la. Nou di ke katab la ak tout tèt yo ta dwe ale nan anyè a include parapò ak kote enstalasyon an.

install(DIRECTORY include/mylib DESTINATION include)

Apre sa, nou enfòme sistèm nan bati ke nou vle pou kapab rele lòd la nan pwojè twazyèm pati find_package(Mylib) epi jwenn yon objektif Mylib::mylib.

install(TARGETS mylib EXPORT MylibConfig)
install(EXPORT MylibConfig NAMESPACE Mylib:: DESTINATION share/Mylib/cmake)

Pwochen eple a ta dwe konprann fason sa a. Lè nan yon pwojè twazyèm pati nou rele lòd la find_package(Mylib 1.2.3 REQUIRED), ak vèsyon reyèl la nan bibliyotèk la enstale yo pral enkonpatib ak vèsyon an 1.2.3CMake pral otomatikman jenere yon erè. Sa vle di, ou pa pral bezwen swiv vèsyon manyèlman.

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)

Tès yo

Si tès yo enfim klèman lè l sèvi avèk opsyon ki koresponn lan oswa pwojè nou an se yon sou-pwojè, se sa ki, li konekte ak yon lòt pwojè CMake lè l sèvi avèk lòd la add_subdirectory, nou pa avanse pi lwen sou yerachi a, ak script la, ki dekri kòmandman yo pou jenere ak kouri tès, tou senpleman pa kouri.

if(NOT MYLIB_TESTING)
    message(STATUS "Тестирование проекта Mylib выключено")
elseif(IS_SUBPROJECT)
    message(STATUS "Mylib не тестируется в режиме подмодуля")
else()
    add_subdirectory(test)
endif()

Dokimantasyon

Dokimantasyon pa pral pwodwi tou nan ka a nan yon sou-pwojè.

if(NOT IS_SUBPROJECT)
    add_subdirectory(doc)
endif()

Bwat sab sou entènèt

Menm jan an tou, sou-pwojè a pa pral gen yon bwat sab sou entènèt tou.

if(NOT IS_SUBPROJECT)
    add_subdirectory(online)
endif()

Script tès (test/CMakeLists.txt)

Tès

Premye a tout, nou jwenn yon pake ak fondasyon tès ki nesesè yo (ranplase ak youn pi renmen ou).

find_package(doctest 2.3.3 REQUIRED)

Ann kreye dosye ègzèkutabl nou an ak tès yo. Anjeneral mwen ajoute dirèkteman nan binè a ègzèkutabl sèlman dosye a ki pral genyen fonksyon an main.

add_executable(mylib-unit-tests test_main.cpp)

Apre sa, mwen ajoute dosye nan ki tès yo tèt yo dekri pita. Men, ou pa bezwen fè sa.

target_sources(mylib-unit-tests PRIVATE mylib/myfeature.cpp)

Nou konekte depandans. Tanpri sonje ke nou lye sèlman sib CMake nou te bezwen nan binè nou an epi yo pa t rele kòmandman an target_include_directories. Tit ki soti nan kad tès la ak nan pa nou an Mylib::mylib, osi byen ke bati paramèt (nan ka nou an, sa a se estanda lang C++) te vini ansanm ak objektif sa yo.

target_link_libraries(mylib-unit-tests
    PRIVATE
        Mylib::mylib
        doctest::doctest
)

Finalman, nou kreye yon sib enbesil, "konstriksyon an" ki ekivalan a kouri tès yo, epi ajoute sib sa a nan bati default la (atribi a responsab pou sa a. ALL). Sa vle di ke konstriksyon default la deklannche tès yo kouri, sa vle di nou p'ap janm bliye kouri yo.

add_custom_target(check ALL COMMAND mylib-unit-tests)

Kouvèti

Apre sa, nou pèmèt mezi kouvèti kòd si opsyon ki apwopriye a espesifye. Mwen pa pral antre nan detay, paske yo gen rapò plis ak yon zouti pou mezire pwoteksyon pase CMake. Li enpòtan sèlman sonje ke baze sou rezilta yo pral kreye yon objektif coverage, ak ki li se pratik yo kòmanse mezire pwoteksyon.

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()

Script pou dokimantasyon (doc/CMakeLists.txt)

Jwenn Doxygen.

find_package(Doxygen)

Apre sa, nou tcheke si itilizatè a te mete varyab lang nan. Si wi, Lè sa a, nou pa manyen li, si se pa, Lè sa a, nou pran Ris. Lè sa a, nou konfigirasyon dosye sistèm Doxygen yo. Tout varyab ki nesesè yo, ki gen ladan lang lan, ale la pandan pwosesis konfigirasyon an (gade. команду configure_file).

Lè sa a, nou kreye yon objektif doc, ki pral kòmanse jenere dokiman. Piske jenere dokiman se pa pi gwo bezwen nan pwosesis devlopman an, sib la pa pral aktive pa default; li pral oblije lanse klèman.

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 ()

Script pou sandbox sou entènèt (sou entènèt/CMakeLists.txt)

Isit la nou jwenn twazyèm Python la epi kreye yon sib wandbox, ki jenere yon demann ki koresponn ak API sèvis la Wandbox, epi li voye l ale. Repons lan vini ak yon lyen ki mennen nan bwat sab la fini.

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()

Pwojè deyò

Koulye a, kite a gade nan ki jan yo sèvi ak tout bagay sa yo.

Asanble

Bati pwojè sa a, tankou nenpòt lòt pwojè sou sistèm konstriksyon CMake, gen de etap:

Jenerasyon

cmake -S путь/к/исходникам -B путь/к/сборочной/директории [опции ...]

Si lòd ki anwo a pa t travay akòz yon ansyen vèsyon CMake, eseye omisyon -S:

cmake путь/к/исходникам -B путь/к/сборочной/директории [опции ...]

Plis sou opsyon.

Bati pwojè a

cmake --build путь/к/сборочной/директории [--target target]

Plis sou objektif asanble yo.

Opsyon

MYLIB_COVERAGE

cmake -S ... -B ... -DMYLIB_COVERAGE=ON [прочие опции ...]

Gen ladan sib coverage, ak ki ou ka kòmanse mezire kouvèti kòd pa tès yo.

MYLIB_TESTING

cmake -S ... -B ... -DMYLIB_TESTING=OFF [прочие опции ...]

Bay kapasite pou enfim bati tès inite ak sib check. Kòm yon rezilta, mezi kouvèti kòd pa tès yo etenn (gade. MYLIB_COVERAGE).

Tès la tou otomatikman enfim si pwojè a konekte ak yon lòt pwojè kòm yon sous-pwojè lè l sèvi avèk kòmandman an add_subdirectory.

MYLIB_DOXYGEN_LANGUAGE

cmake -S ... -B ... -DMYLIB_DOXYGEN_LANGUAGE=English [прочие опции ...]

Chanje lang nan dokiman an ke sib la jenere doc bay youn nan. Pou yon lis lang ki disponib, gade Sit entènèt sistèm Doxygen.

Larisi aktive pa default.

Objektif asanble yo

Pa default

cmake --build path/to/build/directory
cmake --build path/to/build/directory --target all

Si sib la pa espesifye (ki ekivalan a sib la all), kolekte tout sa li kapab, epi tou li rele sib la check.

mylib-inite-tès

cmake --build path/to/build/directory --target mylib-unit-tests

Konpile tès inite yo. Aktive pa default.

tcheke

cmake --build путь/к/сборочной/директории --target check

Kouri tès inite yo kolekte (kolekte, si se pa deja). Aktive pa default.

См. также mylib-unit-tests.

pwoteksyon

cmake --build путь/к/сборочной/директории --target coverage

Analize kouri (kouri, si se pa deja) tès inite pou kouvèti kòd pa tès lè l sèvi avèk pwogram nan gcovr.

Echapman kouch la pral gade yon bagay tankou sa a:

------------------------------------------------------------------------------
                           GCC Code Coverage Report
Directory: /path/to/cmakecpptemplate/include/
------------------------------------------------------------------------------
File                                       Lines    Exec  Cover   Missing
------------------------------------------------------------------------------
mylib/myfeature.hpp                            2       2   100%   
------------------------------------------------------------------------------
TOTAL                                          2       2   100%
------------------------------------------------------------------------------

Sib la disponib sèlman lè opsyon a pèmèt MYLIB_COVERAGE.

См. также check.

doc

cmake --build путь/к/сборочной/директории --target doc

Kòmanse jenerasyon dokiman kòd lè l sèvi avèk sistèm nan Oksijèn.

bwat ralonj

cmake --build путь/к/сборочной/директории --target wandbox

Repons nan sèvis la sanble yon bagay tankou sa a:

{
    "permlink" :    "QElvxuMzHgL9fqci",
    "status" :  "0",
    "url" : "https://wandbox.org/permlink/QElvxuMzHgL9fqci"
}

Se sèvis la itilize pou sa Wandbox. Mwen pa konnen ki jan fleksib sèvè yo, men mwen panse ke opòtinite sa a pa ta dwe abize.

egzanp

Bati pwojè a nan mòd debug ak mezi pwoteksyon

cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Debug -DMYLIB_COVERAGE=ON
cmake --build путь/к/сборочной/директории --target coverage --parallel 16

Enstale yon pwojè san yo pa asanble preliminè ak tès

cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DMYLIB_TESTING=OFF -DCMAKE_INSTALL_PREFIX=путь/к/установойной/директории
cmake --build путь/к/сборочной/директории --target install

Bati nan mòd lage ak yon konpilatè bay yo

cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_PREFIX_PATH=путь/к/директории/куда/установлены/зависимости
cmake --build путь/к/сборочной/директории --parallel 4

Jenere dokiman an angle

cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Release -DMYLIB_DOXYGEN_LANGUAGE=English
cmake --build путь/к/сборочной/директории --target doc

Zouti

  1. CMake 3.13

    An reyalite, vèsyon 3.13 CMake sèlman oblije kouri kèk nan kòmandman konsole ki dekri nan èd sa a. Soti nan pwen de vi nan sentaks la nan scripts CMake, vèsyon 3.8 se ase si jenerasyon yo rele nan lòt fason.

  2. Tès bibliyotèk doktest

    Tès yo ka enfim (gade опцию MYLIB_TESTING).

  3. Oksijèn

    Pou chanje lang nan ki dokiman an pral pwodwi, yo bay yon opsyon MYLIB_DOXYGEN_LANGUAGE.

  4. Entèprèt lang Piton 3

    Pou jenerasyon otomatik bwat sab sou entènèt.

Analiz estatik

Avèk CMake ak yon koup nan zouti bon, ou ka bay analiz estatik ak efò minim.

Cppcheck

CMake gen sipò entegre pou yon zouti analiz estatik Cppcheck.

Pou fè sa ou bezwen sèvi ak opsyon an CMAKE_CXX_CPPCHECK:

cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_CPPCHECK="cppcheck;--enable=all;-Iпуть/к/исходникам/include"

Apre sa, analiz estatik yo pral otomatikman lanse chak fwa sous la konpile ak rekonpile. Pa gen okenn nesesite pou fè anyen anplis.

sonnen

Avèk èd nan yon zouti bèl bagay scan-build Ou kapab tou kouri analiz estatik nan pa gen tan:

scan-build cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Debug
scan-build cmake --build путь/к/сборочной/директории

Isit la, kontrèman ak ka a ak Cppcheck, ou bezwen kouri bati a chak fwa nan scan-build.

Apreword

CMake se yon sistèm trè pwisan ak fleksib ki pèmèt ou aplike fonksyonalite pou chak gou ak koulè. Epi, byenke sentaks la pafwa kite anpil yo dwe vle, dyab la toujou pa terib tankou li pentire. Sèvi ak sistèm CMake build la pou benefis sosyete a ak sante.

Telechaje modèl pwojè a

Sous: www.habr.com

Add nouvo kòmantè