C++ and CMake — brothers forever, part II

C++ and CMake — brothers forever, part II

In the previous part This engaging story discusses the organization of a header library within the CMake build system generator.

This time, we will add a compiled library and also discuss how to link the modules together.

As before, those who can't wait can jump straight to the updated repository and try everything out for themselves.


Content

  1. Separate
  2. Conquer

Separate

The first step to achieving our lofty goal is to divide the software being developed into universal isolated blocks that are consistent from the user's perspective.

In the first part, we described such a standard block—a project with a header library. Now, let's add a compiled library to our project.

To do this, we will extract the implementation of the function myfunc into a separate .cppfile:

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

Then, we will define a compiled library (myfeature), which will consist of the file generated in the previous step. The new library clearly requires the existing headers, and to ensure this, it is essential to link it to the existing target .cppmylib . Moreover, the linkage between them is public, meaning that everything connected to the target, will automatically gain access to the target myfeaturelet's go over the linking methods . Moreover, the linkage between them is public, meaning that everything connected to the target (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)+################################################################################################### +## +## Compiled Library +## +################################################################################################### + +add_library(myfeature src/mylib/myfeature.cpp) +target_link_libraries(myfeature PUBLIC mylib) + +add_library(Mylib::myfeature ALIAS myfeature) +).

Next, we will ensure that the new library also gets installed in 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 is important to note that for the target

, as well as for myfeature. . Moreover, the linkage between them is public, meaning that everything connected to the target a pseudonym was created with the prefix Mylib::. The same configuration is set for both targets when exporting them for installation in the system. This allows for consistent operation with targets under any binding scheme.

After that, we needed to link the module tests with the new library (the function myfunc was removed from the header, so it now needs to be linked):

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
 )

Headers (Mylib::mylib) no longer need to be included separately, because, as already mentioned, they are automatically included with the library (Mylib::myfeature).

And let's add a couple of nuances to ensure coverage measurements taking into account the newly added 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 doesn't matter how they are linked together within the project. What matters is which targets serve as the interface of our module, meaning they are exposed externally.

Conquer

Now we have standard module blocks, and we can take control over them: creating a structure of any complexity out of them, installing them in the system or linking them with each other within a unified build system.

Installation into the system

One way to use the module is to install our module into the system.

cmake --build path/to/build/directory --target install

After that, it can be included in any other project with the command find_package.

find_package(Mylib 1.0 REQUIRED)

Including as a submodule

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

Using

There are various binding methods, but the result is the same. In either case, in the project using our module, the targets will be available Mylib::myfeature and 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)

In our specific case, the library Mylib::myfeature should be linked when it is necessary to connect with the library libmyfeature. If headers are sufficient, then it is better to use the library Mylib::mylib.

CMake targets can be tricky, for example, designed solely to pass through certain properties, dependencies, etc. Interacting with them is done uniformly.

Which is what was required.

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers 🔥 Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster