CMake an C++ si Bridder fir ëmmer

CMake an C++ si Bridder fir ëmmer

Wärend der Entwécklung hunn ech gär Compiler änneren, Modi bauen, Ofhängegkeetsversioune, statesch Analyse ausféieren, Leeschtung moossen, Ofdeckung sammelen, Dokumentatioun generéieren, etc. An ech hunn CMake wierklech gär well et mir erlaabt alles ze maachen wat ech wëll.

Vill Leit kritiséieren CMake, an dacks verdéngt, awer wann Dir et kuckt, ass net alles sou schlecht, a viru kuerzem guer net schlecht, an d'Richtung vun der Entwécklung ass ganz positiv.

An dëser Notiz wëll ech Iech soen wéi Dir einfach eng Headerbibliothéik an C ++ am CMake System organiséiert fir déi folgend Funktionalitéit ze kréien:

  1. Assemblée;
  2. Autorun Tester;
  3. Code Ofdeckung Miessung;
  4. Installatioun;
  5. Auto Dokumentatioun;
  6. Online Sandkëscht Generatioun;
  7. Statesch Analyse.

Jiddereen deen schonn d'Virdeeler an C-Make versteet kann einfach download Projet Schabloun an ufänken et ze benotzen.


Inhalt

  1. Projet vu bannen
    1. Projet Struktur
    2. Main CMake Datei (./CMakeLists.txt)
      1. Projet Informatiounen
      2. Projet Optiounen
      3. Compilatioun Optiounen
      4. Haaptziel
      5. Kader
      6. Tester
      7. Dokumentatioun
      8. Online Sandkëscht
    3. Testskript (test/CMakeLists.txt)
      1. Testen
      2. Coating
    4. Skript fir Dokumentatioun (doc/CMakeLists.txt)
    5. Skript fir Online Sandkëscht (online/CMakeLists.txt)
  2. Projet ausserhalb
    1. Assemblée
      1. Generatioun
      2. Assemblée
    2. Optiounen
      1. MYLIB_COVERAGE
      2. MYLIB_TESTING
      3. MYLIB_DOXYGEN_LANGUAGE
    3. Assemblée Ziler
      1. Par défaut
      2. mylib-Eenheet-Tester
      3. kontrolléieren
      4. duerginn
      5. Dokter
      6. wandbox
    4. Beispiller
  3. Tools
  4. Statesch Analyse
  5. Afterword

Projet vu bannen

Projet Struktur

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

Mir schwätzen haaptsächlech iwwer wéi CMake Scripten organiséiert ginn, sou datt se am Detail diskutéiert ginn. Jiddereen kann de Rescht vun de Fichieren direkt gesinn op der Schablounprojet Säit.

Main CMake Datei (./CMakeLists.txt)

Projet Informatiounen

Als éischt musst Dir déi erfuerderlech Versioun vum CMake System ufroen. CMake entwéckelt sech, Kommando Ënnerschrëften a Verhalen a verschiddene Konditiounen änneren. Fir datt CMake direkt versteet wat mir dovunner wëllen, musse mir direkt eis Ufuerderungen dofir notéieren.

cmake_minimum_required(VERSION 3.13)

Da bezeechnen mir eise Projet, säin Numm, Versioun, benotzte Sproochen, asw. (kuckt. команду project).

An dësem Fall weisen mir d'Sprooch un CXX (an dat heescht C ++) sou datt CMake net belaascht a sicht no engem C Sprooche-Compiler (par défaut enthält CMake zwou Sproochen: C an C ++).

project(Mylib VERSION 1.0 LANGUAGES CXX)

Hei kënnt Dir direkt kucken ob eise Projet an engem anere Projet als Ënnerprojet mat abegraff ass. Dëst wäert an Zukunft vill hëllefen.

get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)

Projet Optiounen

Mir ginn zwou Méiglechkeeten.

Déi éischt Optioun ass MYLIB_TESTING - Eenheetstester auszeschalten. Dëst kann néideg sinn wa mir sécher sinn datt alles an der Rei ass mat den Tester, awer mir wëllen nëmmen zum Beispill eise Projet installéieren oder packen. Oder eise Projet ass als Ënnerprojet abegraff - an dësem Fall ass de Benotzer vun eisem Projet net interesséiert fir eis Tester auszeféieren. Dir testt d'Ofhängegkeeten net déi Dir benotzt, oder?

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

