În timpul dezvoltării, îmi place să schimb compilatoarele, să construiesc moduri, versiunile de dependență, să efectuez analize statice, să măsor performanța, să colectez acoperire, să generez documentație etc. Și îmi place foarte mult CMake pentru că îmi permite să fac tot ce îmi doresc.
Mulți oameni critică CMake și de multe ori pe merit, dar dacă te uiți la el, nu totul este atât de rău și recent nu-i rău deloc, iar direcția de dezvoltare este destul de pozitivă.
În această notă, vreau să vă spun cum să organizați pur și simplu o bibliotecă de antet în C++ în sistemul CMake pentru a obține următoarea funcționalitate:
Vom vorbi în principal despre cum să organizați scripturile CMake, așa că vor fi discutate în detaliu. Oricine poate vizualiza restul fișierelor direct pe pagina de proiect șablon.
În primul rând, trebuie să solicitați versiunea necesară a sistemului CMake. CMake evoluează, semnăturile de comandă și comportamentul în diferite condiții se schimbă. Pentru ca CMake să înțeleagă imediat ce dorim de la el, trebuie să ne înregistrăm imediat cerințele pentru acesta.
cmake_minimum_required(VERSION 3.13)
Apoi vom desemna proiectul nostru, numele, versiunea, limbile folosite etc. (vezi. команду project).
În acest caz indicăm limba CXX (și asta înseamnă C++), astfel încât CMake să nu se încordeze și să caute un compilator de limbaj C (în mod implicit, CMake include două limbaje: C și C++).
project(Mylib VERSION 1.0 LANGUAGES CXX)
Aici puteți verifica imediat dacă proiectul nostru este inclus într-un alt proiect ca subproiect. Acest lucru va ajuta foarte mult în viitor.
Prima opțiune este MYLIB_TESTING — pentru a dezactiva testele unitare. Acest lucru poate fi necesar dacă suntem siguri că totul este în ordine cu testele, dar vrem doar, de exemplu, să ne instalăm sau să împachetăm proiectul. Sau proiectul nostru este inclus ca subproiect - în acest caz, utilizatorul proiectului nostru nu este interesat să ruleze testele noastre. Nu testezi dependențele pe care le folosești, nu-i așa?
În plus, vom face o opțiune separată MYLIB_COVERAGE pentru măsurarea acoperirii codului prin teste, dar va necesita instrumente suplimentare, deci va trebui să fie activat în mod explicit.
Desigur, suntem programatori cool plus, așa că dorim nivelul maxim de diagnosticare în timp de compilare de la compilator. Nici un singur mouse nu va strecura.
Biblioteca noastră constă numai din fișiere antet, ceea ce înseamnă că nu avem nicio epuizare sub formă de biblioteci statice sau dinamice. Pe de altă parte, pentru a putea folosi biblioteca noastră extern, aceasta trebuie să fie instalată, să fie detectabilă în sistem și conectată la proiectul dvs. și, în același timp, aceleași anteturi, precum și eventual unele suplimentare, sunt atașate proprietăților acestuia.
În acest scop, creăm o bibliotecă de interfață.
add_library(mylib INTERFACE)
Legăm antetele la biblioteca noastră de interfețe.
Utilizarea modernă, la modă, pentru tineri a CMake implică faptul că anteturile, proprietățile etc. transmise printr-o singură țintă. Așa că este suficient să spun target_link_libraries(target PRIVATE dependency), și toate anteturile care sunt asociate cu ținta dependency, va fi disponibil pentru sursele aparținând țintei target. Și nu aveți nevoie de niciunul [target_]include_directories. Acest lucru va fi demonstrat mai jos în analiză Scriptul CMake pentru testele unitare.
Această comandă asociază anteturile de care avem nevoie cu biblioteca noastră de interfață, iar dacă biblioteca noastră este conectată la orice țintă din aceeași ierarhie CMake, atunci anteturile din director vor fi asociate cu aceasta. ${CMAKE_CURRENT_SOURCE_DIR}/include, și dacă biblioteca noastră este instalată pe sistem și conectată la alt proiect folosind comanda find_package, atunci anteturile din director vor fi asociate cu acesta include raportat la directorul de instalare.
Să stabilim un standard de limbă. Desigur, chiar ultimul. În același timp, nu doar includem standardul, ci îl extindem și la cei care vor folosi biblioteca noastră. Acest lucru se realizează datorită faptului că proprietatea set are o categorie INTERFACE (A se vedea. comanda target_compile_features).
Să creăm un alias pentru biblioteca noastră. Mai mult, pentru frumusețe, va fi într-un „namespace” special. Acest lucru va fi util atunci când în biblioteca noastră apar diferite module și mergem să le conectăm independent unul de celălalt. Ca la Busta, de exemplu.
Instalarea anteturilor noastre în sistem. Totul este simplu aici. Spunem că folderul cu toate anteturile ar trebui să intre în director include raportat la locul de instalare.
Următoarea vrajă ar trebui înțeleasă astfel. Când într-un proiect terță parte, apelăm comanda find_package(Mylib 1.2.3 REQUIRED), iar versiunea reală a bibliotecii instalate va fi incompatibilă cu versiunea 1.2.3CMake va genera automat o eroare. Adică, nu va trebui să urmăriți manual versiunile.
Dacă testele sunt dezactivate explicit folosind opțiunea corespunzătoare sau proiectul nostru este un subproiect, adică este conectat la un alt proiect CMake folosind comanda add_subdirectory, nu ne deplasăm mai departe de-a lungul ierarhiei, iar scriptul, care descrie comenzile pentru generarea și rularea testelor, pur și simplu nu rulează.
if(NOT MYLIB_TESTING)
message(STATUS "Тестирование проекта Mylib выключено")
elseif(IS_SUBPROJECT)
message(STATUS "Mylib не тестируется в режиме подмодуля")
else()
add_subdirectory(test)
endif()
Conectăm dependențe. Vă rugăm să rețineți că am legat doar țintele CMake de care aveam nevoie la binarul nostru și nu am apelat comanda target_include_directories. Titluri din cadrul de testare și din al nostru Mylib::mylib, precum și parametrii de construcție (în cazul nostru, acesta este standardul limbajului C++) au venit împreună cu aceste obiective.
În cele din urmă, creăm o țintă inactivă, a cărei „build” este echivalentă cu rularea testelor și adăugăm această țintă la versiunea implicită (atributul este responsabil pentru acest lucru ALL). Aceasta înseamnă că versiunea implicită declanșează rularea testelor, ceea ce înseamnă că nu vom uita niciodată să le rulăm.
add_custom_target(check ALL COMMAND mylib-unit-tests)
Apoi, activăm măsurarea acoperirii codului dacă este specificată opțiunea corespunzătoare. Nu voi intra în detalii, pentru că se referă mai mult la un instrument de măsurare a acoperirii decât la CMake. Este important doar să rețineți că pe baza rezultatelor se va crea un obiectiv coverage, cu care este convenabil să începeți măsurarea acoperirii.
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()
Apoi, verificăm dacă utilizatorul a setat variabila de limbă. Dacă da, atunci nu o atingem, dacă nu, atunci luăm limba rusă. Apoi configurăm fișierele de sistem Doxygen. Toate variabilele necesare, inclusiv limba, merg acolo în timpul procesului de configurare (vezi. команду configure_file).
Apoi ne creăm un obiectiv doc, care va începe să genereze documentația. Deoarece generarea documentației nu este cea mai mare nevoie în procesul de dezvoltare, ținta nu va fi activată implicit; va trebui să fie lansată în mod explicit.
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 ()
Aici găsim al treilea Python și creăm o țintă wandbox, care generează o solicitare corespunzătoare API-ului serviciului Cutie cu baghete, și îl trimite departe. Răspunsul vine cu un link către cutia de nisip finalizată.
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()
Oferă posibilitatea de a dezactiva construirea și țintirea testului unitar check. Ca urmare, măsurarea acoperirii codului prin teste este dezactivată (vezi. MYLIB_COVERAGE).
Testarea este, de asemenea, dezactivată automat dacă proiectul este conectat la un alt proiect ca subproiect folosind comanda add_subdirectory.
Schimbă limba documentației pe care o generează ținta doc la cel dat. Pentru o listă a limbilor disponibile, consultați Site-ul web al sistemului Doxygen.
Serviciul este folosit pentru aceasta Cutie cu baghete. Nu știu cât de flexibile sunt serverele lor, dar cred că această oportunitate nu trebuie abuzată.
De fapt, versiunea 3.13 de CMake este necesară doar pentru a rula unele dintre comenzile consolei descrise în acest ajutor. Din punctul de vedere al sintaxei scripturilor CMake, versiunea 3.8 este suficientă dacă generarea este numită în alte moduri.
După aceasta, analiza statică va fi lansată automat de fiecare dată când sursa este compilată și recompilată. Nu este nevoie să faceți nimic suplimentar.
Zăngăni
Cu ajutorul unui instrument minunat scan-build De asemenea, puteți rula analize statice în cel mai scurt timp:
CMake este un sistem foarte puternic și flexibil care vă permite să implementați funcționalități pentru fiecare gust și culoare. Și, deși sintaxa lasă uneori mult de dorit, diavolul încă nu este atât de groaznic pe cât este pictat. Utilizați sistemul de construcție CMake în beneficiul societății și al sănătății.