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.
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.
, 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.
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)
Ma réponse est basée sur la réponse à partir de firegurafiku. Je l'ai modifié de la manière suivante:
- Ajouté
CMAKE_ARGS
à l'appelExternalProject_Add
afin qu'il fonctionne avec msvc. - obtient la source gtest à partir d'un emplacement de fichier plutôt que de télécharger
- ajout de la définition portable (pour MSVC et non-MSVC) et de l'utilisation de IMPORTED_LOCATION
- 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)