
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 and try everything out for themselves.
Content
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 ().
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 .
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.
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 installAfter that, it can be included in any other project with the command .
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 .
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
