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]