C++ e CMake: fratelli per sempre, parte II

C++ e CMake: fratelli per sempre, parte II

Nella parte precedente Questa divertente storia parlava dell'organizzazione di una libreria di intestazioni all'interno del generatore del sistema di build CMake.

Questa volta aggiungeremo una libreria compilata e parleremo anche del collegamento dei moduli tra loro.

Come prima, chi è impaziente può farlo subito vai al repository aggiornato e tocca tutto con le tue mani.


contenuto

  1. Dividere
  2. Conquistare

Dividere

La prima cosa che dobbiamo fare per raggiungere il nostro nobile obiettivo è dividere il software che sviluppiamo in blocchi universali, isolati e uniformi dal punto di vista dell'utente.

Nella prima parte è stato descritto un blocco standard di questo tipo: un progetto con una libreria di intestazioni. Ora aggiungiamo una libreria compilata al nostro progetto.

Per fare ciò, eliminiamo l’implementazione della funzione myfunc in un separato .cpp-file:

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;
+    }
+}

Quindi definiamo la libreria da compilare (myfeature), che sarà costituito da quanto ottenuto nel passaggio precedente .cpp-file. La nuova libreria ovviamente richiede intestazioni esistenti e, per fornire ciò, può e deve essere collegata allo scopo esistente mylib. Inoltre, la connessione tra loro è pubblica, il che significa che tutto ciò a cui sarà collegato l'obiettivo myfeature, riceverà automaticamente il carico e il target mylib (di più sui metodi di lavorazione a maglia).

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

Successivamente, ci assicureremo che anche la nuova libreria sia installata sul sistema:

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

Va notato che per lo scopo myfeature, quanto a mylib è stato creato un alias con un prefisso Mylib::. Lo stesso viene scritto per entrambi gli scopi quando li si esporta per l'installazione sul sistema. Ciò consente di lavorare in modo uniforme con obiettivi per qualsiasi schema vincolante.

Fatto questo non resta che collegare gli unit test con la nuova libreria (function myfunc tolto dal titolo, quindi ora devi collegare):

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
 )

Intestazioni (Mylib::mylib) ora non è necessario collegarsi separatamente, perché, come già accennato, vengono automaticamente collegati insieme alla libreria (Mylib::myfeature).

E aggiungiamo un paio di sfumature per garantire le misurazioni della copertura tenendo conto della libreria appena arrivata:

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

Puoi aggiungere più librerie, eseguibili, ecc. Non importa quanto esattamente siano collegati tra loro all'interno del progetto. L'unica cosa importante è quali target costituiscono l'interfaccia del nostro modulo, ovvero sporgono.

Conquistare

Ora disponiamo di moduli a blocchi standard e possiamo dominarli: comporli in una struttura di qualsiasi complessità, installandoli in un sistema o collegandoli insieme all'interno di un unico sistema di assemblaggio.

Installazione nel sistema

Una delle opzioni per utilizzare il modulo è installare il nostro modulo nel sistema.

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

Successivamente, si connette a qualsiasi altro progetto utilizzando il comando find_package.

find_package(Mylib 1.0 REQUIRED)

Collegamento come sottomodulo

Un'altra opzione è connettere la cartella con il nostro progetto a un altro progetto come sottomodulo utilizzando il comando add_subdirectory.

l'uso di

I metodi di rilegatura sono diversi, ma il risultato è lo stesso. In entrambi i casi, gli obiettivi saranno disponibili nel progetto utilizzando il nostro modulo Mylib::myfeature и Mylib::mylib, che può essere utilizzato, ad esempio, in questo modo:

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

Nello specifico nel nostro caso, la biblioteca Mylib::myfeature devono essere collegati quando è necessario collegarsi con la libreria libmyfeature. Se ci sono abbastanza intestazioni, vale la pena utilizzare la libreria Mylib::mylib.

Le destinazioni CMake possono essere complicate, ad esempio, intese solo per inoltrare alcune proprietà, dipendenze, ecc. Allo stesso tempo, lavorare con loro avviene allo stesso modo.

Questo è ciò che dovevamo ottenere.

Fonte: habr.com

Aggiungi un commento