CMake+;谷歌测试

CMake+;谷歌测试,cmake,googletest,Cmake,Googletest,我刚刚下载了googletest,用CMake生成了它的makefile并构建了它。现在,我需要在我的测试项目中使用它 有了CMake,我被建议不要直接指向gtest库(使用include\u目录或link\u目录),而是使用find\u package() 问题是,生成的gtest makefile没有安装目标。我无法理解find_包(需要GTest)在没有某种安装的情况下如何工作。此外,将gtest文件夹作为子文件夹放在我的项目中是不可能的 谢谢你的帮助。这是一个不寻常的案例;大多数项目都指

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

有了CMake,我被建议不要直接指向gtest库(使用
include\u目录
link\u目录
),而是使用
find\u package()

问题是,生成的gtest makefile没有安装目标。我无法理解
find_包(需要GTest)
在没有某种安装的情况下如何工作。此外,将gtest文件夹作为子文件夹放在我的项目中是不可能的


谢谢你的帮助。

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

CMake的
ExternalProject\u 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})
如果在一个空目录(例如
MyTest
)中创建此文件作为CMakeLists.txt,则:

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


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

使用
ExternalProject
模块和导入的库功能
cmake
有一个稍微不那么复杂的解决方案。它从存储库中检出代码,构建代码,并从构建的静态库中创建目标(在我的系统上它们是
libgtest.a
libgtest_main.a

您可能需要在此处替换
SVN\u修订版
或添加
LOG\u配置
LOG\u构建
选项。创建
GTest
GTestMain
目标后,它们可以如下使用:

add_executable(Test
    test1.cc
    test2.cc)
target_link_libraries(Test GTestMain)
或者,如果您有自己的
main()
函数:

add_executable(Test
    main.cc
    test1.cc
    test2.cc)
target_link_libraries(Test GTest)

我的答案是根据我的回答。我通过以下方式对其进行了修改:

  • CMAKE_ARGS
    添加到
    ExternalProject_Add
    调用中,使其与msvc一起工作
  • 从文件位置而不是下载位置获取gtest源
  • 添加了导入位置的可移植(适用于MSVC和非MSVC)定义和用法
  • 解决了当
    接口\u INCLUDE\u目录
    由于外部项目尚未生成而尚不存在时,在配置时调用set_target_属性不起作用的问题
  • 我更喜欢将gtest作为一个外部项目,而不是将其源代码直接添加到我的项目中。一个原因是我不喜欢在搜索代码时包含gtest源代码。可以在调用
    ExternalProject\u 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)
    

    提问原始问题已经很久了,但为了其他人的利益,可以使用
    ExternalProject
    下载gtest源代码,然后使用
    add_subdirectory()
    将其添加到构建中。这有以下优点:

    • gtest是作为主构建的一部分构建的,因此它使用相同的编译器标志等,不需要安装在任何地方
    • 没有必要将gtest源添加到您自己的源代码树中
    按照正常方式使用,ExternalProject不会在配置时(即运行CMake时)进行下载和解压缩,但您可以让它这样做。我已经写了一篇关于如何做到这一点的博客文章,其中还包括一个通用的实现,它适用于任何使用CMake作为其构建系统的外部项目,而不仅仅是gtest。你可以在这里找到它:


    更新:上述方法现在也适用。

    当您通过

    sudo apt install libgtest-dev
    
    源代码存储在位置
    /usr/src/googletest

    您只需将
    CMakeLists.txt
    指向该目录,即可找到必要的依赖项

    类似于下面的内容

    add_subdirectory(/usr/src/googletest gtest)
    target_link_libraries(your_executable gtest)
    

    这个主题有点陈旧,但是出现了一种在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)
    
    如果要支持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()
    
    那你就加上

    enable_testing()
    
    add_executable(test ${SOURCES} )
    
    target_link_libraries(test gtest_main ${YOUR_LIBS})
    
    add_test(NAME tests COMMAND test)
    

    进一步阅读:

    对我来说似乎很复杂。是否无法直接引用google test CMakeLists.txt文件?或者我应该将googletest库添加为子目录吗?我不认为这比将googletest添加为子目录复杂得多,但如果您有选择的话,请确定-将googletest添加为源代码树的子目录,并通过
    add_subdirectory
    将其拉入。(你在最初的问题中明确指出这不是一个选项)。是的,确实如此。我想使用更好的解决方案。但子目录似乎比您提供的解决方案更好、更干净……但我只是从CMake world开始……;)我同意它可能稍微简单一点,但我不认为它更干净。使用
    ADD_子目录
    选项,您将在自己的源代码树中获得第三方源代码,而
    ExternalProject_ADD
    将第三方代码转储到您的(一次性)构建树中,使您的源代码树只保留您自己的源代码。不过,这取决于您——我并不强烈推荐一种方法胜过另一种方法。+1可以让您的第一个解决方案保持源代码树的整洁。我将调查这两种解决方案,看看哪一种最适合我。非常感谢你的帮助!如何添加include目录?@Jeff:updated a
    # 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()
    
    enable_testing()
    
    add_executable(test ${SOURCES} )
    
    target_link_libraries(test gtest_main ${YOUR_LIBS})
    
    add_test(NAME tests COMMAND test)