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

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

Nella parte precedente di questo affascinante racconto si parlava dell'organizzazione di una libreria di intestazione nel contesto del generatore di sistemi di compilazione CMake.

Questa volta aggiungeremo una libreria compilabile e discuteremo dell'organizzazione dei moduli tra loro.

Come sempre, quelli che non possono attendere possono subito passare al repository aggiornato e mettere tutto alla prova con le proprie mani.


Contenuto

  1. Dividi
  2. Domina

Dividi

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

Nella prima parte è stato descritto un blocco standard: un progetto con una libreria di intestazione. Ora aggiungeremo al nostro progetto una libreria compilabile.

Per questo pero, estrarremo 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 
+
+namespace mylib
+{
+    bool myfunc (mystruct)
+    {
+        return true;
+    }
+}

Poi definiremo la libreria compilabile (myfeature), che consisterà nel file appena ottenuto. .cppOvviamente, alla nuova libreria servono già le intestazioni esistenti, e per garantirlo è possibile e necessario collegarla con l'obiettivo esistente mylib. Il collegamento tra di loro sarà pubblico, il che significa che tutto a cui sarà collegato l'obiettivo myfeature, riceverà automaticamente anche l'obiettivo mylib (per maggiori informazioni sui metodi di collegamento).

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)

+###################################################################################################
+##
+##      Libreria Compilabile
+##
+###################################################################################################
+
+add_library(myfeature src/mylib/myfeature.cpp)
+target_link_libraries(myfeature PUBLIC mylib)
+
+add_library(Mylib::myfeature ALIAS myfeature)
+

Successivamente faremo sì che la nuova libreria venga installata nel 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)

È importante notare che per l'obiettivo myfeature, come per mylib è stato creato uno pseudonimo con il prefisso Mylib::. La stessa cosa è stata specificata per entrambi gli obiettivi durante l'esportazione per l'installazione nel sistema. Questo consente un lavoro uniforme con gli obiettivi in qualsiasi schema di collegamento.

Dopo di che, è rimasto da legare i test modulari con la nuova libreria (la funzione myfunc è stata estratta dall'intestazione, quindi ora è necessario collegarsi):

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 è più necessario collegarle separatamente, poiché, come già detto, si collegano automaticamente insieme alla libreria (Mylib::myfeature).

E aggiungeremo un paio di dettagli per garantire le misurazioni della copertura tenendo conto della nuova libreria introdotta:

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

Si possono aggiungere più librerie, file eseguibili e così via. Non importa come sono collegati tra loro nel progetto. L'importante è solo quali obiettivi rappresentano l'interfaccia del nostro modulo, cioè quelli accessibili esternamente.

Domina

Ora abbiamo moduli standard e possiamo gestirli: costruire da essi strutture di qualsiasi complessità, installandoli nel sistema o collegandoli tra loro all'interno di un unico sistema di build.

Installazione nel sistema

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

cmake --build percorso/alla/directory/di/build --target install

Dopo di ciò, può essere collegato in qualsiasi altro progetto con il comando find_package.

find_package(Mylib 1.0 REQUIRED)

Collegamento come sottomodulo

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

Utilizzo

I metodi di collegamento sono diversi, ma il risultato è lo stesso. In entrambi i casi, nel progetto che utilizza il nostro modulo, saranno disponibili gli obiettivi Mylib::myfeature e Mylib::mylib, che possono essere utilizzati, ad esempio, in questo modo:

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

Nel nostro caso specifico, la libreria Mylib::myfeature deve essere inclusa quando è necessario collegarsi con la libreria libmyfeature. Se sono sufficienti solo gli header, allora si può tornare a utilizzare la libreria Mylib::mylib.

Gli obiettivi di CMake possono essere astuti, ad esempio, progettati solo per passare alcune proprietà, dipendenze, ecc. In questo modo, il lavoro con essi avviene in modo uniforme.

Questo è ciò che si voleva ottenere.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster