C++ und CMake – Brüder für immer, Teil II

C++ und CMake – Brüder für immer, Teil II

Im vorigen Teil In dieser unterhaltsamen Geschichte ging es um die Organisation einer Header-Bibliothek innerhalb des CMake-Build-Systemgenerators.

Dieses Mal werden wir eine kompilierte Bibliothek hinzufügen und auch über die Verknüpfung von Modulen untereinander sprechen.

Wer ungeduldig ist, kann es nach wie vor sofort Gehen Sie zum aktualisierten Repository und berühre alles mit deinen eigenen Händen.


Inhalt

  1. Teilen
  2. Erobern

Teilen

Um unser hohes Ziel zu erreichen, müssen wir zunächst die von uns entwickelte Software in universelle, isolierte Blöcke unterteilen, die aus Benutzersicht einheitlich sind.

Im ersten Teil wurde ein solcher Standardbaustein beschrieben – ein Projekt mit einer Header-Bibliothek. Fügen wir nun unserem Projekt eine kompilierte Bibliothek hinzu.

Nehmen wir dazu die Implementierung der Funktion heraus myfunc in einem separaten .cpp-Datei:

diff --git a/include/mylib/myfeature.hpp b/include/mylib/myfeature.hpp
index 43db388..ba62b4f 100644
--- a/include/mylib/myfeature.hpp
+++ b/include/mylib/myfeature.hpp
@@ -46,8 +46,5 @@ namespace mylib

         ~  see mystruct
      */
-    inline bool myfunc (mystruct)
-    {
-        return true;
-    }
+    bool myfunc (mystruct);
 }
diff --git a/src/mylib/myfeature.cpp b/src/mylib/myfeature.cpp
new file mode 100644
index 0000000..abb5004
--- /dev/null
+++ b/src/mylib/myfeature.cpp
@@ -0,0 +1,9 @@
+#include <mylib/myfeature.hpp>
+
+namespace mylib
+{
+    bool myfunc (mystruct)
+    {
+        return true;
+    }
+}

Dann definieren wir die zu kompilierende Bibliothek (myfeature), die aus dem besteht, was im vorherigen Schritt erhalten wurde .cpp-Datei. Die neue Bibliothek erfordert natürlich vorhandene Header, und um diese bereitzustellen, kann und sollte sie an den bestehenden Zweck gebunden werden mylib. Darüber hinaus ist die Verbindung zwischen ihnen öffentlich, was bedeutet, dass alles, mit dem das Ziel verbunden ist, verbunden wird myfeature, erhält automatisch die Last und das Ziel mylib (mehr über Strickmethoden).

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 108045c..0de77b8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -64,6 +64,17 @@ target_compile_features(mylib INTERFACE cxx_std_17)

 add_library(Mylib::mylib ALIAS mylib)

+###################################################################################################
+##
+##      Компилируемая библиотека
+##
+###################################################################################################
+
+add_library(myfeature src/mylib/myfeature.cpp)
+target_link_libraries(myfeature PUBLIC mylib)
+
+add_library(Mylib::myfeature ALIAS myfeature)
+

Als nächstes stellen wir sicher, dass die neue Bibliothek auch auf dem System installiert ist:

@@ -72,7 +83,7 @@ add_library(Mylib::mylib ALIAS mylib)

 install(DIRECTORY include/mylib DESTINATION include)

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

 include(CMakePackageConfigHelpers)

Es sollte beachtet werden, dass dies der Fall ist myfeature, wie für mylib Es wurde ein Alias ​​mit Präfix erstellt Mylib::. Beim Export zur Installation auf dem System wird für beide Zwecke das Gleiche geschrieben. Dadurch ist es möglich, für jeden einheitlich mit Zielen zu arbeiten Bindungsschema.

Danach müssen nur noch Unit-Tests mit der neuen Bibliothek verknüpft werden (function myfunc aus dem Titel entfernt, also müssen Sie jetzt verlinken):

diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 5620be4..bc1266c 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -4,7 +4,7 @@ add_executable(mylib-unit-tests test_main.cpp)
 target_sources(mylib-unit-tests PRIVATE mylib/myfeature.cpp)
 target_link_libraries(mylib-unit-tests
     PRIVATE
-        Mylib::mylib
+        Mylib::myfeature
         doctest::doctest
 )

Überschriften (Mylib::mylib) müssen Sie jetzt keine separate Verbindung mehr herstellen, da diese, wie bereits erwähnt, automatisch zusammen mit der Bibliothek verbunden werden (Mylib::myfeature).

Und fügen wir noch ein paar Nuancen hinzu, um sicherzustellen, dass die Abdeckungsmessungen die neu angekommene Bibliothek berücksichtigen:

@@ -15,11 +15,16 @@ if(MYLIB_COVERAGE AND GCOVR_EXECUTABLE)
     target_compile_options(mylib-unit-tests PRIVATE --coverage)
     target_link_libraries(mylib-unit-tests PRIVATE gcov)

+    target_compile_options(myfeature PRIVATE --coverage)
+    target_link_libraries(myfeature PRIVATE gcov)
+
     add_custom_target(coverage
         COMMAND
             ${GCOVR_EXECUTABLE}
-                --root=${PROJECT_SOURCE_DIR}/include/
-                --object-directory=${CMAKE_CURRENT_BINARY_DIR}
+                --root=${PROJECT_SOURCE_DIR}/
+                --filter=${PROJECT_SOURCE_DIR}/include
+                --filter=${PROJECT_SOURCE_DIR}/src
+                --object-directory=${PROJECT_BINARY_DIR}
         DEPENDS
             check
     )

Sie können weitere Bibliotheken, ausführbare Dateien usw. hinzufügen. Dabei spielt es keine Rolle, wie genau sie innerhalb des Projekts miteinander verbunden sind. Wichtig ist nur, welche Ziele die Schnittstelle unseres Moduls sind, also hervorstechen.

Erobern

Jetzt haben wir Standard-Blockmodule, und wir können sie beherrschen: Sie können sie zu einer Struktur beliebiger Komplexität zusammensetzen, sie in ein System einbauen oder sie innerhalb eines einzigen Montagesystems miteinander verbinden.

Einbau in das System

Eine der Möglichkeiten, das Modul zu nutzen, besteht darin, unser Modul in das System zu installieren.

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

Danach wird es über den Befehl mit einem beliebigen anderen Projekt verbunden find_package.

find_package(Mylib 1.0 REQUIRED)

Anschluss als Submodul

Eine andere Möglichkeit besteht darin, den Ordner mit unserem Projekt über den Befehl als Submodul mit einem anderen Projekt zu verbinden add_subdirectory.

Verwenden

Die Bindungsmethoden sind unterschiedlich, aber das Ergebnis ist das gleiche. In beiden Fällen werden Ziele im Projekt über unser Modul verfügbar sein Mylib::myfeature и Mylib::mylib, was zum Beispiel so verwendet werden kann:

add_executable(some_executable some.cpp sources.cpp)
target_link_libraries(some_executable PRIVATE Mylib::myfeature)

Konkret in unserem Fall die Bibliothek Mylib::myfeature müssen angeschlossen werden, wenn eine Verbindung mit der Bibliothek erforderlich ist libmyfeature. Wenn genügend Header vorhanden sind, lohnt sich die Nutzung der Bibliothek Mylib::mylib.

CMake-Ziele können knifflig sein, da sie beispielsweise nur dazu gedacht sind, einige Eigenschaften, Abhängigkeiten usw. weiterzuleiten. Gleichzeitig erfolgt die Arbeit mit ihnen auf die gleiche Weise.

Das ist es, was wir brauchen.

Source: habr.com

Kommentar hinzufügen