Zousätzlech wäerte mir eng separat Optioun maachen MYLIB_COVERAGE fir d'Codedeckung duerch Tester ze moossen, awer et erfuerdert zousätzlech Tools, also muss et explizit aktivéiert ginn.

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

Compilatioun Optiounen

Natierlech si mir cool Plus Programméierer, also wëlle mir de maximalen Niveau vun der Compile-Zäit Diagnostik vum Compiler. Net eng eenzeg Maus rutscht duerch.

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
)

Mir wäerten och Extensiounen ausschalten fir voll mam C++ Sproochestandard ze respektéieren. Si sinn als Standard am CMake aktivéiert.

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

Haaptziel

Eis Bibliothéik besteet nëmmen aus Headerdateien, dat heescht datt mir keen Auspuff a Form vu statesche oder dynamesche Bibliothéiken hunn. Op der anerer Säit, fir eis Bibliothéik extern ze benotzen, muss se installéiert ginn, et muss am System festgestallt ginn a mat Ärem Projet verbonne sinn, a gläichzäiteg déiselwecht Header, wéi och eventuell e puer zousätzlech, sinn un et Eegeschafte verbonnen.

Fir dësen Zweck erstellen mir eng Interfacebibliothéik.

add_library(mylib INTERFACE)

Mir binden Header un eis Interfacebibliothéik.

Modern, modesch, Jugend Notzung vu CMake implizéiert datt Header, Eegeschaften, asw. duerch een eenzegt Zil iwwerdroen. Also ass et genuch ze soen target_link_libraries(target PRIVATE dependency), an all Header déi mam Zil verbonne sinn dependency, wäert verfügbar sinn fir Quellen déi zum Zil gehéieren target. An Dir braucht keng [target_]include_directories. Dëst wäert ënnen an der Analyse bewisen ginn CMake Skript fir Eenheetstester.

Et ass och derwäert oppassen op de sougenannte. выражения-генераторы: $<...>.

Dëse Kommando assoziéiert d'Header déi mir brauchen mat eiser Interfacebibliothéik, a wann eis Bibliothéik mat engem Zil an der selwechter CMake Hierarchie verbonnen ass, da ginn d'Header aus dem Verzeechnes mat deem verbonnen. ${CMAKE_CURRENT_SOURCE_DIR}/include, a wann eis Bibliothéik um System installéiert ass a mat engem anere Projet mat dem Kommando verbonnen ass find_package, da ginn Header aus dem Verzeichnis domat verbonnen include relativ zum Installatiounsverzeichnis.

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

Loosst eis e Sproochestandard setzen. Natierlech, déi lescht. Zur selwechter Zäit enthalen mir net nëmmen de Standard, mee verlängeren se och op déi, déi eis Bibliothéik benotzen. Dëst gëtt erreecht wéinst der Tatsaach datt d'Set Immobilie eng Kategorie huet INTERFACE (cm. target_compile_features Kommando).

target_compile_features(mylib INTERFACE cxx_std_17)

Loosst eis en Alias ​​​​fir eis Bibliothéik erstellen. Ausserdeem, fir Schéinheet, wäert et an engem speziellen "Namespace" sinn. Dëst wäert nëtzlech sinn wann verschidde Moduler an eiser Bibliothéik erschéngen, a mir gi se onofhängeg vuneneen verbannen. Wéi zum Beispill zu Busta.

add_library(Mylib::mylib ALIAS mylib)

Kader

Installéiere vun eise Header an de System. Alles ass einfach hei. Mir soen datt den Dossier mat all den Header an den Dossier sollt goen include relativ zu der Installatioun Plaz.

install(DIRECTORY include/mylib DESTINATION include)

Als nächst informéiere mir de Build-System datt mir de Kommando an Drëtt-Partei-Projeten ruffen wëllen find_package(Mylib) a kritt e Goal Mylib::mylib.

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

Déi nächst Zauber soll esou verstane ginn. Wann an engem Drëtte-Partei Projet ruffe mir de Kommando find_package(Mylib 1.2.3 REQUIRED), an déi richteg Versioun vun der installéierter Bibliothéik wäert mat der Versioun inkompatibel sinn 1.2.3CMake generéiert automatesch e Feeler. Dat ass, Dir braucht keng Versiounen manuell ze verfollegen.

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)

Tester

