CMake + GoogleTest

Kor*_*idu 47 cmake googletest

我刚刚下载了googletest,用CMake生成了它的makefile并构建了它.现在,我需要在我的测试项目中使用它.

有了CMake,我被告知不要直接指向gtest库(使用include _directorieslink_directories),而是使用find_package().

问题是,生成的gtest makefile没有安装目标.我无法理解find_package(GTest REQUIRED)如果没有某种安装可以工作.此外,将gtest文件夹作为子文件夹放在我的项目中是不可能的.

谢谢你的帮助.

Fra*_*ser 64

这是一个不寻常的案例; 大多数项目指定安装规则.

CMake的ExternalProject_Add模块可能是这项工作的最佳工具.这允许您从项目中下载,配置和构建gtest,然后链接到gtest库.

我已经使用Visual Studio 10和11在Windows上测试了以下CMakeLists.txt,在使用GCC 4.8和Clang 3.2的Ubuntu上测试了它 - 可能需要针对其他平台/编译器进行调整:

cmake_minimum_required(VERSION 2.8.7 FATAL_ERROR)
project(Test)

# Create main.cpp which uses gtest
file(WRITE src/main.cpp "#include \"gtest/gtest.h\"\n\n")
file(APPEND src/main.cpp "TEST(A, B) { SUCCEED(); }\n")
file(APPEND src/main.cpp "int main(int argc, char **argv) {\n")
file(APPEND src/main.cpp "  testing::InitGoogleTest(&argc, argv);\n")
file(APPEND src/main.cpp "  return RUN_ALL_TESTS();\n")
file(APPEND src/main.cpp "}\n")

# Create patch file for gtest with MSVC 2012
if(MSVC_VERSION EQUAL 1700)
  file(WRITE gtest.patch "Index: cmake/internal_utils.cmake\n")
  file(APPEND gtest.patch "===================================================================\n")
  file(APPEND gtest.patch "--- cmake/internal_utils.cmake   (revision 660)\n")
  file(APPEND gtest.patch "+++ cmake/internal_utils.cmake   (working copy)\n")
  file(APPEND gtest.patch "@@ -66,6 +66,9 @@\n")
  file(APPEND gtest.patch "       # Resolved overload was found by argument-dependent lookup.\n")
  file(APPEND gtest.patch "       set(cxx_base_flags \"\${cxx_base_flags} -wd4675\")\n")
  file(APPEND gtest.patch "     endif()\n")
  file(APPEND gtest.patch "+    if (MSVC_VERSION EQUAL 1700)\n")
  file(APPEND gtest.patch "+      set(cxx_base_flags \"\${cxx_base_flags} -D_VARIADIC_MAX=10\")\n")
  file(APPEND gtest.patch "+    endif ()\n")
  file(APPEND gtest.patch "     set(cxx_base_flags \"\${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32\")\n")
  file(APPEND gtest.patch "     set(cxx_base_flags \"\${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN\")\n")
  file(APPEND gtest.patch "     set(cxx_exception_flags \"-EHsc -D_HAS_EXCEPTIONS=1\")\n")
else()
  file(WRITE gtest.patch "")
endif()

# Enable ExternalProject CMake module
include(ExternalProject)

# Set the build type if it isn't already
if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release)
endif()

# Set default ExternalProject root directory
set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/ThirdParty)

# Add gtest
ExternalProject_Add(
    googletest
    SVN_REPOSITORY http://googletest.googlecode.com/svn/trunk/
    SVN_REVISION -r 660
    TIMEOUT 10
    PATCH_COMMAND svn patch ${CMAKE_SOURCE_DIR}/gtest.patch ${CMAKE_BINARY_DIR}/ThirdParty/src/googletest
    # Force separate output paths for debug and release builds to allow easy
    # identification of correct lib in subsequent TARGET_LINK_LIBRARIES commands
    CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
               -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=DebugLibs
               -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=ReleaseLibs
               -Dgtest_force_shared_crt=ON
    # Disable install step
    INSTALL_COMMAND ""
    # Wrap download, configure and build steps in a script to log output
    LOG_DOWNLOAD ON
    LOG_CONFIGURE ON
    LOG_BUILD ON)

# Specify include dir
ExternalProject_Get_Property(googletest source_dir)
include_directories(${source_dir}/include)

# Add compiler flag for MSVC 2012
if(MSVC_VERSION EQUAL 1700)
  add_definitions(-D_VARIADIC_MAX=10)
endif()

# Add test executable target
add_executable(MainTest ${PROJECT_SOURCE_DIR}/src/main.cpp)

# Create dependency of MainTest on googletest
add_dependencies(MainTest googletest)

# Specify MainTest's link libraries
ExternalProject_Get_Property(googletest binary_dir)
if(MSVC)
  set(Suffix ".lib")
else()
  set(Suffix ".a")
  set(Pthread "-pthread")
