web-dev-qa-db-fra.com

CMake + GoogleTest

Je viens de télécharger googletest, j'ai généré son makefile avec CMake et je l'ai construit. Maintenant, je dois l'utiliser dans mon projet de test.

Avec CMake, il m'a été conseillé de ne pas pointer directement vers les bibliothèques gtest (en utilisant include _directories Ou link_directories) Mais d'utiliser find_package() à la place.

Le problème est qu'il n'y a pas de cible d'installation pour le makefile gtest généré. Je ne peux pas comprendre comment find_package(GTest REQUIRED) pourrait fonctionner sans une sorte d'installation. De plus, mettre le dossier gtest en tant que sous-dossier dans mon projet n'est pas possible.

Merci pour toute aide.

47
Korchkidu

Il s'agit d'un cas inhabituel; la plupart des projets spécifient des règles d'installation.

Le module ExternalProject_Add De CMake est peut-être le meilleur outil pour ce travail. Cela vous permet de télécharger, de configurer et de construire gtest à partir de votre projet, puis de créer un lien vers les bibliothèques gtest.

J'ai testé le CMakeLists.txt suivant sur Windows avec Visual Studio 10 et 11, et sur Ubuntu en utilisant GCC 4.8 et Clang 3.2 - il pourrait avoir besoin d'être ajusté pour d'autres plates-formes/compilateurs:

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

Si vous créez ceci en tant que CMakeLists.txt dans un répertoire vide (par exemple MyTest), alors:

cd MyTest
mkdir build
cd build
cmake ..

Cela devrait créer un fichier main.cpp de base dans MyTest/src Et créer un fichier de projet (MyTest/build/Test.sln Sous Windows)

Lorsque vous générez le projet, il doit télécharger les sources gtest dans MyTest/build/ThirdParty/src/googletest Et les générer dans MyTest/build/ThirdParty/src/googletest-build. Vous devriez alors pouvoir exécuter la cible MainTest avec succès.

64
Fraser

Cela fait longtemps que la question d'origine n'est pas posée, mais pour le bien des autres, il est possible d'utiliser ExternalProject pour télécharger la source gtest, puis d'utiliser add_subdirectory() pour l'ajouter à votre build . Cela présente les avantages suivants:

  • gtest est construit dans le cadre de votre build principal, il utilise donc les mêmes drapeaux de compilateur, etc. et n'a pas besoin d'être installé n'importe où.
  • Il n'est pas nécessaire d'ajouter les sources gtest à votre propre arborescence de sources.

Utilisé de manière normale, ExternalProject ne fera pas le téléchargement et le déballage au moment de la configuration (c'est-à-dire lorsque CMake est exécuté), mais vous pouvez le faire faire. J'ai écrit un article de blog sur la façon de procéder, qui comprend également une implémentation généralisée qui fonctionne pour tout projet externe qui utilise CMake comme système de construction, pas seulement gtest. Vous pouvez le trouver ici:

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

Mise à jour: L'approche décrite ci-dessus est désormais également partie de la documentation googletest .

28
Craig Scott

Ma réponse est basée sur la réponse de firegurafik . Je l'ai modifié de la manière suivante:

  1. ajoutée CMAKE_ARGS à la ExternalProject_Add call donc ça marche avec msvc.
  2. obtient la source gtest à partir d'un emplacement de fichier plutôt que de télécharger
  3. ajout de la définition et de l'utilisation portables (pour MSVC et non-MSVC) de IMPORTED_LOCATION
  4. Résolution du problème avec l'appel à set_target_properties ne fonctionnant pas au moment de la configuration lorsque le INTERFACE_INCLUDE_DIRECTORIES n'existe pas encore car le projet externe n'a pas encore été construit.

Je préfère garder gtest en tant que projet externe plutôt que d'ajouter sa source directement à mon projet. L'une des raisons est que je n'aime pas avoir le code source gtest inclus lorsque je recherche mon code. Tout indicateur de build spécial dont mon code a besoin et qui devrait également être utilisé lors de la construction de gtest peut être ajouté à la définition de CMAKE_ARGS dans l'appel à ExternalProject_Add

Voici mon approche modifiée:

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)
8
Phil

Il existe une solution un peu moins complexe utilisant le module ExternalProject et la fonctionnalité de bibliothèques importées de cmake. Il extrait le code du référentiel, le construit et crée une cible à partir des bibliothèques statiques construites (elles sont libgtest.a Et libgtest_main.a Sur mon système).

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)

Vous pouvez remplacer SVN_REVISION Ou ajouter les options LOG_CONFIGURE Et LOG_BUILD Ici. Une fois les cibles GTest et GTestMain créées, elles peuvent être utilisées comme suit:

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

ou, si vous avez votre propre fonction main():

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

Lorsque vous obtenez le libgtest-dev package via

Sudo apt install libgtest-dev

La source est stockée dans l'emplacement /usr/src/googletest

Vous pouvez simplement pointer votre CMakeLists.txt dans ce répertoire afin qu'il puisse trouver les dépendances nécessaires

Quelque chose comme ce qui suit

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