5

我正在为我的项目使用 Google Mock,并且说明说最好与项目一起构建库,因为不同的编译器标志可能会引入错误。我不想将 gmock/ 目录添加到我的存储库;我宁愿将源作为外部依赖项并将其插入我的构建过程。这让我想到了我的问题:我如何指示 CMake 将外部源目录拉到构建中(即在我的项目的构建目录中构建它)?我在这里发现了一个类似的问题,但答案需要一个严格的目录放置,我宁愿有那个可配置的。还有其他方法吗?

4

2 回答 2

7

您可以使用ExternalProject_Add它,因为它允许您从项目的构建树中下载、配置和构建 gmock,然后链接到 gmock 库。

@arrowdodger 的答案可能是解决这个问题的更常见的方法。使用他的方法,您通常不会在构建树中拥有 gmock 源。这可能是好是坏取决于你想要什么。

使用ExternalProject_Add,每次构建 gmock 或依赖目标时都会拉取 gmock 源(svn 更新)。这使构建稍微慢了一点,但显然使源代码保持最新并且很方便(让开发人员安装它的依赖性减少了)。对于像 gmock 这样不经常更改的项目,一直更新的开销对您来说可能太多了。

以下 CMakeLists.txt 使用 Visual Studio 2010 和 2012 工作 - 它可能需要针对其他平台进行调整。特别是,gtest 目前无法使用 Visual Studio 2012 构建盒子(请参阅此错误报告),因此补丁文件和调用中PATCH_COMMANDExternalProject_Add

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

如果您在一个空目录(例如 MyTest)中将其创建为 CMakeLists.txt,则:

cd MyTest
mkdir build
cd build
cmake ..

这应该在 MyTest/src 中创建一个基本的 main.cpp 并创建一个项目文件(Windows 上的 MyTest/build/Test.sln)

构建项目时,它应该将 gmock 源下载到 MyTest/build/ThirdParty/src/googlemock,并在 MyTest/build/ThirdParty/src/googlemock-build 中构建它们。然后,您应该能够成功运行 MainTest 目标。

有关该ExternalProject_Add命令的更多信息,请参阅这篇题为“使用 CMake 2.8 构建外部项目”的文章

这是包含此 CMakeLists.txt的要点

于 2012-08-12T10:54:34.127 回答
6

我看到 Google Mock 支持 CMake。在这种情况下,您只需添加此行

add_subdirectory(${GOOGLE_MOCK_SOURCE_DIR})

进入根目录CMakeLists.txt并让用户GOOGLE_MOCK_SOURCE_DIR在配置步骤中设置变量:

set(GOOGLE_MOCK_SOURCE_DIR "" CACHE PATH "Path to the GMock source")
if(NOT ${GOOGLE_MOCK_SOURCE_DIR} OR NOT EXISTS "${GOOGLE_MOCK_SOURCE_DIR}/CMakeLists.txt")
  message(FATAL_ERROR "GOOGLE_MOCK_SOURCE_DIR isn't set properly!")
endif()
于 2012-08-12T09:44:10.047 回答