Post

CMake - Integrating C++ Libraries

In modern C++ development, CMake is an indispensable tool for managing project dependencies. This comprehensive guide empowers you to effortlessly integrate widely used C++ libraries into your projects using the versatile CMake build system.

We’ll delve into strategies for incorporating Google Test, Google Benchmark, OpenCV, PkgFinder, GStreamer, Catch2, and Qt, providing clear instructions and code examples tailored to both Windows and Ubuntu environments.

1. Streamlining Library Integration with CMake

CMake offers a robust mechanism for managing external libraries, simplifying the process of adding them to your C++ projects. Here are the fundamental approaches:

  • FetchContent Module: This convenient module assists in downloading and integrating libraries directly from source repositories. It’s ideal for libraries under active development or those not readily available through package managers.

  • find_package Command: For libraries distributed with package managers (e.g., PkgConfig, Conan, vcpkg, etc.), CMake’s find_package command simplifies the discovery and linking process.

  • Manual Configuration: In some cases, you might need to set environment variables or manually specify library and include directories. This approach is usually employed for libraries not managed by package managers.

Exploring Popular C++ Libraries:

Now, let’s embark on a journey through integrating some of the most popular C++ libraries.

2. Google Test

Purpose: Google Test is a robust testing framework designed for writing unit tests in C++.

2.1. Integration Google Test using FetchContent

Windows and Ubuntu Instructions: The procedure for both systems is identical using CMake’s FetchContent.

include(FetchContent)         # to use FetchContent_Declare

# -- START: GOOGLE_TEST_INCLUDE --
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG main
    OVERRIDE_FIND_PACKAGE
)

FetchContent_MakeAvailable(googletest)
include(GoogleTest)         # to use gtest_discover_tests
target_link_libraries(simple_test GTest::gtest_main)
# -- END: GOOGLE_TEST_INCLUDE --

# test files
add_executable(simple_test "simple_test.cpp")
gtest_discover_tests(simple_test)

Simple Test Code: simple_test.cpp

# include <gtest/gtest.h>
TEST(HelloTest, BasicAssertions) {
    // Expect two strings not to be equal.
    EXPECT_STRNE("hello", "world");
    // Expect equality.
    EXPECT_EQ(7 * 6, 42);
}

2.2. Installing Google Test on Ubuntu

For Ubuntu, you can alternatively install Google Test using apt:

sudo apt-get install libgtest-dev

Then manually compile the library and link it to your project.

3. Google Benchmark

Purpose: Google Benchmark simplifies the process of benchmarking C++ code for performance testing.

3.1. Integration Google Benchmark using FetchContent

include(FetchContent)   # to use FetchContent_Declare

# -- START: GOOGLE_BENCHMARK_INCLUDE --
FetchContent_Declare(
    googlebenchmark
    GIT_REPOSITORY https://github.com/google/benchmark.git
    GIT_TAG main
    OVERRIDE_FIND_PACKAGE
)

FetchContent_MakeAvailable(googlebenchmark)
target_link_libraries(${EXE_NAME} benchmark::benchmark)
# -- END: GOOGLE_BENCHMARK_INCLUDE --

# test files
add_executable(simple_benchmark "simple_benchmark.cpp")
gtest_discover_tests(simple_benchmark)

Example Benchmark Code: simple_benchmark.cpp

#include <benchmark/benchmark.h>

static void BM_StringCreation(benchmark::State& state) {
  for (auto _ : state)
    std::string empty_string;
}
// Register the function as a benchmark
BENCHMARK(BM_StringCreation);

static void BM_StringCopy(benchmark::State& state) {
  std::string x = "hello";
  for (auto _ : state)
    std::string copy(x);
}

BENCHMARK(BM_StringCopy);

3.2. Installing Google Benchmark on Ubuntu

sudo apt-get install libbenchmark-dev

Update your CMake file:

find_package(benchmark REQUIRED)
target_link_libraries(MyBenchmarkTarget benchmark::benchmark)

