Добавить внешний исходный каталог в сборку CMake

Я использую Google Mock для своего проекта, и в инструкциях говорится, что лучше собирать библиотеку вместе с проектом, потому что разные флаги компилятора могут привести к ошибкам. Я не хочу добавлять каталог gmock/ в свой репозиторий; Я бы предпочел, чтобы источники были внешней зависимостью и подключались к моему процессу сборки. Что подводит меня к моему вопросу: как я могу указать CMake вытащить внешний исходный каталог в сборку (т.е. построить его в каталоге сборки моего проекта)? Я нашел похожий вопрос здесь, но ответы требуют жесткого размещения каталога, и я бы предпочел, чтобы это настраивалось. Есть ли другой способ сделать это?


person Yaron Tausky    schedule 12.08.2012    source источник


Ответы (2)


Для этого можно использовать ExternalProject_Add, поскольку он позволяет загружать, настраивать и собирать gmock из дерева сборки вашего проекта, а затем ссылаться на библиотеки gmock.

Однако ответ @arrowdodger, вероятно, является более обычным способом сделать это. Используя его метод, у вас обычно нет исходников gmock в дереве сборки. Это может быть хорошо или плохо, в зависимости от того, чего вы хотите.

Используя ExternalProject_Add, исходники gmock извлекаются (обновление svn) каждый раз, когда вы создаете gmock или зависимую цель. Это делает сборку немного медленнее, но явно поддерживает актуальность исходных кодов и удобна (на одну зависимость меньше, чтобы разработчики устанавливали). Однако для такого проекта, как gmock, который меняется нечасто, накладные расходы на постоянное обновление могут быть для вас слишком большими.

Следующий файл CMakeLists.txt работает с Visual Studio 2010 и 2012 — возможно, его нужно настроить для других платформ. В частности, в настоящее время gtest не может создать коробку с помощью Visual Studio 2012 (см. этот отчет об ошибке), следовательно, файл исправления и PATCH_COMMAND в вызове ExternalProject_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})

Если вы создадите это как CMakeLists.txt в пустом каталоге (скажем, MyTest), то:

cd MyTest
mkdir build
cd build
cmake ..

Это должно создать базовый файл main.cpp в MyTest/src и создать файл проекта (MyTest/build/Test.sln в Windows).

Когда вы создаете проект, он должен загрузить исходники gmock в MyTest/build/ThirdParty/src/googlemock и собрать их в MyTest/build/ThirdParty/src/googlemock-build. После этого вы сможете успешно запустить цель MainTest.

Дополнительные сведения о команде ExternalProject_Add см. в этой статье, озаглавленной Создание внешних проектов с помощью CMake 2.8.

Вот суть, содержащая этот CMakeLists.txt

person Fraser    schedule 12.08.2012
comment
CMake игнорирует CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG и CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE при сборке googlemock. Я попытался исправить CMakeLists.txt cmake_minimum_required Googlemock до cmake_minimum_required(VERSION 2.8.10). Это все еще не имеет никакого эффекта. Я не думаю, что возможно иметь отдельные отладочные и выпускные/оптимизированные сборки googletest или googlemock. Удалите части debug и DebugLibs (и всю строку optimized), и это будет построено. - person gotgenes; 06.03.2013
comment
Какая платформа? Это работает, как описано выше, с отдельными выходными папками в Windows с VC++ 2010 и с VC++ 2012 с парой незначительных исправлений, добавленных в gtest. - person Fraser; 07.03.2013
comment
Я еще не пробовал обновленную версию, но хотел сказать спасибо даже за ответ, а также за то, что платформа, на которой я строю, — это Mac OS X 10.7.5, использующий CMake 2.8.10, компиляторы clang по умолчанию и лязг++. Как вы думаете, повлияла ли на это платформа? В итоге я использовал решение, похожее на гибрид вашего и этого gist.github.com/oneamtu/3734295, который можно увидеть по адресу github.com/gotgenes/ gtestctest-sandbox/blob/ - person gotgenes; 07.03.2013
comment
@gotgenes - я думаю, проблема в платформе. У меня сейчас нет доступа к Mac, но мне интересно, сработает ли замена частей ...ARCHIVE_OUTPUT_DIRECTORY... на ...LIBRARY_OUTPUT_DIRECTORY...? Также можно использовать CMAKE_<CONFIG>_POSTFIX, например -DCMAKE_DEBUG_POSTFIX=-d - person Fraser; 08.03.2013

Я вижу, что 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()
person arrowd    schedule 12.08.2012
comment
+1 Хотя вам все равно понадобится пара include_directories вызовов для ${GOOGLE_MOCK_SOURCE_DIR}/googlemock/include и ${GOOGLE_MOCK_SOURCE_DIR}/googlemock/gtest/include, я думаю. - person Fraser; 12.08.2012
comment
Кроме того, это на самом деле не втягивает внешний исходный каталог в сборку, но это, возможно, не важно. - person Fraser; 12.08.2012
comment
Это не сработает, если исходный каталог не находится внутри моего дерева (это не так). Это первое, что я попробовал. :-) - person Yaron Tausky; 12.08.2012
comment
Вы можете указать двоичный каталог, см. cmake.org/cmake/help /v2.8.8/cmake.html#command:добавить_подкаталог - person arrowd; 12.08.2012