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.
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.
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.
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)
Another option is to connect the folder with our project to another project as a submodule using the add_subdirectory
.
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.