I'm using Google Mock for my project, and the instructions say it's better to build the library along with the project, because different compiler flags can introduce bugs. I don't want to add the gmock/ directory to my repository; I'd rather have the sources as an external dependency and plug it into my build process. Which brings me to my question: how can I instruct CMake to pull the external source directory into the build (i.e. build it in my project's build directory)? I found a similar question here, but the answers requires a rigid directory placement, and I'd rather have that configurable. Any other way to do it?
You could use ExternalProject_Add
for this, since it lets you to download, configure and build gmock from within your project's build tree, and then link to the gmock libraries.
@arrowdodger's answer is probably the more usual way to go about this however. Using his method, you don't normally have the gmock sources in your build tree. This could be good or bad depending on what you want.
Using ExternalProject_Add
, the gmock sources are pulled (svn update) every time you build gmock or a dependent target. This makes the build slightly slower, but clearly keeps the sources up to date and is convenient (it's one less dependency to have developers install). For a project like gmock which doesn't change often, the overhead of updating all the time may be too much for you though.
The following CMakeLists.txt works using Visual Studio 2010 & 2012 - it might need adjusted for other platforms. In particular, gtest currently fails to build out the box using Visual Studio 2012 (see this bug report), hence the patch file and PATCH_COMMAND
in the ExternalProject_Add
call.
cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR)
project(Test)
# Create main.cpp which uses gmock
file(WRITE src/main.cpp "#include \"gmock/gmock.h\"\n\n")
file(APPEND src/main.cpp "struct A {\n virtual void Do() {}\n};\n\n")
file(APPEND src/main.cpp "struct MockA : public A {\n MOCK_METHOD0(Do, void());\n};\n\n")
file(APPEND src/main.cpp "TEST(A, Do) {\n")
file(APPEND src/main.cpp " MockA mock_a;\n")
file(APPEND src/main.cpp " EXPECT_CALL(mock_a, Do()).Times(testing::AtLeast(1));\n")
file(APPEND src/main.cpp " mock_a.Do();\n}\n\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 643)\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")
file(APPEND gtest.patch "Index: include/gtest/internal/gtest-tuple.h\n")
file(APPEND gtest.patch "===================================================================\n")
file(APPEND gtest.patch "--- include/gtest/internal/gtest-tuple.h (revision 643)\n")
file(APPEND gtest.patch "+++ include/gtest/internal/gtest-tuple.h (working copy)\n")
file(APPEND gtest.patch "@@ -1,3 +1,4 @@\n")
file(APPEND gtest.patch "+#include <tuple> /*\n")
file(APPEND gtest.patch " // This file was GENERATED by command:\n")
file(APPEND gtest.patch " // pump.py gtest-tuple.h.pump\n")
file(APPEND gtest.patch " // DO NOT EDIT BY HAND!!!\n")
file(APPEND gtest.patch "@@ -197,8 +198,8 @@\n")
file(APPEND gtest.patch " class tuple<> {\n")
file(APPEND gtest.patch " public:\n")
file(APPEND gtest.patch " tuple() {}\n")
file(APPEND gtest.patch "- tuple(const tuple& /* t */) {}\n")
file(APPEND gtest.patch "- tuple& operator=(const tuple& /* t */) { return *this; }\n")
file(APPEND gtest.patch "+ tuple(const tuple& t) {}\n")
file(APPEND gtest.patch "+ tuple& operator=(const tuple&) { return *this; }\n")
file(APPEND gtest.patch " };\n")
file(APPEND gtest.patch " \n")
file(APPEND gtest.patch " template <GTEST_1_TYPENAMES_(T)>\n")
file(APPEND gtest.patch "@@ -946,7 +947,7 @@\n")
file(APPEND gtest.patch " template <>\n")
file(APPEND gtest.patch " struct SameSizeTuplePrefixComparator<0, 0> {\n")
file(APPEND gtest.patch " template <class Tuple1, class Tuple2>\n")
file(APPEND gtest.patch "- static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) {\n")
file(APPEND gtest.patch "+ static bool Eq(const Tuple1&, const Tuple2&) {\n")
file(APPEND gtest.patch " return true;\n")
file(APPEND gtest.patch " }\n")
file(APPEND gtest.patch " };\n")
else()
file(WRITE gtest.patch "")
endif()
# Enable ExternalProject CMake module
include(ExternalProject)
# Set default ExternalProject root directory
set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/ThirdParty)
# Add gmock
ExternalProject_Add(
googlemock
SVN_REPOSITORY http://googlemock.googlecode.com/svn/trunk/
TIMEOUT 30
PATCH_COMMAND svn patch ${CMAKE_SOURCE_DIR}/gtest.patch ${CMAKE_BINARY_DIR}/ThirdParty/src/googlemock/gtest
# 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_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 for googlemock and googletest
ExternalProject_Get_Property(googlemock source_dir)
include_directories(${source_dir}/include)
include_directories(${source_dir}/gtest/include)
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 googlemock
add_dependencies(MainTest googlemock)
# Specify MainTest's link libraries
ExternalProject_Get_Property(googlemock binary_dir)
target_link_libraries(MainTest
debug ${binary_dir}/DebugLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gmock${CMAKE_FIND_LIBRARY_SUFFIXES}
optimized ${binary_dir}/ReleaseLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gmock${CMAKE_FIND_LIBRARY_SUFFIXES})
If you create this as CMakeLists.txt in an empty directory (say MyTest), then:
cd MyTest
mkdir build
cd build
cmake ..
This should create a basic main.cpp in MyTest/src and create a project file (MyTest/build/Test.sln on Windows)
When you build the project, it should download the gmock sources to MyTest/build/ThirdParty/src/googlemock, and build them in MyTest/build/ThirdParty/src/googlemock-build. You should then be able to run the MainTest target successfully.
For further info on the ExternalProject_Add
command, see this article entitled Building External Projects with CMake 2.8
Here's a gist containing this CMakeLists.txt