1

I'm trying to understand targets and properties in CMake. When using GTest in a C++ project, one needs to link to the GTest static libraries, which in my case have the default paths /usr/lib/libgtest.a and /usr/lib/libgtest_main.a. These paths are contained in variables (or macros?) GTEST_LIBRARIES and GTEST_MAIN_LIBRARIES, which are set when you run find_package(GTest REQUIRED).

Another way (seemingly more "modern CMake"?) is to use GTest::Main as a target in target_link_libraries. This also works but I don't understand why. Where are the library paths in terms of properties of this target?

To find out, I tried to list all the target properties using code from 1. Below code shows how these properties are listed and the output.

// tests_main.cpp
// Google Test framework
#include <gtest/gtest.h>


int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

TEST(ExampleTests, example1) {
    EXPECT_EQ(1, 1);
}

# list_target_properties.cmake

# Taken from https://stackoverflow.com/q/32183975/9988487
# Defines function `print_target_properties` that lists all properties of a target.

# Get all propreties that cmake supports
execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)

# Convert command output into a CMake list
STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")

function(print_properties)
    message ("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}")
endfunction(print_properties)

function(print_target_properties tgt)
    if(NOT TARGET ${tgt})
        message("There is no target named '${tgt}'")
        return()
    endif()

    foreach (prop ${CMAKE_PROPERTY_LIST})
        string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" prop ${prop})
        # Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i
        if(prop STREQUAL "LOCATION" OR prop MATCHES "^LOCATION_" OR prop MATCHES "_LOCATION$")
            continue()
        endif()
        # message ("Checking ${prop}")
        get_property(propval TARGET ${tgt} PROPERTY ${prop} SET)
        if (propval)
            get_target_property(propval ${tgt} ${prop})
            message ("${tgt} ${prop} = ${propval}")
        endif()
    endforeach(prop)
endfunction(print_target_properties)
# CMakeLists.txt
cmake_minimum_required(VERSION 3.12)

find_package(GTest REQUIRED)

add_executable(unit_tests
        tests_main.cpp
        )
target_compile_features(unit_tests
        PUBLIC
        cxx_std_17
        )
target_include_directories(unit_tests
        PUBLIC
        )
if(USE_GTEST_MACROS_TO_FIND_LIBRARY_PATHS)
# I was originally using this way to link to GTest
target_link_libraries(unit_tests
                ${GTEST_LIBRARIES} # macro provided by find_package(GTest REQUIRED) earlier
                ${GTEST_MAIN_LIBRARIES} # macro provided by find_package(GTest REQUIRED) earlier
        pthread
        )
else()
    # Then I started using this way
    target_link_libraries(unit_tests
            GTest::Main
            pthread
            )
endif(USE_GTEST_MACROS_TO_FIND_LIBRARY_PATHS)

# Both ways work but I don't understand why the second way works. What property of `GTest::Main` contains the library
# paths that are contained in ${GTEST_LIBRARIES} and ${GTEST_MAIN_LIBRARIES}?

# Code to print the static library paths for GTest
message(STATUS "Value of GTEST_LIBRARIES: ${GTEST_LIBRARIES}")
message(STATUS "Value of GTEST_MAIN_LIBRARIES: ${GTEST_MAIN_LIBRARIES}")

# Code that (I hope) prints all properties of the GTest::Main target and the GTest::GTest target, which is specified as
# a dependency of GTest::GTest.
include(list_target_properties.cmake) # provides function `print_target_properties`
print_target_properties(GTest::Main)
message(STATUS "---------------------")
print_target_properties(GTest::GTest)

CMake Output:

...
...

