
A fejlesztés során szeretek váltogatni a fordítóprogramok, a build módok, a függőségek verziói között, statikus elemzést végezni, teljesítményt mérni, lefedettséget összegyűjteni, dokumentációt generálni stb. És nagyon szeretem a CMake-et, mert lehetővé teszi, hogy mindent megtegyek, amit szeretnék.
Sokan kritizálják a CMake-et, és gyakran jogosan, de ha alaposan megnézzük, nem olyan rossz a helyzet, és az utóbbi időben nagyon is jónak mondható, és a fejlődési irány teljesen pozitív.
Ebben a bejegyzésben arról szeretnék beszélni, hogy hogyan lehet viszonylag egyszerűen megszervezni egy C++ fejléces könyvtárat a CMake rendszerében, hogy a következő funkciókat kapjuk:
- Build;
- Automatikus tesztelés;
- Kódlefedettség mérése;
- Telepítés;
- Automatikus dokumentálás;
- Online homokozó generálás;
- Statisztikai elemzés.
Aki már jártas a C++-ban és a CMake-ben, az egyszerűen és elkezdheti használni.
Tartalom
.
├── 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.cppFőként arról lesz szó, hogy hogyan szervezzük meg a CMake szkripteket, ezért ezeket részletesen fogjuk kielemezni. A többi fájlt mindenki nézheti a projekt sablon oldalán. .
cmake_minimum_required(VERSION 3.13)
Ezután jelöljük be a projektünket, annak nevét, verzióját, használni kívánt nyelveit és egyéb részleteit (lásd aproject parancsot) ).
CXX (ami a C++-t jelenti), hogy a CMake ne feszengjen, és ne keresse a C nyelv fordítóját (alapértelmezetten a CMake-ben két nyelv van: C és C++). project(Mylib VERSION 1.0 LANGUAGES CXX)
project(Mylib VERSION 1.0 LANGUAGES CXX)Itt azonnal ellenőrizheti, hogy a projektünk egy másik projekt alprojektjeként szerepel-e. Ez nagyban segít a későbbiekben.
get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)
Két opciót fogunk figyelembe venni.
Az első opció — — a moduláris tesztek letiltása. Erre szükség lehet, ha biztosak vagyunk abban, hogy a tesztek rendben vannak, és csak a projektünket szeretnénk telepíteni vagy csomagolni. Vagy ha a projektünk alprojektként van beállítva — ebben az esetben a projektünk felhasználója nem érdekelt a tesztjeink futtatásában. Ugye nem teszteli a használt függőségeket?
option(MYLIB_TESTING "Moduláris tesztelés engedélyezése" ON)Ezen kívül létrehozunk egy külön opciót a tesztekkel való kódfedettség mérésére, de ehhez további eszközökre lesz szükség, ezért azt kifejezetten engedélyezni kell.
option(MYLIB_COVERAGE "Kódfedettség mérése tesztekkel" OFF)
Természetesen csúcsprogramozók vagyunk, ezért a fordítótól a lehető legmagasabb szintű fordítási diagnosztikát szeretnénk. Egy egér sem fog elmenekülni.
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
)A kiterjesztéseket is letiltjuk, hogy teljes mértékben megfeleljünk a C++ nyelv standardjának. Alapértelmezés szerint a CMake-ben engedélyezve vannak.
if(NOT CMAKE_CXX_EXTENSIONS)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
A könyvtárunk kizárólag fejlécfájlokból áll, ezért nincs statikus vagy dinamikus könyvtárunk. Másrészt, hogy a könyvtárunkat külső forrásokból lehessen használni, telepíteni kell, hogy felfedezhető legyen a rendszerben, és csatlakoztatható legyen a projektünkhöz, emellett a megfelelő fejlécfájloknak is meg kell lenniük, valamint esetleg néhány további jellemzőnek.
Ehhez interfész könyvtárat hozunk létre.
add_library(mylib INTERFACE)A fejlécfájlokat hozzákötjük az interfész könyvtárunkhoz.
A modern, divatos, fiatalos CMake használat azt feltételezi, hogy a fejlécfájlok, jellemzők stb. egyetlen célon keresztül átadódnak. Így elég azt mondani, , és minden fejlécfájl, amely a célhoz van társítva, dependency, elérhető lesz az ehhez tartozó források számára. target. Nincs szükség semmire, [target_]include_directoriesEz alább bemutatásra kerül a feldolgozás során. .
Érdemes figyelembe venni a úgynevezett .
Ez a parancs társítja a szükséges fejlécfájlokat a könyvtárunkkal, amikor is, ha a könyvtárunkat bekapcsolják egy projekthez a CMake hierarchián belül, akkor a fejlécfájlok a következő könyvtárból fognak társulni: ${CMAKE_CURRENT_SOURCE_DIR}/include, és ha a könyvtárunkat telepítették a rendszerbe és egy másik projektbe hozzáadják a parancs segítségével, , akkor a fejlécfájlok a következő könyvtárból fognak társulni: include a telepítési könyvtárhoz viszonyítva.
target_include_directories(mylib INTERFACE
$
$
)Állítsuk be a nyelv szabványát. Nyilvánvalóan a legfrissebb. Ugyanakkor nemcsak a szabványt aktiváljuk, hanem kiterjesztjük azt arra, aki használni fogja a könyvtárunkat. Ezt a beállított tulajdonság kategóriájának köszönhetően valósítjuk meg. INTERFACE (lásd ).
target_compile_features(mylib INTERFACE cxx_std_17)Készítünk egy álnév a könyvtárunk számára. A szebb megjelenés érdekében egy külön "névtérbe" fog kerülni. Ez hasznos lesz, amikor a könyvtárunkban különböző modulok jelennek meg, és ezeket függetlenül csatlakoztatjuk. .
add_library(Mylib::mylib ALIAS mylib)
A fejléceink telepítése a rendszerbe. Itt minden egyszerű. Megmondjuk, hogy a fejlécfájlokat tartalmazó mappának a következő könyvtárba kell kerülnie: include a telepítési helyhez viszonyítva.
install(DIRECTORY include/mylib DESTINATION include)Ezután tájékoztatjuk a build rendszert arról, hogy szeretnénk, ha harmadik projektekben a következő parancsot használnák: find_package(Mylib) és megkapni a célt: Mylib::mylib.
install(TARGETS mylib EXPORT MylibConfig)
install(EXPORT MylibConfig NAMESPACE Mylib:: DESTINATION share/Mylib/cmake)A következő varázslatot úgy kell érteni, hogy amikor egy külső projektnél meghívjuk a parancsot: find_package(Mylib 1.2.3 REQUIRED),és ekkor a telepített könyvtár valós verziója nem lesz kompatibilis a verzióval, 1.2.3, a CMake automatikusan hibát generál. Tehát nem kell kézzel figyelni a verziókra.
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)
Ha a teszteket kifejezetten letiltják a megfelelő opció segítségével. vagy a projektünk egy alprojekt, ami azt jelenti, hogy egy másik CMake-projektbe van csatlakoztatva a parancs segítségével , nem lépünk tovább a hierarchiában, és a szkript, amelyben a teszteléshez szükséges parancsok szerepelnek, egyszerűen nem fut le.
if(NOT MYLIB_TESTING)
message(STATUS "A Mylib projekt tesztelése ki van kapcsolva")
elseif(IS_SUBPROJECT)
message(STATUS "A Mylib nem tesztelhető almodulként")
else()
add_subdirectory(test)
endif()
A dokumentáció sem fog generálódni alprojektek esetén.
if(NOT IS_SUBPROJECT)
add_subdirectory(doc)
endif()
Hasonlóképpen, az alprojekt számára online homokozó sem lesz.
if(NOT IS_SUBPROJECT)
add_subdirectory(online)
endif()
Először megtaláljuk a szükséges tesztkeretrendszer csomagot (cserélje le a saját kedvencére).
find_package(doctest 2.3.3 REQUIRED)Létrehozzuk az executable fájlunkat a tesztekkel. Általában közvetlenül az executable binárisba csak azt a fájlt adom hozzá, amely tartalmazza a funkciót main.
add_executable(mylib-unit-tests test_main.cpp)De a fájlokat, amelyekben maguk a tesztek vannak leírva, később adom hozzá. De ez nem kötelező.
target_sources(mylib-unit-tests PRIVATE mylib/myfeature.cpp)Csatlakoztatjuk a függőségeket. Vegye figyelembe, hogy a binárisunkhoz csak a szükséges CMake-objektumokat köthetjük, és nem hívtuk meg a parancsot target_include_directories. A teszt keretrendszerből és a mi Mylib::mylib, valamint a build paraméterek (a mi esetünkben ez a C++ nyelv szabványossága) együtt jártak ezekkel a célokkal.
target_link_libraries(mylib-unit-tests
PRIVATE
Mylib::mylib
doctest::doctest
)Végül létrehozunk egy fiktív célt, amelynek a 'build' célja egyenértékű a tesztek futásával, és ezt a célt hozzáadjuk az alapértelmezett buildhez (ezt az attribútum ALL). Ez azt jelenti, hogy az alapértelmezett build indítja a tesztek futását, tehát soha nem feledkezünk meg róluk.
add_custom_target(check ALL COMMAND mylib-unit-tests)
Ezután bekapcsoljuk a kódlefedettség mérését, ha a megfelelő opciót megadták. A részletekbe nem mélyednék bele, mert ezek inkább a lefedettségmérő eszközhöz tartoznak, mint a CMake-hez. Csak azt fontos megjegyezni, hogy az eredmények alapján létrejön egy cél , amely segítségével kényelmesen indítható a lefedettség mérése.
find_program(GCOVR_EXECUTABLE gcovr)
if(MYLIB_COVERAGE AND GCOVR_EXECUTABLE)
message(STATUS "A tesztelési kódlefedettség mérése engedélyezve van")
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 "A tesztelési kódlefedettség méréséhez szükséges a gcovr program")
endif()
.
find_package(Doxygen)Ezután ellenőrizzük, hogy a felhasználó beállította-e a nyelvi változót. Ha igen, akkor nem nyúlunk hozzá, ha nem, akkor oroszt veszünk. Ezután konfiguráljuk a Doxygen rendszerfájlokat. Minden szükséges változó, beleértve a nyelvet is, bekerül a konfigurálás folyamán (lásd a ).
Ezután létrehozunk egy célt , amely majd elindítja a dokumentáció generálását. Mivel a dokumentáció generálása nem a legfontosabb feladat a fejlesztés során, alapértelmezetten a cél nincs engedélyezve, azt kifejezetten el kell indítani.
if (Doxygen_FOUND)
if (NOT MYLIB_DOXYGEN_LANGUAGE)
set(MYLIB_DOXYGEN_LANGUAGE Orosz)
endif()
message(STATUS "A Doxygen dokumentáció ${MYLIB_DOXYGEN_LANGUAGE} nyelven generálva lesz")
configure_file(Doxyfile.in Doxyfile)
add_custom_target(doc COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}\/Doxyfile)
endif ()
Itt a harmadik Python-t keressük meg és létrehozunk egy célt , amely generálja az API szolgáltatásnak megfelelő kérdést , és elküldi azt. Válaszul egy linket kapunk a kész homokozóra.
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 "Az online homokozó létrehozásához szükséges a Python 3 interpreter")
endif()
Most nézzük meg, hogyan lehet mindezt használni.
A projekt építése, mint bármely más CMake alapú projekt, két lépésből áll:
cmake -S út\/a\/forrásokhoz -B út\/a\/build\/könyvtárhoz [opciók ...]Ha a fenti parancs nem működik a régi CMake verzió miatt, próbálja meg elhagyni a
-S:cmake út\/a\/forrásokhoz -B út\/a\/build\/könyvtárhoz [opciók ...]
.
cmake --build út\/a\/build\/könyvtárhoz [--target cél].
cmake -S ... -B ... -DMYLIB_COVERAGE=ON [egyéb opciók ...]Engedélyez egy célt , amellyel lehetőséged van elindítani a kód tesztlefedettségi mérését.
cmake -S ... -B ... -DMYLIB_TESTING=OFF [egyéb opciók ...]Lehetőséget ad a moduláris tesztek és célzás kikapcsolására. . Ennek következtében a kód tesztlefedettségi mérése le lesz állítva (lásd ).
A tesztelés automatikusan le lesz tiltva abban az esetben, ha a projektet másik projekt alprojekteként kapcsolják be a parancs segítségével .
cmake -S ... -B ... -DMYLIB_DOXYGEN_LANGUAGE=English [egyéb opciók ...]Átállítja a cél által generált dokumentáció nyelvét a megadottra. A rendelkezésre álló nyelvek listáját itt találod .
Alapértelmezés szerint orosz nyelvű.
cmake --build path/to/build/directory
cmake --build path/to/build/directory --target allHa a cél nincs megadva (ami egyenértékű a céllal mind), mindent összeszerel, amit lehet, és végrehajtja a célt. .
cmake --build path/to/build/directory --target mylib-unit-testsÖsszeállítja a moduláris teszteket. Alapértelmezés szerint engedélyezve van.
cmake --build path/to/build/directory --target checkFuttatja az összegyűjtött (összeszereli, ha még nem tette) moduláris teszteket. Alapértelmezés szerint engedélyezve van.
Lásd még .
cmake --build path/to/build/directory --target coverageElemzi a végrehajtott (futtatja, ha még nem tette) moduláris teszteket a kód tesztlefedettségére a .
A lefedettség kimenete nagyjából így fog kinézni:
------------------------------------------------------------------------------
GCC Kód Lefedettségi Jelentés
Könyvtár: /path/to/cmakecpptemplate/include/
------------------------------------------------------------------------------
Fájl Sorok Exec Lefedettség Hiányzó
------------------------------------------------------------------------------
mylib/myfeature.hpp 2 2 100%
------------------------------------------------------------------------------
ÖSSZESEN 2 2 100%
------------------------------------------------------------------------------A cél csak akkor érhető el, ha a lehetőség be van kapcsolva. .
Lásd még .
cmake --build path/to/build/directory --target docElindítja a kód dokumentációjának generálását a rendszer segítségével. .
cmake --build path/to/build/directory --target wandboxA szolgáltatás válasza nagyjából így néz ki:
{
"permlink" : "QElvxuMzHgL9fqci",
"status" : "0",
"url" : "https://wandbox.org/permlink/QElvxuMzHgL9fqci"
}Ehhez a szolgáltatást használják. . Nem tudom, mennyire rugalmasak a szervereik, de azt hiszem, nem érdemes visszaélni ezzel a lehetőséggel.
A projekt összeállítása hibakeresési módban a lefedettség mérésével.
cmake -S path/to/source -B path/to/build/directory -DCMAKE_BUILD_TYPE=Debug -DMYLIB_COVERAGE=ON
cmake --build path/to/build/directory --target coverage --parallel 16Projekt telepítése előzetes build és tesztelés nélkül
cmake -S út/k/színtárgyak -B út/k/build/mappa -DMYLIB_TESTING=OFF -DCMAKE_INSTALL_PREFIX=út/k/telepítési/mappa
cmake --build út/k/build/mappa --target installBuild egy kiadási módban megadott fordítóval
cmake -S út/k/színtárgyak -B út/k/build/mappa -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_PREFIX_PATH=út/k/mappába/ahol/a/függőségek/telepítve/vannak
cmake --build út/k/build/mappa --parallel 4Dokumentáció generálása angol nyelven
cmake -S út/k/színtárgyak -B út/k/build/mappa -DCMAKE_BUILD_TYPE=Release -DMYLIB_DOXYGEN_LANGUAGE=English
cmake --build út/k/build/mappa --target doc
3.13
Valójában a CMake 3.13 csak néhány konzolparancs futtatásához szükséges, amelyeket a jelen útmutatóban írunk le. A CMake-skript szintaxisához elegendő a 3.8-as verzió, ha a generálást más módon hívjuk meg.
Tesztelő könyvtár
A tesztelést ki lehet kapcsolni (lásd: ).
A dokumentáció nyelvének váltásához lehetőség van az alábbi opcióra .
Programozási nyelvértelmező
Automatikus generálás .
A CMake és néhány jó eszköz segítségével statikus analízist végezhetünk minimális erőfeszítéssel.
scan-build segítségével.
A CMake beépített támogatást nyújt a statikus analízis eszközhöz .
Ehhez használni kell az alábbi opciót :
cmake -S út/k/színtárgyak -B út/k/build/mappa -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_CPPCHECK="cppcheck;--enable=all;-Iút/k/színtárgyak/include"Ezt követően a statikus elemzést automatikusan végrehajtják minden alkalommal, amikor a forrást fordítják vagy újrafordítják. Semmi másra nincs szükség.
Clang
Egy csodás eszközzel szintén gyorsan elindítható a statikus analízis:
scan-build cmake -S út/k/színtárgyak -B út/k/build/mappa -DCMAKE_BUILD_TYPE=Debug
scan-build cmake --build út/k/build/mappaItt, ellentétben a Cppcheck esetével, minden alkalommal el kell indítani a build-et a scan-build.
A CMake egy nagyon erőteljes és rugalmas rendszer, amely lehetővé teszi a funkciók megvalósítását bármilyen ízlés és szín szerint. És bár néha a szintaxis nem a legjobb, valójában nem olyan ijesztő, mint amilyennek látszik. Használja a CMake build rendszert a társadalom javára és az egészség érdekében.
→
Forrás: habr.com
