C++ y CMake: hermanos para siempre, parte II

C++ y CMake: hermanos para siempre, parte II

En la parte anterior Esta entretenida historia hablaba sobre la organización de una biblioteca de encabezados dentro del generador del sistema de compilación CMake.

Esta vez le agregaremos una biblioteca compilada y también hablaremos sobre cómo vincular módulos entre sí.

Como antes, aquellos que estén impacientes pueden inmediatamente ir al repositorio actualizado y toca todo con tus propias manos.


contenido

  1. Dividir
  2. Conquistar

Dividir

Lo primero que debemos hacer para lograr nuestro elevado objetivo es dividir el software que desarrollamos en bloques universales, aislados y uniformes desde el punto de vista del usuario.

En la primera parte, se describió un bloque estándar: un proyecto con una biblioteca de encabezados. Ahora agreguemos una biblioteca compilada a nuestro proyecto.

Para hacer esto, saquemos la implementación de la función. myfunc en un separado .cpp-archivo:

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

Luego definimos la biblioteca a compilar (myfeature), que estará compuesto por lo obtenido en el paso anterior .cpp-archivo. La nueva biblioteca obviamente requiere encabezados existentes y, para proporcionarlos, puede y debe vincularse al propósito existente. mylib. Además, la conexión entre ellos es pública, lo que significa que todo aquello a lo que estará conectado el objetivo myfeature, recibirá automáticamente la carga y el objetivo mylib (más sobre métodos de tejido).

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

A continuación, nos aseguraremos de que la nueva biblioteca también esté instalada en el 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)

Cabe señalar que para el propósito myfeature, como para mylib se creó un alias con un prefijo Mylib::. Lo mismo se escribe para ambos propósitos al exportarlos para su instalación en el sistema. Esto hace posible trabajar de manera uniforme con objetivos para cualquier esquema vinculante.

Después de esto solo queda vincular las pruebas unitarias con la nueva biblioteca (función myfunc eliminado del título, por lo que ahora necesita vincular):

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
 )

Encabezados (Mylib::mylib) ahora no es necesario conectarse por separado, porque, como ya se mencionó, se conectan automáticamente junto con la biblioteca (Mylib::myfeature).

Y agreguemos un par de matices para asegurar las mediciones de cobertura teniendo en cuenta la biblioteca recién llegada:

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

Puede agregar más bibliotecas, ejecutables, etc. No importa exactamente cómo estén conectados entre sí dentro del proyecto. Lo único importante es qué objetivos son la interfaz de nuestro módulo, es decir, sobresalen.

Conquistar

Ahora tenemos módulos de bloques estándar y podemos dominarlos: componerlos en una estructura de cualquier complejidad, instalarlos en un sistema o vincularlos dentro de un único sistema de ensamblaje.

Instalación en el sistema.

Una de las opciones para usar el módulo es instalar nuestro módulo en el sistema.

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

Después de eso, se conecta a cualquier otro proyecto usando el comando find_package.

find_package(Mylib 1.0 REQUIRED)

Conexión como submódulo

Otra opción es conectar la carpeta con nuestro proyecto a otro proyecto como un submódulo usando el comando add_subdirectory.

el uso de

Los métodos de unión son diferentes, pero el resultado es el mismo. En ambos casos, los objetivos estarán disponibles en el proyecto utilizando nuestro módulo. Mylib::myfeature и Mylib::mylib, que se puede utilizar, por ejemplo, así:

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

Concretamente en nuestro caso, la biblioteca Mylib::myfeature Necesita estar conectado cuando es necesario vincularse con la biblioteca. libmyfeature. Si hay suficientes encabezados, entonces vale la pena usar la biblioteca. Mylib::mylib.

Los objetivos de CMake pueden ser complicados, por ejemplo, solo están destinados a reenviar algunas propiedades, dependencias, etc. Al mismo tiempo, trabajar con ellos se produce de la misma forma.

Eso es lo que necesitábamos conseguir.

Fuente: habr.com

Añadir un comentario