C++ and CMake - brothers forever, part II

C++ and CMake - brothers forever, part II

In the previous part This entertaining story talked about the organization of the header library within the framework of the CMake build system generator.

This time we will add a compiled library to it, and also talk about linking modules with each other.

As before, those who are impatient can immediately go to updated repository and touch everything with your hands.


Content

  1. Divide
  2. Rule

Divide

The first thing to do to achieve our lofty goal is to divide the developed software into universal isolated blocks that are uniform from the user's point of view.

In the first part, such a building block was described - a project with a header library. Now let's add a compiled library to our project.

To do this, we will take out the implementation of the function myfunc into a separate .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;
+    }
+}

Then we define the library to be compiled (myfeature), which will consist of the obtained at the previous step .cpp-file. The new library obviously needs the existing headers, and in order to provide this, it can and should be linked to the existing goal. mylib. Moreover, the binding between them is public, which means that everything to which the target will be connected myfeature, will automatically receive the load and target mylib (more about tying methods).

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

Next, we will make sure that the new library is also installed on the system:

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

It should be noted that for the purpose myfeature, as for mylib an alias was created with a prefix Mylib::. The same is written for both purposes when exporting them for installation on the system. This makes it possible to work uniformly with goals for any binding scheme.

After that, it remains to tie unit tests with the new library (function myfunc moved out of the header, so now you need to link):

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
 )

Titles (Mylib::mylib) now you don’t need to connect separately, because, as already mentioned, they are automatically connected together with the library (Mylib::myfeature).

And let's add a couple of nuances to ensure coverage measurements taking into account the newly arrived library:

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

You can add more libraries, executables, etc. It does not matter how exactly they are knitted together within the framework of the project. The only important thing is which targets are the interface of our module, that is, stick out.

Rule

Now we have standard modules-blocks, and we can rule over them: compose a structure of any complexity from them, installing them into the system or linking them together within a single assembly system.

System installation

One of the options for using the module is to install our module on the system.

cmake --build ΠΏΡƒΡ‚ΡŒ/ΠΊ/сборочной/Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΈ --target install

After that, it is connected to any other project using the command find_package.

find_package(Mylib 1.0 REQUIRED)

Connecting as a submodule

Another option is to connect the folder with our project to another project as a submodule using the command add_subdirectory.

Using

The binding methods are different, but the result is the same. In both cases, in the project using our module, goals will be available Mylib::myfeature ΠΈ Mylib::mylib, which can be used, for example, like this:

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

Specifically, in our case, the library Mylib::myfeature you need to connect when you need to link with the library libmyfeature. If there are enough headers, then you should use the library Mylib::mylib.

CMake targets can be tricky, for example, intended only to forward some properties, dependencies, etc. At the same time, they work in the same way.

Which is what you need to get.

Source: habr.com

Add a comment