endif()
target_link_libraries(
    MainTest
    debug ${binary_dir}/DebugLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gtest${Suffix}
    optimized ${binary_dir}/ReleaseLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gtest${Suffix}
    ${Pthread})
Run Code Online (Sandbox Code Playgroud)

如果在空目录(例如MyTest)中将其创建为CMakeLists.txt ,则:

cd MyTest
mkdir build
cd build
cmake ..
Run Code Online (Sandbox Code Playgroud)

这应该创建一个基本的main.cpp MyTest/src并创建一个项目文件(MyTest/build/Test.sln在Windows上)

在构建项目时,它应该将gtest源代码下载到MyTest/build/ThirdParty/src/googletest并构建它们MyTest/build/ThirdParty/src/googletest-build.然后,您应该能够成功运行MainTest目标.

  • 我同意它可能稍微简单,但我认为它不是更清洁.使用`ADD_SUBDIRECTORY`选项,您最终会在自己的源代码树中使用第三方源代码,而`ExternalProject_Add`会在您的(一次性)构建树中转储第三方代码,而您的源代码树只包含您自己的源代码.这取决于你 - 我不会强烈推荐一个而不是另一个. (8认同)
  • 对我来说似乎很复杂.有没有办法直接引用谷歌测试CMakeLists.txt文件?或者我应该将googletest库添加为子目录吗? (4认同)
  • 我不认为它比将googletest添加为子目录要复杂得多,但是如果您有选择那么肯定 - 将googletest添加为源树的子目录并通过`ADD_SUBDIRECTORY`将其拉入.(您确实指出这不是原始问题中的选项). (3认同)

Cra*_*ott 28

很久以前问到原始问题,但为了其他人的利益,可以使用ExternalProject下载gtest源,然后用add_subdirectory()它来添加到你的构建中.这具有以下优点:

  • gtest是作为主构建的一部分构建的,因此它使用相同的编译器标志等,并且不需要在任何地方安装.
  • 无需将gtest源添加到您自己的源代码树中.

以正常方式使用,ExternalProject将不会在配置时进行下载和解包(即运行CMake时),但您可以这样做.我写过一篇关于如何做到这一点的博客文章,其中还包括一个通用实现,适用于任何使用CMake作为构建系统的外部项目,而不仅仅是gtest.你可以在这里找到它:

https://crascit.com/2015/07/25/cmake-gtest/

更新:上述方法现在也是googletest文档的一部分.


fir*_*iku 8

使用ExternalProject模块和导入库的功能有一点不太复杂的解决方案cmake.它从存储库中检出代码,构建它并从构建的静态库(它们libgtest.alibgtest_main.a我的系统)创建目标.

find_package(Threads REQUIRED)
include(ExternalProject)

