From 4b771940329acf371037e55b88730cdf9fbe391e Mon Sep 17 00:00:00 2001 From: Roman Lebedev Date: Tue, 30 Apr 2019 13:36:29 +0300 Subject: [PATCH] CMake: codedrop of googletest cmake magic from me (#809) https://github.com/google/benchmark/pull/801 is stuck with some cryptic cmake failure due to some linking issue between googletest and threading libraries. I suspect that is mostly happening because of the, uhm, intentionally extremely twisted-in-the-brains approach that is being used to actually build the library as part of the buiild, except without actually building it as part of the build. If we do actually build it as part of the build, then all the transitive dependencies should magically be in order, and maybe everything will just work. This new version of cmake magic was written by me in https://github.com/darktable-org/rawspeed/blob/0e22f085c57cd629983c7351c598abd744b421b5/cmake/Modules/GoogleTest.cmake.in https://github.com/darktable-org/rawspeed/blob/0e22f085c57cd629983c7351c598abd744b421b5/cmake/Modules/GoogleTest.cmake, based on the official googletest docs and LOTS of experimentation. --- CMakeLists.txt | 2 +- cmake/GoogleTest.cmake | 40 ++++++++++++++ cmake/GoogleTest.cmake.in | 58 +++++++++++++++++++ cmake/HandleGTest.cmake | 113 -------------------------------------- test/CMakeLists.txt | 8 +-- 5 files changed, 100 insertions(+), 121 deletions(-) create mode 100644 cmake/GoogleTest.cmake create mode 100644 cmake/GoogleTest.cmake.in delete mode 100644 cmake/HandleGTest.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index e51cb385..5797f87f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,7 +268,7 @@ add_subdirectory(src) if (BENCHMARK_ENABLE_TESTING) enable_testing() if (BENCHMARK_ENABLE_GTEST_TESTS) - include(HandleGTest) + include(GoogleTest) endif() add_subdirectory(test) endif() diff --git a/cmake/GoogleTest.cmake b/cmake/GoogleTest.cmake new file mode 100644 index 00000000..6b98c913 --- /dev/null +++ b/cmake/GoogleTest.cmake @@ -0,0 +1,40 @@ +# Download and unpack googletest at configure time +set(GOOGLETEST_PREFIX "${benchmark_BINARY_DIR}/third_party/googletest") +configure_file(${benchmark_SOURCE_DIR}/cmake/GoogleTest.cmake.in ${GOOGLETEST_PREFIX}/CMakeLists.txt @ONLY) + +execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" + -DALLOW_DOWNLOADING_GOOGLETEST=${BENCHMARK_DOWNLOAD_DEPENDENCIES} -DGOOGLETEST_PATH:PATH="${CMAKE_CURRENT_SOURCE_DIR}/googletest" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${GOOGLETEST_PREFIX} +) + +if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") +endif() + +execute_process( + COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${GOOGLETEST_PREFIX} +) + +if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") +endif() + +# Prevent overriding the parent project's compiler/linker +# settings on Windows +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +include(${GOOGLETEST_PREFIX}/googletest-paths.cmake) + +# Add googletest directly to our build. This defines +# the gtest and gtest_main targets. +add_subdirectory(${GOOGLETEST_SOURCE_DIR} + ${GOOGLETEST_BINARY_DIR} + EXCLUDE_FROM_ALL) + +set_target_properties(gtest PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $) +set_target_properties(gtest_main PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $) +set_target_properties(gmock PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $) +set_target_properties(gmock_main PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $) diff --git a/cmake/GoogleTest.cmake.in b/cmake/GoogleTest.cmake.in new file mode 100644 index 00000000..28818ee2 --- /dev/null +++ b/cmake/GoogleTest.cmake.in @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(googletest-download NONE) + +# Enable ExternalProject CMake module +include(ExternalProject) + +option(ALLOW_DOWNLOADING_GOOGLETEST "If googletest src tree is not found in location specified by GOOGLETEST_PATH, do fetch the archive from internet" OFF) +set(GOOGLETEST_PATH "/usr/src/googletest" CACHE PATH + "Path to the googletest root tree. Should contain googletest and googlemock subdirs. And CMakeLists.txt in root, and in both of these subdirs") + +# Download and install GoogleTest + +message(STATUS "Looking for Google Test sources") +message(STATUS "Looking for Google Test sources in ${GOOGLETEST_PATH}") +if(EXISTS "${GOOGLETEST_PATH}" AND IS_DIRECTORY "${GOOGLETEST_PATH}" AND EXISTS "${GOOGLETEST_PATH}/CMakeLists.txt" AND + EXISTS "${GOOGLETEST_PATH}/googletest" AND IS_DIRECTORY "${GOOGLETEST_PATH}/googletest" AND EXISTS "${GOOGLETEST_PATH}/googletest/CMakeLists.txt" AND + EXISTS "${GOOGLETEST_PATH}/googlemock" AND IS_DIRECTORY "${GOOGLETEST_PATH}/googlemock" AND EXISTS "${GOOGLETEST_PATH}/googlemock/CMakeLists.txt") + message(STATUS "Found Google Test in ${GOOGLETEST_PATH}") + + ExternalProject_Add( + googletest + PREFIX "${CMAKE_BINARY_DIR}" + DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/download" + SOURCE_DIR "${GOOGLETEST_PATH}" # use existing src dir. + BINARY_DIR "${CMAKE_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + ) +else() + if(NOT ALLOW_DOWNLOADING_GOOGLETEST) + message(SEND_ERROR "Did not find Google Test sources! Either pass correct path in GOOGLETEST_PATH, or enable ALLOW_DOWNLOADING_GOOGLETEST, or disable BENCHMARK_ENABLE_GTEST_TESTS / BENCHMARK_ENABLE_TESTING.") + else() + message(WARNING "Did not find Google Test sources! Fetching from web...") + ExternalProject_Add( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + PREFIX "${CMAKE_BINARY_DIR}" + STAMP_DIR "${CMAKE_BINARY_DIR}/stamp" + DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/download" + SOURCE_DIR "${CMAKE_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + ) + endif() +endif() + +ExternalProject_Get_Property(googletest SOURCE_DIR BINARY_DIR) +file(WRITE googletest-paths.cmake +"set(GOOGLETEST_SOURCE_DIR \"${SOURCE_DIR}\") +set(GOOGLETEST_BINARY_DIR \"${BINARY_DIR}\") +") diff --git a/cmake/HandleGTest.cmake b/cmake/HandleGTest.cmake deleted file mode 100644 index b9c14436..00000000 --- a/cmake/HandleGTest.cmake +++ /dev/null @@ -1,113 +0,0 @@ - -include(split_list) - -macro(build_external_gtest) - include(ExternalProject) - set(GTEST_FLAGS "") - if (BENCHMARK_USE_LIBCXX) - if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - list(APPEND GTEST_FLAGS -stdlib=libc++) - else() - message(WARNING "Unsupported compiler (${CMAKE_CXX_COMPILER}) when using libc++") - endif() - endif() - if (BENCHMARK_BUILD_32_BITS) - list(APPEND GTEST_FLAGS -m32) - endif() - if (NOT "${CMAKE_CXX_FLAGS}" STREQUAL "") - list(APPEND GTEST_FLAGS ${CMAKE_CXX_FLAGS}) - endif() - string(TOUPPER "${CMAKE_BUILD_TYPE}" GTEST_BUILD_TYPE) - if ("${GTEST_BUILD_TYPE}" STREQUAL "COVERAGE") - set(GTEST_BUILD_TYPE "DEBUG") - endif() - # FIXME: Since 10/Feb/2017 the googletest trunk has had a bug where - # -Werror=unused-function fires during the build on OS X. This is a temporary - # workaround to keep our travis bots from failing. It should be removed - # once gtest is fixed. - if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - list(APPEND GTEST_FLAGS "-Wno-unused-function") - endif() - split_list(GTEST_FLAGS) - set(EXCLUDE_FROM_ALL_OPT "") - set(EXCLUDE_FROM_ALL_VALUE "") - if (${CMAKE_VERSION} VERSION_GREATER "3.0.99") - set(EXCLUDE_FROM_ALL_OPT "EXCLUDE_FROM_ALL") - set(EXCLUDE_FROM_ALL_VALUE "ON") - endif() - ExternalProject_Add(googletest - ${EXCLUDE_FROM_ALL_OPT} ${EXCLUDE_FROM_ALL_VALUE} - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG master - PREFIX "${CMAKE_BINARY_DIR}/googletest" - INSTALL_DIR "${CMAKE_BINARY_DIR}/googletest" - CMAKE_CACHE_ARGS - -DCMAKE_BUILD_TYPE:STRING=${GTEST_BUILD_TYPE} - -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} - -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER} - -DCMAKE_INSTALL_PREFIX:PATH= - -DCMAKE_INSTALL_LIBDIR:PATH=/lib - -DCMAKE_CXX_FLAGS:STRING=${GTEST_FLAGS} - -Dgtest_force_shared_crt:BOOL=ON - ) - - ExternalProject_Get_Property(googletest install_dir) - set(GTEST_INCLUDE_DIRS ${install_dir}/include) - file(MAKE_DIRECTORY ${GTEST_INCLUDE_DIRS}) - - set(LIB_SUFFIX "${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(LIB_PREFIX "${CMAKE_STATIC_LIBRARY_PREFIX}") - if("${GTEST_BUILD_TYPE}" STREQUAL "DEBUG") - set(LIB_SUFFIX "d${CMAKE_STATIC_LIBRARY_SUFFIX}") - endif() - - # Use gmock_main instead of gtest_main because it initializes gtest as well. - # Note: The libraries are listed in reverse order of their dependancies. - foreach(LIB gtest gmock gmock_main) - add_library(${LIB} UNKNOWN IMPORTED) - set_target_properties(${LIB} PROPERTIES - IMPORTED_LOCATION ${install_dir}/lib/${LIB_PREFIX}${LIB}${LIB_SUFFIX} - INTERFACE_INCLUDE_DIRECTORIES ${GTEST_INCLUDE_DIRS} - INTERFACE_LINK_LIBRARIES "${GTEST_BOTH_LIBRARIES}" - ) - add_dependencies(${LIB} googletest) - list(APPEND GTEST_BOTH_LIBRARIES ${LIB}) - endforeach() -endmacro(build_external_gtest) - -if (BENCHMARK_ENABLE_GTEST_TESTS) - if (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/googletest) - set(GTEST_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/googletest") - set(INSTALL_GTEST OFF CACHE INTERNAL "") - set(INSTALL_GMOCK OFF CACHE INTERNAL "") - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/googletest) - set(GTEST_BOTH_LIBRARIES gtest gmock gmock_main) - foreach(HEADER test mock) - # CMake 2.8 and older don't respect INTERFACE_INCLUDE_DIRECTORIES, so we - # have to add the paths ourselves. - set(HFILE g${HEADER}/g${HEADER}.h) - set(HPATH ${GTEST_ROOT}/google${HEADER}/include) - find_path(HEADER_PATH_${HEADER} ${HFILE} - NO_DEFAULT_PATHS - HINTS ${HPATH} - ) - if (NOT HEADER_PATH_${HEADER}) - message(FATAL_ERROR "Failed to find header ${HFILE} in ${HPATH}") - endif() - list(APPEND GTEST_INCLUDE_DIRS ${HEADER_PATH_${HEADER}}) - endforeach() - elseif(BENCHMARK_DOWNLOAD_DEPENDENCIES) - build_external_gtest() - else() - find_package(GTest REQUIRED) - find_path(GMOCK_INCLUDE_DIRS gmock/gmock.h - HINTS ${GTEST_INCLUDE_DIRS}) - if (NOT GMOCK_INCLUDE_DIRS) - message(FATAL_ERROR "Failed to find header gmock/gmock.h with hint ${GTEST_INCLUDE_DIRS}") - endif() - set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS}) - # FIXME: We don't currently require the gmock library to build the tests, - # and it's likely we won't find it, so we don't try. As long as we've - # found the gmock/gmock.h header and gtest_main that should be good enough. - endif() -endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d3a07639..030f35aa 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -178,14 +178,8 @@ add_test(complexity_benchmark complexity_test --benchmark_min_time=${COMPLEXITY_ if (BENCHMARK_ENABLE_GTEST_TESTS) macro(compile_gtest name) add_executable(${name} "${name}.cc") - if (TARGET googletest) - add_dependencies(${name} googletest) - endif() - if (GTEST_INCLUDE_DIRS) - target_include_directories(${name} PRIVATE ${GTEST_INCLUDE_DIRS}) - endif() target_link_libraries(${name} benchmark - ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + gmock_main ${CMAKE_THREAD_LIBS_INIT}) endmacro(compile_gtest) macro(add_gtest name)