4. OpenCV

Purpose: OpenCV is a powerful library for real-time computer vision and image processing.

4.1. Integration of OpenCV on Windows

  • Download the pre-built OpenCV binaries for MinGW from the OpenCV-MinGW-Build.
  • Ensure you add the OpenCV binaries to your PATH variable.

CMakeLists.txt:

# Add this
set(OpenCV_DIR "C:\\OpenCV-MinGW-Build-OpenCV-4.5.5-x64")

# Find and link OpenCV
find_package(OpenCV REQUIRED)
target_include_directories(MyApp PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(MyApp PRIVATE ${OpenCV_LIBS} )

To avoid hard coded paths in CMake, you could consider adding OpenCV_DIR to your system’s PATH variable.

4.2. Integration of OpenCV on Ubuntu

To install OpenCV on Ubuntu:

sudo apt-get install libopencv-dev

Then update your CMakeLists.txt:

find_package(OpenCV REQUIRED)
target_link_libraries(MyApp PRIVATE ${OpenCV_LIBS})

Sample OpenCV Code: main.cpp

#include <opencv2/opencv.hpp>

int main(int argc, char *argv[]) {

     // Create a window using OpenCV
     cv::namedWindow("MyWindow", cv::WINDOW_AUTOSIZE);

     // Wait for a keystroke in the window
     cv::waitKey(0);

    return 0;
}

5. PkgFinder Package Manager

  • Purpose: A platform-independent package manager for C libraries.

5.1. Integration of PkgFinder on Windows

5.2. Installing on Ubuntu

sudo apt-get install pkg-config

Example usage in CMakeLists.txt

find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBNAME REQUIRED libname)
target_include_directories(MyApp PRIVATE ${LIBNAME_INCLUDE_DIRS})
target_link_libraries(MyApp PRIVATE ${LIBNAME_LIBRARIES})

6. GStreamer

  • Purpose: GStreamer is a powerful multimedia framework for streaming and video processing.

6.1. Integration of GStreamer on Windows

Gstream requires PkgFinder packe for CMake