set(GTEST_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/gtest")
ExternalProject_Add(GTestExternal
    SVN_REPOSITORY http://googletest.googlecode.com/svn/trunk
    SVN_REVISION -r HEAD
    TIMEOUT 10
    PREFIX "${GTEST_PREFIX}"
    INSTALL_COMMAND "")

set(LIBPREFIX "${CMAKE_STATIC_LIBRARY_PREFIX}")
set(LIBSUFFIX "${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(GTEST_LOCATION "${GTEST_PREFIX}/src/GTestExternal-build")
set(GTEST_INCLUDES "${GTEST_PREFIX}/src/GTestExternal/include")
set(GTEST_LIBRARY  "${GTEST_LOCATION}/${LIBPREFIX}gtest${LIBSUFFIX}")
set(GTEST_MAINLIB  "${GTEST_LOCATION}/${LIBPREFIX}gtest_main${LIBSUFFIX}")

add_library(GTest IMPORTED STATIC GLOBAL)
set_target_properties(GTest PROPERTIES
    IMPORTED_LOCATION                 "${GTEST_LIBRARY}"
    INTERFACE_INCLUDE_DIRECTORIES     "${GTEST_INCLUDES}"
    IMPORTED_LINK_INTERFACE_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}")

add_library(GTestMain IMPORTED STATIC GLOBAL)
set_target_properties(GTestMain PROPERTIES
    IMPORTED_LOCATION "${GTEST_MAINLIB}"
    IMPORTED_LINK_INTERFACE_LIBRARIES
        "${GTEST_LIBRARY};${CMAKE_THREAD_LIBS_INIT}")

add_dependencies(GTest GTestExternal)
Run Code Online (Sandbox Code Playgroud)

您可能需要更换SVN_REVISION或添加LOG_CONFIGURE,并LOG_BUILD在此选项.在创建GTestGTestMain目标之后,可以像这样使用它们:

add_executable(Test
    test1.cc
    test2.cc)
target_link_libraries(Test GTestMain)
Run Code Online (Sandbox Code Playgroud)

或者,如果您有自己的main()功能:

add_executable(Test
    main.cc
    test1.cc
    test2.cc)
target_link_libraries(Test GTest)
Run Code Online (Sandbox Code Playgroud)

  • 你如何添加包含目录? (2认同)

Phi*_*hil 8

我的回答是基于firegurafiku的答案.我通过以下方式修改它:

  1. 添加CMAKE_ARGSExternalProject_Add调用中,因此它与msvc一起使用.
  2. 从文件位置获取gtest源而不是下载
  3. 添加了便携式(用于MSVC和非MSVC)定义和使用IMPORTED_LOCATION
  4. 解决了INTERFACE_INCLUDE_DIRECTORIES由于尚未构建外部项目而调用set_target_properties在配置时不工作的问题.

我更喜欢将gtest作为外部项目而不是将其源代码直接添加到我的项目中.一个原因是因为我不喜欢在搜索代码时包含gtest源代码.我的代码所需的任何特殊构建标志(也应该在构建gtest时使用)可以添加到CMAKE_ARGS调用中的定义中ExternalProject_Add

这是我修改过的方法:

include(ExternalProject)

# variables to help keep track of gtest paths
set(GTEST_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/gtest")
set(GTEST_LOCATION "${GTEST_PREFIX}/src/GTestExternal-build")
set(GTEST_INCLUDES "${GTEST_PREFIX}/src/GTestExternal/include")

# external project download and build (no install for gtest)
ExternalProject_Add(GTestExternal
    URL ${CMAKE_CURRENT_SOURCE_DIR}/../googletest
    PREFIX "${GTEST_PREFIX}"

    # cmake arguments
    CMAKE_ARGS -Dgtest_force_shared_crt=ON

    # Disable install step
    INSTALL_COMMAND ""

    # Wrap download, configure and build steps in a script to log output
    LOG_DOWNLOAD ON
    LOG_CONFIGURE ON
    LOG_BUILD ON
    )

# variables defining the import location properties for the generated gtest and
# gtestmain libraries
if (MSVC)
    set(GTEST_IMPORTED_LOCATION
        IMPORTED_LOCATION_DEBUG           "${GTEST_LOCATION}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}"
        IMPORTED_LOCATION_RELEASE         "${GTEST_LOCATION}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}"
        )
    set(GTESTMAIN_IMPORTED_LOCATION
        IMPORTED_LOCATION_DEBUG           "${GTEST_LOCATION}/Debug/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}"
        IMPORTED_LOCATION_RELEASE         "${GTEST_LOCATION}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}"
        )
else()
    set(GTEST_IMPORTED_LOCATION
        IMPORTED_LOCATION                 "${GTEST_LOCATION}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}")
    set(GTESTMAIN_IMPORTED_LOCATION
        IMPORTED_LOCATION                 "${GTEST_LOCATION}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}")
endif()

# the gtest include directory exists only after it is build, but it is used/needed
# for the set_target_properties call below, so make it to avoid an error
file(MAKE_DIRECTORY ${GTEST_INCLUDES})

# define imported library GTest
add_library(GTest IMPORTED STATIC GLOBAL)
set_target_properties(GTest PROPERTIES
    INTERFACE_INCLUDE_DIRECTORIES     "${GTEST_INCLUDES}"
    IMPORTED_LINK_INTERFACE_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}"
    ${GTEST_IMPORTED_LOCATION}
    )

# define imported library GTestMain
add_library(GTestMain IMPORTED STATIC GLOBAL)
set_target_properties(GTestMain PROPERTIES
    IMPORTED_LINK_INTERFACE_LIBRARIES GTest
    ${GTESTMAIN_IMPORTED_LOCATION}
    )

# make GTest depend on GTestExternal
add_dependencies(GTest GTestExternal)

#
# My targets
#

project(test_pipeline)
add_executable(${PROJECT_NAME} test_pipeline.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
target_link_libraries(${PROJECT_NAME} ${TBB_LIBRARIES})
target_link_libraries(${PROJECT_NAME} GTest)
Run Code Online (Sandbox Code Playgroud)


P S*_*ski 5

这个话题有点老了,但是出现了一种在 CMake 中包含外部库的新方法。

#Requires CMake 3.16+
include(FetchContent)

FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        release-1.8.0
)

FetchContent_MakeAvailable(googletest)
Run Code Online (Sandbox Code Playgroud)

如果您想支持早期版本的 cmake:

# Requires CMake 3.11+
include(FetchContent)

FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        release-1.8.0
)

FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
  FetchContent_Populate(googletest)
  add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
endif()
Run Code Online (Sandbox Code Playgroud)

然后你只需添加

enable_testing()

add_executable(test ${SOURCES} )

target_link_libraries(test gtest_main ${YOUR_LIBS})

add_test(NAME tests COMMAND test)
Run Code Online (Sandbox Code Playgroud)

进一步阅读: https: //cmake.org/cmake/help/latest/module/FetchContent.html