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]
IMPORTED_LOCATIONandIMPORTED_LOCATION_<CONFIG>properties. Not sure why your script doesn't list them.IMPORTED_LOCATIONproperty because ofprop MATCHES "_LOCATION$"check. As forIMPORTED_LOCATION_<CONFIG>properties, they are probably skipped because you don't specify build type (CMAKE_BUILD_TYPE) for your project.cmakeexecutable insideCMakeLists.txtit is better to use${CMAKE_COMMAND}, so you will call exactly the samecmakewhich currently parses your script. It could be no system-widecmakeinstallation, so plaincmakewon'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 lineexecute_process(COMMAND cmake --help-property-list ...)in your code.