C ++ and CMake - Brothers Forever, Part II

Friendship forever







In the previous part of this entertaining story, we talked about organizing a header library as part of the CMake assembly system generator.







This time we add the compiled library to it, and also talk about the layout of the modules with each other.







As before, those who canโ€™t wait, can immediately go to the updated repository and touch everything with their own hands.




Content



  1. Share
  2. Conquer




Share



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







The first part 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 take out the implementation of the myfunc



function in 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 compiled library ( myfeature



), which will consist of the .cpp



file obtained in the previous step. The new library, obviously, requires existing headers, and in order to ensure this, you can and should knit it with the existing purpose of mylib



. Moreover, the link between them is public, which means that everything that the myfeature



target will be connected to myfeature



automatically receive the mylib



target in the load ( more about how to connect ).







 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 the new library 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 well as for mylib



, an alias was prefixed with the prefix Mylib::



. The same thing is written for both purposes when exporting them for installation in the system. This makes it possible to work uniformly with goals for any linking scheme .







After that, it remains to knit unit tests with the new library (the myfunc



function has been myfunc



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





Headers ( Mylib::mylib



) now do not need to be connected separately, because, as already mentioned, they are automatically connected together with the library ( Mylib::myfeature



).







And 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 doesnโ€™t matter how exactly they are knitted together in the framework of the project. The only important thing is what goals are the interface of our module, that is, stick out.









Conquer



Now we have standard modules-blocks, and we can dominate them: make up a structure of any complexity from them, installing them in a system or interconnecting them within a single assembly system.







System Installation



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







 cmake --build /// --target install
      
      





After that, it connects to any other project using the find_package



.







 find_package(Mylib 1.0 REQUIRED)
      
      





Connection as a submodule



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



.







Using



The binding methods are different, but the result is the same. In both cases, in a project using our module, the goals Mylib::myfeature



and Mylib::mylib



will be available, 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 Mylib::myfeature



needs to be connected when it is necessary to libmyfeature



library. If there are enough headers, then you should use the Mylib::mylib



library.







CMake targets can be tricky, for example, designed only to forward some properties, dependencies, etc. At the same time, work with them occurs in a single way.







What was required to receive.








All Articles