CMake + GoogleTest

Je viens de télécharger googletest, de générer son makefile avec CMake et de le construire. Maintenant, je dois l'utiliser dans mon projet de test.

Avec CMake, on m'a 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. En outre, mettre le dossier gtest en tant que sous-dossier dans mon projet n'est pas possible.

Merci pour toute aide.

46
demandé sur Korchkidu 2012-03-13 21:40:20

4 réponses

C'est 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, configurer et construire gtest à partir de votre projet, puis de créer un lien vers les bibliothèques gtest.

J'ai testé les CMakeLists suivantes.txt 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 comme CMakeLists.txt dans un répertoire vide (disons MyTest), puis:

cd MyTest
mkdir build
cd build
cmake ..

Cela devrait créer une base principale.rpc dans MyTest/src et créer un fichier de projet (MyTest/build/Test.sln sur Windows)

Lorsque vous construisez le projet, il doit télécharger les sources gtest dans MyTest/build/ThirdParty/src/googletest, et les construire dans MyTest/build/ThirdParty/src/googletest-build. Vous devriez alors être en mesure d'exécuter la cible MainTest avec succès.

63
répondu Fraser 2013-09-10 21:27:58

, Il est, depuis longtemps, lorsque la première question posée, mais pour le bénéfice des autres, il est possible d'utiliser ExternalProject pour télécharger le gtest source et ensuite utiliser add_subdirectory() pour l'ajouter à votre construction. Cela présente les avantages suivants:

  • gtest est construit dans le cadre de votre construction principale, donc il utilise les mêmes drapeaux du 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. J'ai écrit un article de blog sur la façon de faire cela qui inclut é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 maintenant également partie du googletest documentation.

26
répondu Craig Scott 2017-06-30 23:06:37

Il existe une solution un peu moins complexe utilisant le module ExternalProject et la fonctionnalité de bibliothèques importées de cmake. Il vérifie le code du référentiel, le construit et crée la 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 LOG_CONFIGURE et LOG_BUILD options ici. Après la création des cibles GTest et GTestMain, elles peuvent être utilisées comme ceci:

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
répondu firegurafiku 2015-02-25 09:08:07

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

  1. Ajouté CMAKE_ARGS à l'appel ExternalProject_Add afin qu'il fonctionne 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 portable (pour MSVC et non-MSVC) et de l'utilisation de IMPORTED_LOCATION
  4. a résolu le 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 comme un 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. Tous les drapeaux de construction spéciaux nécessaires à mon code qui devraient également être utilisés lors de la construction de gtest peuvent être ajoutés à 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
répondu Phil 2017-05-23 12:10:03