C++ e CMake – irmãos para sempre, parte II

C++ e CMake – irmãos para sempre, parte II

Na parte anterior Esta história divertida falava sobre a organização de uma biblioteca de cabeçalhos dentro do gerador do sistema de construção CMake.

Desta vez adicionaremos uma biblioteca compilada a ela e também falaremos sobre como vincular módulos entre si.

Como antes, aqueles que estão impacientes podem imediatamente vá para o repositório atualizado e toque em tudo com suas próprias mãos.


Conteúdo

  1. Dividir
  2. Conquistar

Dividir

A primeira coisa que precisamos fazer para atingir nosso objetivo elevado é dividir o software que desenvolvemos em blocos universais, isolados e uniformes do ponto de vista do usuário.

A primeira parte descreveu esse bloco padrão - um projeto com uma biblioteca de cabeçalho. Agora vamos adicionar uma biblioteca compilada ao nosso projeto.

Para fazer isso, vamos retirar a implementação da função myfunc em um separado .cpp-arquivo:

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

Em seguida definimos a biblioteca a ser compilada (myfeature), que consistirá no que foi obtido na etapa anterior .cpp-arquivo. A nova biblioteca obviamente requer cabeçalhos existentes e, para fornecer isso, ela pode e deve estar vinculada ao propósito existente mylib. Além disso, a conexão entre eles é pública, o que significa que tudo ao qual o alvo estará conectado myfeature, receberá automaticamente a carga e o destino mylib (mais sobre métodos de tricô).

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 seguir, garantiremos que a nova biblioteca também esteja instalada no 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)

Deve-se notar que para o efeito myfeature, quanto a mylib um alias com um prefixo foi criado Mylib::. O mesmo é escrito para ambos os fins ao exportá-los para instalação no sistema. Isto torna possível trabalhar uniformemente com metas para qualquer esquema vinculativo.

Depois disso, resta vincular os testes unitários à nova biblioteca (função myfunc retirado do título, então agora você precisa 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
 )

Títulos (Mylib::mylib) agora você não precisa se conectar separadamente, pois, como já mencionado, eles são conectados automaticamente junto com a biblioteca (Mylib::myfeature).

E vamos adicionar algumas nuances para garantir medições de cobertura levando em consideração a biblioteca recém-chegada:

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

Você pode adicionar mais bibliotecas, executáveis, etc. Não importa exatamente como eles estão conectados entre si dentro do projeto. A única coisa importante é quais alvos são a interface do nosso módulo, ou seja, eles se destacam.

Conquistar

Agora temos módulos de blocos padrão e podemos dominá-los: compô-los em uma estrutura de qualquer complexidade, instalando-os em um sistema ou conectando-os em um único sistema de montagem.

Instalação no sistema

Uma das opções de utilização do módulo é instalar nosso módulo no sistema.

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

Depois disso, ele está conectado a qualquer outro projeto usando o comando find_package.

find_package(Mylib 1.0 REQUIRED)

Conexão como um submódulo

Outra opção é conectar a pasta do nosso projeto a outro projeto como um submódulo usando o comando add_subdirectory.

Usar

Os métodos de vinculação são diferentes, mas o resultado é o mesmo. Em ambos os casos, as metas estarão disponíveis no projeto através do nosso módulo Mylib::myfeature и Mylib::mylib, que pode ser usado, por exemplo, assim:

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

Especificamente no nosso caso, a biblioteca Mylib::myfeature precisa estar conectado quando for necessário vincular-se à biblioteca libmyfeature. Se houver cabeçalhos suficientes, vale a pena usar a biblioteca Mylib::mylib.

Os alvos do CMake podem ser complicados, por exemplo, destinados apenas a encaminhar algumas propriedades, dependências, etc. Ao mesmo tempo, trabalhar com eles ocorre da mesma forma.

Isso é o que precisávamos conseguir.

Fonte: habr.com

Adicionar um comentário