CMake объявляет зависимость функции от ExternalProject_Add

TLDR:

Моя проблема в том, что CMake начинает выполнять эту функцию до загрузки репозитория. Я хотел бы объявить зависимость для этой функции от ExternalProject_Add, чтобы CMake понимал, что он должен загрузить, построить и затем запустить функцию.

Контекст:

У меня есть модуль cmake SomeModule.cmake, который должен добавить flatbuffers в качестве внешнего проекта из своего репозитория и построить его. Сборка создаст исполняемый файл компилятора flatbuffers, который я намереваюсь использовать в файле some/directory/CMakeLists.txt для создания файлов заголовков C++ из схемы flatbuffers. Итак, в том же модуле CMake, который я использую ExternalProject_Add, я объявил функцию CMake, которая генерирует файлы заголовков из заданного набора файлов схемы, и где-то в some/directory/CMakeLists.txt я вызываю эту функцию.

Моя проблема в том, что CMake начинает выполнять эту функцию до загрузки репозитория. Я хотел бы объявить зависимость для этой функции от ExternalProject_Add, чтобы CMake понимал, что он должен загрузить, построить и затем запустить функцию.

Хватит говорить. Вот соответствующие части кода:

SomeModule.cmake:

include(ExternalProject)

set(flatbuffers_CMAKE_ARGS
    "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
    "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
    "-DFLATBUFFERS_BUILD_TESTS=OFF"
    "-DFLATBUFFERS_BUILD_FLATC=ON"
    "-DFLATBUFFERS_BUILD_FLATHASH=OFF"
    "-DCMAKE_INSTALL_PREFIX=${OTS_DEPENDENCIES}"
)

ExternalProject_Add(
    flatbuffers
    GIT_REPOSITORY  "https://github.com/google/flatbuffers.git"
    GIT_TAG         "v1.9.0"
    SOURCE_DIR      "${OTS_DEPDENDENCIES_DIR}/flatbuffers"
    BINARY_DIR      "${OTS_DEPDENDENCIES_DIR}/flatbuffers"
    CMAKE_ARGS      "${flatbuffers_CMAKE_ARGS}"
    INSTALL_COMMAND ""
)

ExternalProject_Get_Property(flatbuffers SOURCE_DIR)
ExternalProject_Get_Property(flatbuffers BINARY_DIR)
set(flatbuffers_SOURCE_DIR ${SOURCE_DIR})
set(flatbuffers_BINARY_DIR ${BINARY_DIR})
set(flatbuffers_INCLUDE_DIR ${flatbuffers_SOURCE_DIR}/include)
set(flatbuffers_FLATC_EXECUTABLE ${flatbuffers_BINARY_DIR}/flatc)
# please assume that the variables above are all set to appropriate values

function(FlatbuffersGenerateCpp SCHEMA_FILES GENERATED_DIR GENERATED_CXX)
    foreach(SCHEMA_FILE ${SCHEMA_FILES})
        get_filename_component(NAME ${SCHEMA_FILE} NAME_WE)
        set(GENERATED_HEADER_FILE_PATH ${GENERATED_DIR}/${NAME}_generated.h)
        message(STATUS "attempting to generate: ${GENERATED_HEADER_FILE_PATH}")
        add_custom_command(
            DEPENDS ${flatbuffers_FLATC_EXECUTABLE}
            OUTPUT ${GENERATED_HEADER_FILE_PATH}
            COMMAND ${flatbuffers_FLATC_EXECUTABLE} -o ${GENERATED_DIR} -c ${SCHEMA_FILE}
            COMMENT "generating flatbuffers c++ header file: ${GENERATED_HEADER_FILE_PATH}"
        )
        list(APPEND GENERATED_FILES ${GENERATED_HEADER_FILE_PATH})
    endforeach()
    message(STATUS "generated c++ header files: ${GENERATED_FILES}")
    set(${GENERATED_CXX} ${GENERATED_FILES} PARENT_SCOPE)
endfunction()

И some/directory/CMakeLists.txt:

# cmake module path is properly set so the following works:
include(SomeModule)

