Während der Entwicklung ändere ich gerne Compiler, Build-Modi und Abhängigkeitsversionen, führe statische Analysen durch, messe die Leistung, sammle Abdeckung, erstelle Dokumentation und so weiter. Und ich liebe CMake wirklich, weil ich damit tun kann, was ich will.
Viele schimpfen über CMake, und das oft zu Recht, aber wenn man hinschaut, ist es nicht so schlimm, aber in letzter Zeit gar nicht so schlecht, und die Richtung der Entwicklung ist durchaus positiv.
In diesem Hinweis möchte ich Ihnen erklären, wie einfach es ist, eine C++-Header-Bibliothek in einem CMake-System zu organisieren, um die folgende Funktionalität zu erhalten:
Montage;
Autorun-Tests;
Messung der Codeabdeckung;
Installation;
Autodokumentation;
Online-Sandbox-Generierung;
Statische Analyse.
Wer die Vor- und Nachteile bereits versteht, kann es einfach tun Projektvorlage herunterladen und fangen Sie an, es zu verwenden.
Dabei geht es vor allem um die Organisation von CMake-Skripten, daher werden diese im Detail analysiert. Die restlichen Dateien können von jedem direkt eingesehen werden. auf der Seite des Vorlagenprojekts.
Zunächst müssen Sie die gewünschte Version des CMake-Systems anfordern. CMake entwickelt sich, Befehlssignaturen ändern sich, Verhalten unter verschiedenen Bedingungen. Damit CMake sofort versteht, was wir davon wollen, müssen wir unsere Anforderungen sofort festlegen.
cmake_minimum_required(VERSION 3.13)
Dann bezeichnen wir unser Projekt, seinen Namen, seine Version, die verwendeten Sprachen usw. команду project).
Geben Sie in diesem Fall die Sprache an CXX (was C++ bedeutet), damit sich CMake nicht die Mühe macht, nach einem C-Compiler zu suchen (standardmäßig sind in CMake zwei Sprachen enthalten: C und C++).
project(Mylib VERSION 1.0 LANGUAGES CXX)
Hier können Sie sofort prüfen, ob unser Projekt als Teilprojekt in ein anderes Projekt eingebunden ist. Das wird in Zukunft sehr helfen.
Die erste Option ist MYLIB_TESTING - um Unit-Tests zu deaktivieren. Dies kann erforderlich sein, wenn wir sicher sind, dass bei den Tests alles in Ordnung ist, und wir beispielsweise unser Projekt nur installieren oder verpacken möchten. Oder unser Projekt wird als Unterprojekt eingebunden – in diesem Fall ist der Benutzer unseres Projekts nicht daran interessiert, unsere Tests durchzuführen. Sie testen die Abhängigkeiten, die Sie verwenden, nicht, oder?
Darüber hinaus werden wir eine separate Option erstellen MYLIB_COVERAGE zum Messen der Codeabdeckung mit Tests, es sind jedoch zusätzliche Tools erforderlich, sodass Sie es explizit aktivieren müssen.
Unsere Bibliothek besteht nur aus Header-Dateien, das heißt, wir haben keine Ausgabe in Form von statischen oder dynamischen Bibliotheken. Um unsere Bibliothek von außen nutzen zu können, müssen Sie sie hingegen installieren, im System finden und mit Ihrem Projekt verbinden können, und gleichzeitig auch diese Header sowie möglicherweise einige zusätzliche Eigenschaften.
Zu diesem Zweck erstellen wir eine Schnittstellenbibliothek.
add_library(mylib INTERFACE)
Wir binden die Header an unsere Schnittstellenbibliothek.
Der moderne, trendige und jugendliche Einsatz von CMake bedeutet, dass Header, Eigenschaften usw. über ein einzelnes Ziel übertragen. Es reicht also zu sagen target_link_libraries(target PRIVATE dependency)und alle Header, die dem Ziel zugeordnet sind dependency, wird für Quellen verfügbar sein, die zum Ziel gehören target. Und du brauchst keine [target_]include_directories. Dies wird im Folgenden beim Parsen demonstriert CMake-Skript für Unit-Tests.
Dieser Befehl verknüpft die von uns benötigten Header mit unserer Schnittstellenbibliothek. Wenn unsere Bibliothek mit einem beliebigen Ziel innerhalb derselben CMake-Hierarchie verbunden ist, werden ihr die Header aus dem Verzeichnis zugeordnet ${CMAKE_CURRENT_SOURCE_DIR}/include, und wenn unsere Bibliothek auf dem System installiert und über den Befehl mit einem anderen Projekt verbunden ist find_package, dann werden ihm die Header aus dem Verzeichnis zugeordnet include relativ zum Installationsverzeichnis.
Legen Sie den Sprachstandard fest. Natürlich das Neueste. Gleichzeitig nehmen wir den Standard nicht nur auf, sondern verteilen ihn auch an diejenigen, die unsere Bibliothek nutzen werden. Dies wird dadurch erreicht, dass die festgelegte Eigenschaft eine Kategorie hat INTERFACE (Siehe. target_compile_features-Befehl).
Wir bekommen einen Alias für unsere Bibliothek. Und der Schönheit halber wird es in einem speziellen „Namensraum“ sein. Dies ist nützlich, wenn in unserer Bibliothek verschiedene Module erscheinen und wir sie unabhängig voneinander verbinden. Wie zum Beispiel in Busta.
Installieren unserer Header im System. Hier ist alles einfach. Wir sagen, dass der Ordner mit allen Headern in das Verzeichnis fallen sollte include bezüglich des Installationsortes.
Als nächstes teilen wir dem Build-System mit, dass wir den Befehl in Drittprojekten aufrufen können möchten find_package(Mylib) und ein Ziel finden Mylib::mylib.
So ist die folgende Beschwörung zu verstehen. In einem Nebenprojekt rufen wir den Befehl auf find_package(Mylib 1.2.3 REQUIRED), und gleichzeitig ist die echte Version der installierten Bibliothek mit der Version nicht kompatibel 1.2.3, CMake generiert automatisch einen Fehler. Das heißt, Sie müssen die Versionen nicht manuell nachverfolgen.
Wenn Tests explizit mit deaktiviert werden entsprechende Option oder unser Projekt ist ein Unterprojekt, das heißt, es ist über den Befehl mit einem anderen CMake-Projekt verbunden add_subdirectory, gehen wir in der Hierarchie nicht weiter nach unten und das Skript, das die Befehle zum Generieren und Ausführen von Tests beschreibt, startet einfach nicht.
if(NOT MYLIB_TESTING)
message(STATUS "Тестирование проекта Mylib выключено")
elseif(IS_SUBPROJECT)
message(STATUS "Mylib не тестируется в режиме подмодуля")
else()
add_subdirectory(test)
endif()
Zunächst finden wir ein Paket mit dem gewünschten Test-Framework (ersetzen Sie es durch Ihr Lieblings-Framework).
find_package(doctest 2.3.3 REQUIRED)
Wir erstellen unsere ausführbare Datei mit Tests. Normalerweise füge ich direkt zur ausführbaren Binärdatei nur die Datei hinzu, in der sich die Funktion befindet main.
add_executable(mylib-unit-tests test_main.cpp)
Und die Dateien, in denen die Tests selbst beschrieben sind, füge ich später hinzu. Dies ist jedoch nicht erforderlich.
Wir verbinden Abhängigkeiten. Bitte beachten Sie, dass wir nur die benötigten CMake-Ziele an unsere Binärdatei angehängt und den Befehl nicht aufgerufen haben target_include_directories. Header aus dem Testframework und von unserem Mylib::mylibsowie Build-Optionen (in unserem Fall der C++-Sprachstandard) wurden zusammen mit diesen Zielen gecrawlt.
Schließlich erstellen wir ein Dummy-Ziel, dessen „Build“ dem Ausführen von Tests entspricht, und fügen dieses Ziel dem Standard-Build (dem Attribut) hinzu ALL). Das bedeutet, dass der Standard-Build die Ausführung der Tests auslöst, sodass wir nie vergessen werden, sie auszuführen.
add_custom_target(check ALL COMMAND mylib-unit-tests)
Aktivieren Sie als Nächstes die Messung der Codeabdeckung, sofern die entsprechende Option aktiviert ist. Ich werde nicht auf die Details eingehen, da sie eher mit dem Coverage-Messtool als mit CMake zusammenhängen. Wichtig ist nur zu beachten, dass aus den Ergebnissen ein Ziel entsteht coverage, mit dem Sie bequem mit der Messung der Abdeckung beginnen können.
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()
Als nächstes prüfen wir, ob die Variable mit der Sprache vom Benutzer festgelegt wurde. Wenn ja, dann rühren wir es nicht an, wenn nicht, dann nehmen wir Russisch. Anschließend konfigurieren wir die Doxygen-Systemdateien. Alle notwendigen Variablen, einschließlich der Sprache, gelangen während des Konfigurationsprozesses dorthin (siehe. команду configure_file).
Dann schaffen wir ein Ziel doc, wodurch mit der Erstellung der Dokumentation begonnen wird. Da die Erstellung der Dokumentation nicht den größten Bedarf im Entwicklungsprozess darstellt, wird das Ziel nicht standardmäßig einbezogen, sondern muss explizit ausgeführt werden.
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 ()
Hier finden wir den dritten Python und erstellen ein Ziel wandbox, wodurch eine der Dienst-API entsprechende Anfrage generiert wird Zauberstabbox, und sendet es. Als Antwort kommt ein Link zur fertigen Sandbox.
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()
Bietet die Option, Unit-Test-Build und -Ziel zu deaktivieren check. Dadurch wird die Messung der Codeabdeckung durch Tests ausgeschaltet (siehe MYLIB_COVERAGE).
Außerdem wird das Testen automatisch deaktiviert, wenn das Projekt über den Befehl als Unterprojekt mit einem anderen Projekt verbunden wird add_subdirectory.
Schaltet die Sprache der Dokumentation um, die das Ziel generiert doc zum Gegebenen. Eine Liste der verfügbaren Sprachen finden Sie unter Doxygen-Website.
Hierzu wird der Dienst genutzt. Zauberstabbox. Ich weiß nicht, welche Rubber-Server sie haben, aber ich denke, dass man diese Funktion nicht missbrauchen sollte.
Tatsächlich ist CMake Version 3.13 nur erforderlich, um einige der in dieser Hilfe beschriebenen Konsolenbefehle auszuführen. Aus Sicht der Syntax von CMake-Skripten ist Version 3.8 ausreichend, wenn die Generierung auf andere Weise aufgerufen wird.
Danach wird die statische Analyse jedes Mal automatisch gestartet, wenn die Quellen kompiliert und neu kompiliert werden. Es muss nichts extra getan werden.
Clang
Mit einem wunderbaren Werkzeug scan-build Sie können auch im Handumdrehen eine statische Analyse durchführen:
CMake ist ein sehr leistungsstarkes und flexibles System, mit dem Sie Funktionen für jeden Geschmack und jede Farbe implementieren können. Und obwohl die Syntax manchmal zu wünschen übrig lässt, ist der Teufel immer noch nicht so schrecklich, wie er dargestellt wird. Nutzen Sie das CMake-Build-System zum Wohle der Gesellschaft und Gesundheit.