CMake + GoogleTest

Ho appena scaricato googletest, ho generato il suo makefile con CMake e l’ho creato. Ora, ho bisogno di usarlo nel mio progetto di test.

Con CMake, mi è stato consigliato di non puntare direttamente alle librerie gtest (usando include _directories o link_directories ), ma invece usare find_package() .

Il problema è che non esiste una destinazione di installazione per il makefile gtest generato. Non riesco a capire come find_package(GTest REQUIRED) possa funzionare senza alcun tipo di installazione. Inoltre, non è ansible inserire la cartella gtest come sottocartella nel mio progetto.

Grazie per qualsiasi aiuto.

    Questo è un caso insolito; la maggior parte dei progetti specifica le regole di installazione.

    Il modulo ExternalProject_Add di CMake è forse lo strumento migliore per questo lavoro. Questo ti permette di scaricare, configurare e compilare gtest dal tuo progetto, e quindi collegarti alle librerie gtest.

    Ho testato il seguente CMakeLists.txt su Windows con Visual Studio 10 e 11 e su Ubuntu con GCC 4.8 e Clang 3.2 – potrebbe essere necessario aggiustarlo per altre piattaforms / compilatori:

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

    Se lo crei come CMakeLists.txt in una directory vuota (ad esempio MyTest ), allora:

     cd MyTest mkdir build cd build cmake .. 

    Questo dovrebbe creare un main.cpp di base in MyTest/src e creare un file di progetto ( MyTest/build/Test.sln su Windows)

    Quando si genera il progetto, è necessario scaricare i sorgenti gtest su MyTest/build/ThirdParty/src/googletest e crearli in MyTest/build/ThirdParty/src/googletest-build . Dovresti quindi essere in grado di eseguire correttamente l’objective MainTest.

    È passato molto tempo quando è stata posta la domanda originale, ma a beneficio degli altri, è ansible utilizzare ExternalProject per scaricare la sorgente gtest e quindi utilizzare add_subdirectory() per aggiungerla alla build. Questo ha i seguenti vantaggi:

    • gtest è costruito come parte della tua build principale, quindi usa gli stessi flag del compilatore, ecc. e non ha bisogno di essere installato da nessuna parte.
    • Non c’è bisogno di aggiungere le fonti gtest al proprio albero dei sorgenti.

    Utilizzato nel modo normale, ExternalProject non eseguirà il download e il disimballaggio al momento della configurazione (ovvero quando viene eseguito CMake), ma è ansible farlo. Ho scritto un post sul blog su come fare ciò che include anche un’implementazione generalizzata che funziona per qualsiasi progetto esterno che usa CMake come sistema di compilazione, non solo gtest. Potete trovare qui:

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

    Aggiornamento: l’approccio sopra descritto ora fa anche parte della documentazione più oculata .

    C’è una soluzione un po ‘meno complessa usando il modulo ExternalProject e la funzionalità delle librerie importate di cmake . libgtest_main.a il codice dal repository, lo crea e crea il target da librerie statiche costruite (sono libgtest.a e libgtest_main.a sul mio sistema).

     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) 

    Potresti voler sostituire SVN_REVISION o aggiungere le opzioni LOG_CONFIGURE e LOG_BUILD qui. Dopo aver GTest GTestMain obiettivi GTest e GTestMain , possono essere utilizzati in questo modo:

     add_executable(Test test1.cc test2.cc) target_link_libraries(Test GTestMain) 

    oppure, se hai la tua funzione main() :

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

    La mia risposta è basata sulla risposta di firegurafiku . L’ho modificato nei seguenti modi:

    1. aggiunto CMAKE_ARGS alla chiamata ExternalProject_Add modo che funzioni con msvc.
    2. ottiene la fonte gtest da un percorso file piuttosto che il download
    3. aggiunta di definizioni portatili (per MSVC e non MSVC) e utilizzo di IMPORTED_LOCATION
    4. È stato risolto il problema con la chiamata a set_target_properties che non funziona al momento della configurazione quando INTERFACE_INCLUDE_DIRECTORIES non esiste ancora perché il progetto esterno non è stato ancora creato.

    Preferisco mantenere gtest come un progetto esterno piuttosto che aggiungere la sua fonte direttamente al mio progetto. Una ragione è perché non mi piace avere il codice sorgente gtest incluso quando cerco il mio codice. Eventuali flag di compilazione speciali necessari per il mio codice che dovrebbero essere utilizzati anche durante la creazione di gtest possono essere aggiunti alla definizione di CMAKE_ARGS nella chiamata a ExternalProject_Add

    Ecco il mio approccio modificato:

     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)