set(flatbuffers_GENERATED_INCLUDES_DIR
    ${CMAKE_BINARY_DIR}/generated/config/flatbuffers
)

FlatbuffersGenerateCpp(
    "${flatbuffers_SCHEMAS}"
    "${flatbuffers_GENERATED_INCLUDES_DIR}"
    flatbuffers_GENERATED_CXX
)

add_library(
    my_framework
SHARED
    ${THE_PUBLIC_HEADER_FILES}
    ${THE_IMPL_SOURCE_FILES}
    ${THE_IMPL_HEADER_FILES}
    ${flatbuffers_GENERATED_CXX}
)

add_dependencies(my_framework flatbuffers ${flatbuffers_GENERATED_CXX})

target_include_directories(my_framework PRIVATE ${flatbuffers_INCLUDE_DIR})
target_include_directories(my_framework PRIVATE ${CMAKE_SOURCE_DIR})
target_include_directories(my_framework PRIVATE ${CMAKE_BINARY_DIR}/generated)
set_source_files_properties(${flatbuffers_GENERATED_CXX} PROPERTIES GENERATED TRUE)

person Pejman    schedule 12.07.2018    source источник
comment
Функции CMake выполняются на этапе configure, поэтому вам также необходимо собрать внешний проект на этапе настройки. (На этом этапе нет никаких зависимостей - скрипты CMake обрабатываются последовательно, строка за строкой). См. этот вопрос о создании внешнего проекта на этапе настройки. .   -  person Tsyvarev    schedule 12.07.2018
comment
Спасибо за ваш комментарий. Вы всегда полезны. :) Честно говоря, я не знал о разнице между этапами настройки и сборки в CMake. Я бы начал изучать упомянутый вопрос.   -  person Pejman    schedule 12.07.2018


Ответы (1)


Я начал изменять свой код на основе комментария, опубликованного Цыварева:

Функции CMake выполняются на этапе настройки, поэтому вам также необходимо собрать внешний проект на этапе настройки.

Хотя я верил, что предложенное им решение сработает, мне было немного неудобно, и я продолжал думать, что должно быть более элегантное решение. Я посоветовался с коллегой и нашел лучшее решение, которое так же просто, как следующий diff (который удаляет ${flatbuffers_GENERATED_CXX}).

- add_dependencies(my_framework flatbuffers ${flatbuffers_GENERATED_CXX})
+ add_dependencies(my_framework flatbuffers)

мы рассмотрели, что проблема с рассматриваемым кодом заключается в том, что CMake считывает add_dependencies(my_framework flatbuffers ${flatbuffers_GENERATED_CXX}) и понимает, что ему нужно ${flatbuffers_GENERATED_CXX} в качестве цели для сборки my_framework, поэтому он продолжает выполнение функции. Но он никак не может понять, что функция зависит от внешнего проекта. Теперь, если мы удалим явное объявление зависимости ${flatbuffers_GENERATED_CXX}, CMake отложит запуск функции до разрешения других зависимостей (цель flatbuffers), которые эффективно загрузят и создадут внешний проект до запуска проекта.

person Pejman    schedule 12.07.2018
comment
У этого решения есть небольшой недостаток, заключающийся в том, что мы удаляем явное объявление зависимости, которое (я думаю) всегда улучшает читабельность. - person Pejman; 12.07.2018
comment
Хм, кажется, проблема, с которой CMake начинает выполнять эту функцию до загрузки репозитория, не та, с которой вы столкнулись на самом деле: можно запустить функцию, которая создает правила (add_custom_command) для создания файлы, без загрузки проекта flatbuffers. Что вам нужно, так это загрузить flatbuffers до того, как add_custom_command действительно создаст файлы. И это этап сборки, а не настройки. В целом код был правильным: вы создаете цель, которая зависит как от flatbuffers, так и от файлов, генерируемых командой. - person Tsyvarev; 12.07.2018
comment
Обратите внимание, что add_dependencies предназначен только для целей CMake; он не работает с обычными файлами. Так что в любом случае было неправильно использовать ${flatbuffers_GENERATED_CXX} с этой командой CMake. - person Tsyvarev; 12.07.2018