Wann Tester behënnert explizit benotzt entspriechend Optioun oder eise Projet ass en Ënnerprojet, dat heescht, et ass mat engem anere CMake Projet verbonne mat dem Kommando add_subdirectory, mir réckelen net weider laanscht d'Hierarchie, an de Skript, deen d'Befehle beschreift fir Tester ze generéieren an ze lafen, leeft einfach net.

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

Dokumentatioun

Dokumentatioun gëtt och net am Fall vun engem Ënnerprojet generéiert.

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

Online Sandkëscht

Och den Ënnerprojet wäert keng Online Sandkëscht hunn.

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

Testskript (test/CMakeLists.txt)

Testen

Als éischt fanne mir e Package mat dem erfuerderlechen Testkader (ersetzen duerch Äre Liiblings).

find_package(doctest 2.3.3 REQUIRED)

Loosst eis eis ausführbar Datei mat Tester erstellen. Normalerweis fügen ech direkt un den ausführbare Binär nëmmen d'Datei déi d'Funktioun enthält main.

add_executable(mylib-unit-tests test_main.cpp)

An ech addéieren Dateien an deenen d'Tester selwer spéider beschriwwe ginn. Awer Dir musst dat net maachen.

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

Mir verbannen Ofhängegkeeten. Notéiert w.e.g. datt mir nëmmen déi CMake Ziler verbonnen hunn, déi mir gebraucht hunn, un eis Binär an hunn de Kommando net genannt target_include_directories. Rubriken aus dem Test Kader an aus eisem Mylib::mylib, souwéi Bauparameter (an eisem Fall ass dëst de C++ Sproochestandard) mat dësen Ziler duerchkomm.

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

