Better way to work by modules with CMake and GTest or any library.

Hey there,

I am finding myself stuck to integrate some libraries of my own that are using CMake into a product. I am integrating this into the main project.

The different structure of the libraries is the following:

LIB_A:

+-- include/
|   +-- header files 
+--src
|   +-- source files
+-- test
|   +-- test source files
|   +-- CMakeLists.txt // where I fetch GoogleTest and a JSON library for testing purposes
+-- CMakeLists.txt // where I created the lib and enable testing adding the test subdirectory

And that structure is followed for the rest of the libraries I have, which are around 5, for different devices like cameras, sensors, and stuff.

I am using git submodules in the main project to include my libraries, and I am adding them with
add_subdirectory(3rdparty/LIB_A)

I am also fetching Google Test for testing purposes in the main project, mainly integration of each library with the rest of the components or with other specific features of the product. The issue I having is that Google Test is being already detected when fetching it in the main project.

How can I avoid this? The issue is that each library is using it. I could install GTest in the system but my manager, for some reason, does not want that approach.

Best regards

Take a look at the FetchContent module. It uses GoogleTest as one of its examples. It’s not the only way to bring it into your build, but I’ve found it to be one of the easiest.

Hi Craig,

I found that’s the way to go as well. I am not having an issue with how the module FetchContent works.
My submodule CMake its as follows:

message(STATUS "Fetching googletest")
include(FetchContent)

FetchContent_Declare(
        googletest
        GIT_REPOSITORY https://github.com/google/googletest.git
        GIT_TAG release-1.11.0
        GIT_SHALLOW TRUE
        GIT_PROGRESS TRUE)
FetchContent_MakeAvailable(googletest)

message(STATUS "Fetching JSON library")
set(JSON_BuildTests OFF CACHE INTERNAL "")
FetchContent_Declare(
        json
        GIT_REPOSITORY https://github.com/nlohmann/json
        GIT_TAG v3.9.1
        GIT_SHALLOW TRUE
        GIT_PROGRESS TRUE)
FetchContent_MakeAvailable(json)

set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

add_executable(XUnitTes X1UnitTest.cpp X2UnitTest.cpp X3UnitTesting.cpp)
target_link_libraries(xUnitTest gtest_main MOTOR nlohmann_json::nlohmann_json)

and it works like a charm :grinning:

The problem comes when I am adding the submodule to my main project where I use Google Test for testing as well. GoogleTest is being detected in build-debug/_deps/googletest-src already when CMake runs the fetching from the submodule.

This is the CMake file for testing in my main project.

include(CTest)
include(FetchContent)

FetchContent_Declare(
        googletest
        GIT_REPOSITORY https://github.com/google/googletest.git
        GIT_TAG release-1.11.0
        GIT_SHALLOW TRUE
        GIT_PROGRESS TRUE)
FetchContent_MakeAvailable(googletest)

set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
add_executable(PlaceHolderUnitTest PlaceHolderUnitTest.cpp)

target_link_libraries(PlaceHolderUnitTest gtest_main)

I don’t see how I can tell CMake to build GTest in another module. Should I use FetchContent_Populate instead of FetchContent_MakeAvailable?

Edit: As I mentioned in the post, the submodules are being added with git submodule, not with the FetchContent module.

Edit2: I guess the correct way of doing if gtest was populated already is with FetchContent_Populate. If this works I will post the final CMakeLists.txt.

Best regards,
Xhustango.

I don’t understand why the distinction between the submodules and main project. If you are adding the submodules to the main project using add_subdirectory(), then the whole thing is one project and it should all share the same GoogleTest instance.

Use FetchContent_MakeAvailable() and avoid calling FetchContent_Populate() if you can. The latter should only be needed in unusual situations. The FetchContent_MakeAvailable() was added later and should be the preferred method for most situations now.

I am mentioning the distinction because when using my ModuleA which is a library into the ProjectA which is using GTest as well and I was getting an error (Google Test was fetched previously when fetching in the ProjectA) that I am not having at the moment.

I must be missing something about the way your project is structured. Can you put together a complete, minimal project which demonstrates the issue you are running into? That should make the problem clearer.

The structure is as follows:

+-- 3rdparty/
|   +-- LibA  // contains fetching  gtest and json to read config files
|   +-- LibB/ // contains fetching  gtest
|   +-- LibC/ // contains fetching  gtest
+-- ProjectA
|   +-- include/
|   |   +-- header files 
|   +--src
|   |   +-- source files
|   +-- test
|   |   +-- test source files
|   |   +-- CMakeLists.txt // It was containing ExternalProject_Add for get  GTest to build the test binaries.
|   +-- CMakeLists.txt // Building projectA
+-- CMakeLists.txt // main CMake where I do add_subdirectory(ProjectA); add_subdirectory(LibA)

The error I was getting it was related to using the external project module of CMake that was realizing that the GoogleTest was already fetched at the project level. Changing the external project module to use the FetchingContent did the trick. All this is was coming from a previous configuration where ExternalProject_Add was being used.

So FetchContent fixes the issue, and as well I even removed it from there because there is no need anymore to fetch as its coming from one of the libraries I built.