To use GStreamer in your C++ projects, ensure the following:

  1. Make sure that Microsoft Visual C++ Redistributable is installed (or install it from the official website vc_redist.x86.exe)

  2. Install the GStreamer runtime and SDK (MSVC-64 version) from GStreamer’s website.

  3. Add the GStreamer’s binary folder to the system PATH variable.

  4. Set the PKG_CONFIG_PATH environment variable to the GStreamer package config path:
    PKG_CONFIG_PATH = path/to/gstreamer/1.0/msvc_x86_64/lib/pkgconfig`

Update your CMakeLists.txt:

find_package(PkgConfig REQUIRED)

pkg_check_modules(GST REQUIRED gstreamer-1.0)
pkg_search_module(GSTREAMER REQUIRED IMPORTED_TARGET gstreamer-1.0>=1.4)

target_include_directories(MyApp PRIVATE  ${GST_INCLUDE_DIRS})
target_link_libraries(MyApp PRIVATE PkgConfig::GSTREAMER)

6.2. Integration of GStreamer on Ubuntu

To install GStreamer:

sudo apt-get install gstreamer1.0-dev

7. Catch2

7.1. Downloading and Installing Catch2

Catch2 is another popular testing framework. To add Catch2 to your project, clone the repository and build it:

git clone https://github.com/catchorg/Catch2.git
cd Catch2
# if you want a specific version
git checkout v2.13.9

Build and install the library:

mkdir build && cd build
cmake .. -G "MinGW Makefiles"     # or "Visual Studio 17 2022" 
cmake --build . --config Release  # or Debug
cmake --install . --prefix /path  # Path to installation

By following these instructions, you should be able to seamlessly integrate these libraries into your C++ projects using CMake.

8. Qt6

Purpose: Qt6 is a modern, cross-platform GUI framework for building high-performance, visually rich applications. Qt6 also includes non-GUI components, making it a comprehensive tool for application development.

8.1. Integration of Qt6 on Windows

  • Firstly, install Qt from Qt’s official website
  • Integrating Qt6 in a self-sufficient manner with CMake involves properly setting up the environment and linking the necessary components.

8.2. Setting Up CMake

  1. At the project level, define the CMAKE_PREFIX_PATH to locate the Qt libraries:

     # Paths to depedencies
     set(CMAKE_PREFIX_PATH 
         "${DEPENDENCY_FOLDER}/some_library/" 
         "path/to/Qt/6.5.3/mingw_64/lib/cmake/Qt6"
     )
    
  2. At the executable/library level, include the following configurations:

     find_package(Qt6 REQUIRED COMPONENTS Core Gui)
        
     # Enable Qt's meta-object compiler (moc), resource compiler (rcc), and UI compiler (uic)
     set(CMAKE_AUTOMOC ON)
     set(CMAKE_AUTORCC ON)
     set(CMAKE_AUTOUIC ON)
        
     # Define Qt version macros
     add_definitions(-DQT_VERSION_MAJOR=${Qt6_VERSION_MAJOR})
     add_definitions(-DQT_VERSION_MINOR=${Qt6_VERSION_MINOR})
     add_definitions(-DQT_VERSION_PATCH=${Qt6_VERSION_PATCH})
    
  3. Next, add all what is dependent on MOC, RCC, UIC. and so on

     add_executable(MyApp)
        
     # Declare it as a GUI application (disable console)
     set_target_properties(MyApp PROPERTIES 
         WIN32_EXECUTABLE TRUE 
         MACOSX_BUNDLE TRUE
     )
        
     # Link against Qt6 Components
     target_link_libraries(MyApp PRIVATE Qt6::Core Qt6::Gui)
    
  4. Only then, set up the executable:

     # Add sources and headers
     target_sources(experiment_01 PRIVATE 
         source/main.cpp 
         include/MyApp.h    # Also headers with QObject
     )
        
     # Specify include directories
     target_include_directories(experiment_01 PRIVATE include)
    

8.3. Linking Libraries

  • For platform-specific libraries, copy the necessary plugins (e.g., qwindows.dll) to the platform directory in your binary.
path/to/Qt/6.5.3/mingw_64/plugins/platforms

If you are on Windows, Copy qwindows.dll

  • Core Qt components such as Qt6Core.dll, Qt6Widgets.dll, and Qt6Gui.dll can be found in:
path/to/Qt/6.5.3/mingw_64/bin

8.4. Integration of Qt6 on Ubuntu

To integrate Qt6 on Ubuntu, follow these steps:

  1. Install Qt6: On Ubuntu, you can use the system’s package manager to install the necessary development packages for Qt6.

     sudo apt-get install qt6-base-dev qt6-tools-dev-tools qt6-tools-dev qt6-base-dev-tools
    

    This will install the Qt6 Core, Gui, and other essential components.

  2. Set Up CMake for Qt6: Once Qt6 is installed, configure your CMakeLists.txt to find and link the necessary Qt6 components.

8.5. Deploying Qt6 Applications on Ubuntu

To ensure your Qt6 application runs smoothly on Ubuntu, you might need to ensure that the correct Qt6 libraries and plugins are packaged with your application. For development purposes, the necessary libraries will usually be located automatically through CMake, but for deployment, you may need to package or point to the correct Qt paths manually.

For Qt6 deployment, use the qt6-deploy tool, available as part of the Qt6 installation, to gather all the necessary components and bundle them with your application.

qt6-deploy MyApp

9. Conclusion

In this guide, we have covered the essential steps to seamlessly integrate some of the most widely-used C++ libraries into your projects using CMake. From Google Test for unit testing, Google Benchmark for performance measurement, OpenCV for computer vision, to GStreamer for multimedia handling, and Qt6 for building cross-platform graphical applications, this guide provides a structured approach to handling dependencies.

This post is licensed under CC BY 4.0 by the author.