-- Found GTest: /usr/lib/libgtest.a  
-- Value of GTEST_LIBRARIES: /usr/lib/libgtest.a
-- Value of GTEST_MAIN_LIBRARIES: /usr/lib/libgtest_main.a
GTest::Main AUTOMOC_COMPILER_PREDEFINES = ON
GTest::Main AUTOMOC_MACRO_NAMES = Q_OBJECT;Q_GADGET;Q_NAMESPACE;Q_NAMESPACE_EXPORT
GTest::Main BINARY_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets/cmake-build-debug
GTest::Main BINARY_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets/cmake-build-debug
GTest::Main BUILD_WITH_INSTALL_RPATH = OFF
GTest::Main IMPORTED = TRUE
GTest::Main IMPORTED_LINK_INTERFACE_LANGUAGES = CXX
GTest::Main INSTALL_RPATH = 
GTest::Main INSTALL_RPATH_USE_LINK_PATH = OFF
GTest::Main INTERFACE_LINK_LIBRARIES = GTest::GTest
GTest::Main NAME = GTest::Main
GTest::Main SKIP_BUILD_RPATH = OFF
GTest::Main SOURCE_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets
GTest::Main SOURCE_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets
GTest::Main TYPE = UNKNOWN_LIBRARY
GTest::Main TYPE = UNKNOWN_LIBRARY
-- ---------------------
GTest::GTest AUTOMOC_COMPILER_PREDEFINES = ON
GTest::GTest AUTOMOC_MACRO_NAMES = Q_OBJECT;Q_GADGET;Q_NAMESPACE;Q_NAMESPACE_EXPORT
GTest::GTest BINARY_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets/cmake-build-debug
GTest::GTest BINARY_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets/cmake-build-debug
GTest::GTest BUILD_WITH_INSTALL_RPATH = OFF
GTest::GTest IMPORTED = TRUE
GTest::GTest IMPORTED_LINK_INTERFACE_LANGUAGES = CXX
GTest::GTest INSTALL_RPATH = 
GTest::GTest INSTALL_RPATH_USE_LINK_PATH = OFF
GTest::GTest INTERFACE_INCLUDE_DIRECTORIES = /usr/include
GTest::GTest INTERFACE_LINK_LIBRARIES = Threads::Threads
GTest::GTest NAME = GTest::GTest
GTest::GTest SKIP_BUILD_RPATH = OFF
GTest::GTest SOURCE_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets
GTest::GTest SOURCE_DIR = /home/alice/CLionProjects/UnderstandCMakeTargets
GTest::GTest TYPE = UNKNOWN_LIBRARY
GTest::GTest TYPE = UNKNOWN_LIBRARY
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alice/CLionProjects/UnderstandCMakeTargets/cmake-build-debug

[Finished]
Adomas Baliuka
  • 1,384
  • 2
  • 14
  • 29
  • 1
    According to the script [FindGTest.cmake](https://github.com/Kitware/CMake/blob/master/Modules/FindGTest.cmake#L162), path to the library is set in `IMPORTED_LOCATION` and `IMPORTED_LOCATION_` properties. Not sure why your script doesn't list them. – Tsyvarev Dec 10 '20 at 14:42
  • 1
    Well, your script skips `IMPORTED_LOCATION` property because of `prop MATCHES "_LOCATION$"` check. As for `IMPORTED_LOCATION_` properties, they are probably skipped because you don't specify build type (`CMAKE_BUILD_TYPE`) for your project. – Tsyvarev Dec 10 '20 at 14:47
  • @Tsyvarev Thanks for your comment. In my test I set the build type to DEBUG (default for CLion IDE, which I use). Should have mentioned that in the question. – Adomas Baliuka Dec 10 '20 at 14:55
  • BTW, for call `cmake` executable inside `CMakeLists.txt` it is better to use `${CMAKE_COMMAND}`, so you will call exactly the same `cmake` which currently parses your script. It could be no system-wide `cmake` installation, so plain `cmake` won't work. Also, some IDE (and CLion too) could use their own versions of CMake, which differs from the system one. This all is about the line `execute_process(COMMAND cmake --help-property-list ...)` in your code. – Tsyvarev Dec 10 '20 at 16:01
  • @Tsyvarev I do not fully understand the script which defines the function listing the target properties. It is taken from the linked StackOverflow post, https://stackoverflow.com/q/32183975/9988487. If you have suggestions how to improve that function, please comment on the answer in that thread. – Adomas Baliuka Dec 10 '20 at 16:06
  • @Tsyvarev as you pointed out, the function for listing the properties explicitly excludes the properties I was interested in. If you post this fact as an answer, I can accept it. – Adomas Baliuka Dec 10 '20 at 17:19

0 Answers0