【问题标题】:Add an external source directory to a CMake build将外部源目录添加到 CMake 构建
【发布时间】:2012-08-12 09:15:52
【问题描述】:

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

【问题讨论】:

    标签: cmake gmock


    【解决方案1】:

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

    @arrowdodger 的answer 可能是解决此问题的更常用方法。使用他的方法,您的构建树中通常没有 gmock 源。这可能是好是坏取决于你想要什么。

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

    以下 CMakeLists.txt 使用 Visual Studio 2010 和 2012 工作 - 它可能需要针对其他平台进行调整。特别是,gtest 目前无法使用 Visual Studio 2012 构建盒子(请参阅此 bug report),因此补丁文件和 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 命令的更多信息,请参阅this article entitled Building External Projects with CMake 2.8

    这是包含此 CMakeLists.txt 的 a gist

    【讨论】:

    • CMake 在构建 googlemock 时会忽略 CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUGCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE。我尝试将 googlemock 的 CMakeLists.txt cmake_minimum_required 修补为 cmake_minimum_required(VERSION 2.8.10)。它仍然没有效果。我认为不可能有不同的调试和发布/优化的 googletest 或 googlemock 版本。删除 debugDebugLibs 部分(以及整个 optimized 行),这将构建。
    • 什么平台?这在 Windows 上使用 VC++ 2010 和 VC++ 2012 的单独输出文件夹时如上一样工作,并在 gtest 中添加了几个小补丁。
    • 我还没有尝试过更新版本,但我想说,感谢您的回复,而且我正在构建的平台是 Mac OS X 10.7.5,使用 CMake 2.8 .10,clang 和 clang++ 的默认编译器。您认为平台是否影响了这一点?我最终使用了一个看起来像你和这个 gist.github.com/oneamtu/3734295 混合的解决方案,你可以在 github.com/gotgenes/gtestctest-sandbox/blob/… 看到它
    • @gotgenes - 我猜是平台的问题。我现在无法使用 Mac,但我想知道将 ...ARCHIVE_OUTPUT_DIRECTORY... 部分交换为 ...LIBRARY_OUTPUT_DIRECTORY... 是否可行?还可以选择使用CMAKE_&lt;CONFIG&gt;_POSTFIX,例如-DCMAKE_DEBUG_POSTFIX=-d
    【解决方案2】:

    我看到 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()
    

    【讨论】:

    • +1 虽然我认为您仍然需要对${GOOGLE_MOCK_SOURCE_DIR}/googlemock/include${GOOGLE_MOCK_SOURCE_DIR}/googlemock/gtest/include 进行几次include_directories 调用。
    • 另外,这并不是真正“将外部源目录拉入构建”,但这可能并不重要。
    • 它不起作用,除非源目录在我的树中(它不是)。这是我尝试的第一件事。 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-07
    • 2014-07-27
    • 1970-01-01
    • 2017-05-07
    相关资源
    最近更新 更多