Schlussendlech kreéiere mir en Dummy-Zil, de "Build" vun deem entsprécht Tester lafen, a füügt dëst Zil un de Standardbau (d'Attribut ass verantwortlech fir dëst ALL). Dëst bedeit datt de Standardbau d'Tester ausléist fir ze lafen, dat heescht datt mir ni vergiessen se ze lafen.

add_custom_target(check ALL COMMAND mylib-unit-tests)

Coating

Als nächst aktivéiere mir Code Ofdeckungsmessung wann déi entspriechend Optioun uginn ass. Ech ginn net an d'Detailer, well se méi mat engem Tool fir d'Ofdeckung ze moossen bezéien wéi op CMake. Et ass nëmme wichteg ze bemierken datt op Basis vun de Resultater en Zil erstallt gëtt coverage, mat deem et bequem ass d'Ofdeckung ze moossen.

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

Skript fir Dokumentatioun (doc/CMakeLists.txt)

Doxygen fonnt.

find_package(Doxygen)

Als nächst kucken mir ob de Benotzer d'Sproochvariabel gesat huet. Wann jo, da beréieren mir et net, wann net, da huelen mir Russesch. Da konfiguréiere mir d'Doxygen Systemdateien. All déi néideg Variabelen, och d'Sprooch, ginn do wärend dem Konfiguratiounsprozess (kuckt. команду configure_file).

Da kreéiere mir e Goal doc, déi fänkt un d'Dokumentatioun ze generéieren. Zënter datt d'Dokumentatioun net de gréisste Bedierfnes am Entwécklungsprozess ass, gëtt d'Zil net als Standard aktivéiert; et muss explizit lancéiert ginn.

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

Skript fir Online Sandkëscht (online/CMakeLists.txt)

Hei fanne mir den drëtten Python a kreéieren en Zil wandbox, déi eng Demande generéiert entsprécht dem Service API Wandbox, a schéckt hien ewech. D'Äntwert kënnt mat engem Link op déi fäerdeg Sandkëscht.

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

Projet ausserhalb

Loosst eis elo kucken wéi Dir dëst alles benotzt.

Assemblée

Dëse Projet bauen, wéi all aner Projet am CMake Build System, besteet aus zwou Etappen:

Generatioun

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

Wann de Kommando uewendriwwer net funktionnéiert huet wéinst enger aler Versioun vum CMake, probéiert et ofzeginn -S:

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

Méi iwwer Optiounen.

Bau vum Projet

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

Méi iwwer Versammlungsziler.

Optiounen

MYLIB_COVERAGE

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

Ëmfaasst Zil coverage, mat deem Dir d'Codedeckung duerch Tester moosse kënnt.

MYLIB_TESTING

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

Bitt d'Fähigkeit fir Eenheetstestbau an Zil auszeschalten check. Als Resultat gëtt d'Messung vun der Codeofdeckung duerch Tester ausgeschalt (kuckt. MYLIB_COVERAGE).

Testen ass och automatesch deaktivéiert wann de Projet mat engem anere Projet als Ënnerprojet mam Kommando verbonnen ass add_subdirectory.

MYLIB_DOXYGEN_LANGUAGE

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

Schalt d'Sprooch vun der Dokumentatioun déi d'Zil generéiert doc zu der ginn. Fir eng Lëscht vun verfügbare Sproochen, kuckt Doxygen System Websäit.

Russesch ass par défaut aktivéiert.

Assemblée Ziler

Par défaut

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

Wann d'Zil net spezifizéiert ass (wat entsprécht dem Zil all), sammelt alles wat et kann, a rifft och d'Zil check.

mylib-Eenheet-Tester

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

Kompiléiert Eenheetstester. Par défaut aktivéiert.

kontrolléieren

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

Leeft déi gesammelt (gesammelt, wann net schonn) Eenheetstester. Par défaut aktivéiert.

Kuckt och mylib-unit-tests.

duerginn

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

Analyséiert Lafen (läuft, wann net schonn) Eenheetstester fir Codedeckung duerch Tester mam Programm gcovr.

De Beschichtungsauspuff wäert sou ausgesinn:

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

D'Zil ass nëmme verfügbar wann d'Optioun aktivéiert ass MYLIB_COVERAGE.

Kuckt och check.

Dokter

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

Fänkt Generatioun vu Codedokumentatioun mam System un Sauerstoff.

wandbox

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

D'Äntwert vum Service gesäit sou aus:

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

De Service gëtt dofir benotzt Wandbox. Ech weess net wéi flexibel hir Serveren sinn, awer ech mengen datt dës Geleeënheet net misst mëssbraucht ginn.

Beispiller

Baut de Projet am Debugmodus mat Ofdeckungsmessung

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

Installatioun vun engem Projet ouni virleefeg Assemblée an Testen

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

Bauen am Verëffentlechungsmodus mat engem bestëmmte Compiler

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

Generéiere Dokumentatioun op Englesch

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

Tools

  1. CMaach 3.13

    Tatsächlech ass CMake Versioun 3.13 nëmmen erfuerderlech fir e puer vun de Konsolbefehle ze lafen, déi an dëser Hëllef beschriwwe ginn. Aus der Siicht vun der Syntax vu CMake Scripten ass d'Versioun 3.8 genuch wann d'Generatioun op aner Manéier genannt gëtt.

  2. Testen Bibliothéik doctest

    Testen kënnen ausgeschalt ginn (kuckt опцию MYLIB_TESTING).

  3. Sauerstoff

    Fir d'Sprooch ze wiesselen an där d'Dokumentatioun generéiert gëtt, gëtt eng Optioun ugebueden MYLIB_DOXYGEN_LANGUAGE.

  4. Sprooch Dolmetscher Python 3

    Fir automatesch Generatioun online sandboxen.

Statesch Analyse

Mat CMake an e puer gutt Tools kënnt Dir statesch Analyse mat minimalem Effort ubidden.

Cppcheck

CMake huet gebaut-an Ënnerstëtzung fir eng statesch Analyse Outil Cppcheck.

Fir dëst ze maachen, musst Dir d'Optioun benotzen CMAKE_CXX_CPPCHECK:

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

Duerno gëtt d'statesch Analyse automatesch gestart all Kéier wann d'Quell kompiléiert an nei kompiléiert gëtt. Et ass net néideg eppes zousätzlech ze maachen.

Kleng

Mat der Hëllef vun engem wonnerbar Outil scan-build Dir kënnt och statesch Analyse a kuerzer Zäit lafen:

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

Hei, am Géigesaz zum Fall mat Cppcheck, musst Dir de Build all Kéier duerch lafen scan-build.

Afterword

CMake ass e ganz mächtegen a flexibele System deen Iech erlaabt Funktionalitéit fir all Goût a Faarf ëmzesetzen. An och wann d'Syntax heiansdo vill ze Wënsch léisst, ass den Däiwel nach ëmmer net esou schrecklech wéi hien gemoolt ass. Benotzt de CMake Build System fir de Virdeel vun der Gesellschaft a Gesondheet.

Download Projet Schabloun

Source: will.com

Setzt e Commentaire