2012-03-13 25 views
43

我剛剛下載了googletest,用CMake生成了它的makefile並構建它。現在,我需要在我的測試項目中使用它。CMake + GoogleTest

對於CMake,我被建議不要直接指向gtest庫(使用include _directorieslink_directories),而是使用find_package()來代替。

問題是,gtest makefile沒有生成安裝目標。我無法理解find_package(GTest REQUIRED)如何在沒有某種安裝的情況下工作。另外,將gtest文件夾作爲子文件夾放在我的項目中是不可能的。

感謝您的任何幫助。

回答

62

這是一個不尋常的案例;大多數項目指定安裝規則。

CMake的ExternalProject_Add模塊也許是這份工作的最佳工具。這允許您從項目中下載,配置和構建gtest,然後鏈接到gtest庫。

我已經測試了以下的CMakeLists.txt在Windows上使用Visual Studio 10和11,以及鏘3.2 Ubuntu的使用GCC 4.8和 - 它可能需要調整其他平臺/編譯器:

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}) 

如果在空目錄中創建這個作爲的CMakeLists.txt(比如MyTest),則:

cd MyTest 
mkdir build 
cd build 
cmake .. 

這應該MyTest/src創建一個基本的main.cpp和(在Windows MyTest/build/Test.sln)創建一個項目文件

當您構建項目時,它應該將gtest源下載到MyTest/build/ThirdParty/src/googletest,並在MyTest/build/ThirdParty/src/googletest-build中構建它們。然後,您應該能夠成功運行MainTest目標。

+3

對我來說似乎很複雜。有沒有辦法直接引用谷歌測試CMakeLists.txt文件?或者,我應該將googletest庫作爲子目錄添加嗎? – Korchkidu 2012-03-14 07:03:59

+3

我不認爲它比將googletest作爲子目錄添加得複雜得多,但如果您有選擇,那麼請確保 - 將googletest作爲源代碼樹的子目錄添加並通過「ADD_SUBDIRECTORY」將其拉入。 (你確實指出這不是你原來的問題中的一個選項)。 – Fraser 2012-03-14 10:31:44

+0

是的,確實如此。我想使用更好的解決方案。但是一個子目錄似乎比你提供的解決方案更好,更清潔......但是我只是從CMake世界開始......; – Korchkidu 2012-03-14 12:08:10

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) 

您可能需要更換SVN_REVISION或在此處添加LOG_CONFIGURELOG_BUILD選項。創建GTestGTestMain目標後,他們可以像這樣使用:

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) 
+2

如何添加包含目錄? – Jeff 2014-07-12 05:02:36

+1

@Jeff:更新了答案,對不起半年的延遲。請參閱目標的「INTERFACE_INCLUDE_DIRECTORIES」屬性。 – firegurafiku 2015-02-25 09:09:40

+0

我不得不刪除行INTERFACE_INCLUDE_DIRECTORIES $ {GTEST_INCLUDES}''爲了使這個工作與我自己的'main()'功能 – Twonky 2015-05-20 09:03:51

7

我的答案是基於firegurafiku答案。我修改了它在以下幾個方面:

  1. 添加CMAKE_ARGSExternalProject_Add調用,這樣它用MSVC工作。
  2. 會從一個文件位置GTEST源,而不是下載
  3. 增加便攜式(對於MSVC和非MSVC)定義和IMPORTED_LOCATION
  4. 使用解決的問題與調用set_target_properties在配置時,當不工作INTERFACE_INCLUDE_DIRECTORIES尚不存在,因爲外部項目尚未建立。

我更喜歡讓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) 
+0

感謝您對以前答案的重大更正。 順便說一句,當在Visual Studio中按下「重建」我的任何項目,與gtest鏈接它重新下載並重建gtest。這是一個正常的行爲?爲什麼要重建「外部項目」? – 2016-06-06 21:38:52

+1

有關CMake ExternalProject如何運行的詳細信息對我來說仍然有點神祕。我描述的方式,gtest已經是本地的,至少它不會重新下載。通常在Visual Studio中,重建不僅會重建選定的項目,還會重建它所依賴的其他項目。 cmake生成的解決方案/項目中必須有一些東西使ExternalProject添加目標成爲依賴項。因此gtest得到重建。爲了避免這種情況,您可以執行「僅限項目重建」,或者您可以轉至「生成」 - >「批量生成」並清理要重建的項目。 – Phil 2016-06-07 12:57:27

+0

感謝您的回答! 此外,如果你只是做了以上的事情,你的應用程序將不會使用默認設置在Visual Studio中生成(你會得到多個符號鏈接錯誤),因爲gtest庫使用/ MT運行時而不是默認/ MD 。以下是關於該問題的FAQ條目:https://github.com/google/googletest/blob/master/googletest/docs/FAQ.md#i-am-building-my-project-with-google-test-in- visual-studio-and-all-im-getting-is-a-bunch-of-linker-errors-or-warnings-help – 2016-06-07 20:51:39

21

這是很久以前,當提出原始問題時,爲了別人的利益,可以使用ExternalProject下載gtest源代碼,然後使用add_subdirectory()將其添加到您的版本中。這具有以下優點:

  • gtest作爲您的主構建的一部分而構建,因此它使用相同的編譯器標誌等,並且不需要在任何地方安裝。
  • 不需要將gtest源代碼添加到您自己的源代碼樹中。

以正常方式使用,ExternalProject不會在配置時(即運行CMake時)下載和解包,但您可以這樣做。我寫了一篇關於如何做到這一點的博客文章,其中還包括一個適用於任何使用CMake作爲構建系統的外部項目的通用實現,而不僅僅是gtest。你可以在這裏找到它:

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

更新:上述方法現在也part of the googletest documentation

+2

這個偉大的答案不應該在最底層! – user3667089 2015-11-20 17:29:35

+0

這確實是一個非常好的解決方案。 – JonesV 2016-04-27 06:48:25

+0

BRAVO! - 爲什麼這不是CMake內置的?你可以提交嗎? – Zak 2017-03-21 04:54:46