How to include external libraries
von Felix Mößbauer
Include Google Benchmark / Test in C++ Project
Both Google Test and Google Benchmark are common libraries used in C++ projects for testing and benchmarking. When starting a new project, adding a testsuite should be one of the first actions. But doing that correctly is often a tedious and error-prone task.
In this blog post I will show a common and a better and more recent approach. As you are probably using CMake anyway, it should be no limitation that I only cover this build system. While I show the necessary steps using the mentioned libraries, it should work with most other libraries as well.
Anti-Pattern: External Project
What I have seen multipe times - even as a recommendation - is to use the cmake script ExternalProject. While the integration is done relatively easily there are some major drawbacks:
- Internet access required in build step
- Massively slows down build process (for small changes)
- No packaging possible
Especially in HPC environments often direct internet access is not possible. Hence, the build hangs when the external target is build. There is no easy workaround for that problem, even support for local library versions has to be implemented in the cmake scripts.
Site note: That is why we dropped this approach in DASH.
Solution: Git Submodule
Instead of trying to solve both packaging and building in one step, we seperate this as follows:
- Use Git submodules for external sources
- Build each submodule and link everything together
Git Part
A good starting point for the directory structure could be the following:
Project
> vendor // external sources
>> google // place submodules in this dir
> src // project sources
> CMakeLists.txt // main cmake skript
Now add the submodules:
git submodule add https://github.com/google/googletest.git vendor/google/googletest
git submodule add https://github.com/google/benchmark.git vendor/google/benchmark
Now verify the changes using git status
. The output should be similar to the following:
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .gitmodules
new file: vendor/google/benchmark
new file: vendor/google/googletest
When adding the git submodules it is important to also commit the .gitmodules
, as otherwise they are not referenced in other clones.
The changes are already staged, so just run git commit -m '...'
to commit them.
CMake Part
To solve the issues described above, just clone using git clone --recursive
. Now you have a self-contained directory with all sources.
The following code shows the top-level CMakeLists.txt
where both submodules are build. Here, the precondition is that both modules can be build using cmake.
# build google benchmark (target: benchmark)
# do not build tests of benchmarking lib
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Suppressing benchmark's tests" FORCE)
add_subdirectory(vendor/google/benchmark)
# build tests (targets: gtest_main, gtest)
add_subdirectory(vendor/google/googletest/googletest)
Now we can link against the previously generated targets. As the headers are already bundled with the target, we just have to "link" it.
# google benchmark
target_link_libraries("${PROJECT_NAME}-bench" benchmark)
# googletest
target_link_libraries("${PROJECT_NAME}-test" gtest)
A full working example of this approach can be found in my minimum vertex cover solver library LibMVC.
Conclusion
In my opinion the second approach using submodules is much easier to maintain and offers many advantages. However it requires the users to deal with git submodules which can be tricky sometimes. Another problem arises when the submodules are added later on (after the first clone), as this requires some additional steps to get everything working.
References
- Google Test (GitHub)
- Google Benchmark (GitHub)
- LibMVC (GitHub)
- Git Submodules (Git Documentation)
- ExternalProject (CMake Documentation)
Ähnliche Beiträge
von Felix Mößbauer
Kommentare
Einen Kommentar schreiben