Использование шага загрузки ExternalProject с Ninja - PullRequest
0 голосов
/ 18 мая 2018

Кажется, это общая проблема без четкого ответа.

Ситуация такова: у нас есть сторонняя зависимость, которую мы хотим установить во время сборки при создании цели, которая зависит от нее.Это примерно:

ExternalProject_Add(target-ep
    DOWNLOAD_COMMAND <whatever>
    BUILD_COMMAND ""
    INSTALL_COMMAND ""
    CONFIGURE_COMMAND "")

add_library(target-imp STATIC IMPORTED)
set_target_properties(target-imp PROPERTIES
    INTERFACE_INCLUDE_DIRECTORIES /path/to/install/include
    IMPORTED_LOCATION /path/to/install/lib/libwhatever.a)

add_library(target INTERFACE)
target_link_libraries(target INTERFACE target-imp)
add_dependencies(target target-ep)

(здесь для танго нужны три из-за проблемы cmake 15052 )

При использовании в качестве генератора Unix Makefiles это прекрасно работает.Устанавливает зависимости только по требованию, все сборки работают корректно.

Однако в Ninja это происходит сразу же, например:

ninja: error: '/path/to/install/lib/libwhatever.a', needed by 'something', missing and no known rule to make it

Это происходит потому, что Ninja сканирует зависимости не так, как Make (см. выпуск ниндзя 760 ).Итак, что мы должны сделать, это на самом деле сказать ниндзя, что эта внешняя зависимость существует.Мы можем сделать это:

ExternalProject_Add(target-ep
    DOWNLOAD_COMMAND <whatever>
    BUILD_BYPRODUCTS /path/to/install/lib/libwhatever.a
    BUILD_COMMAND ""
    INSTALL_COMMAND ""
    CONFIGURE_COMMAND "")

Что, к сожалению, также не работает с:

No build step for 'target-ep'ninja: error: mkdir(/path/to/install): Permission denied

Это потому, что мой шаг загрузки имеет права на запись в этот путь, но любая команда mkdirпод управлением базового add_custom_command() с помощью ExternalProject_Add() нет.

Итак:

  1. Возможно ли это вообще с Ninja и CMake?(Версия не является проблемой, я могу использовать последний CMake, если это решает проблему)
  2. Если есть какой-то способ обойти с явным перечислением BUILD_BYPRODUCTS, есть ли способ просто сообщить, что весь каталогчто будет установлено, это побочный продукт?То есть /path/to/install/* является побочным продуктом?

Ответы [ 2 ]

0 голосов
/ 27 мая 2018

Скрытый mkdir шаг ExternalProject (от которого все другие шаги прямо или косвенно зависят) всегда пытается создать полный набор каталогов, даже если они не будут использоваться.Вы можете увидеть это здесь .Для справки, он делает это:

ExternalProject_Add_Step(${name} mkdir
  COMMENT "Creating directories for '${name}'"
  COMMAND ${CMAKE_COMMAND} -E make_directory ${source_dir}
  COMMAND ${CMAKE_COMMAND} -E make_directory ${binary_dir}
  COMMAND ${CMAKE_COMMAND} -E make_directory ${install_dir}
  COMMAND ${CMAKE_COMMAND} -E make_directory ${tmp_dir}
  COMMAND ${CMAKE_COMMAND} -E make_directory ${stamp_dir}${cfgdir}
  COMMAND ${CMAKE_COMMAND} -E make_directory ${download_dir}
  )

Местоположение установки по умолчанию в системах Unix, вероятно, будет /usr/local, поэтому, если у вас нет разрешения на запись во все каталоги, которые он пытается создать, то это может быть связано с вашей проблемой.Я предлагаю вам проверить разрешения каждого из этих мест и убедиться, что они уже существуют или доступны для записи.В качестве альтернативы вы можете указать каталог установки, который является локальным для дерева сборки, так что даже если он не будет использоваться, он, по крайней мере, всегда будет создаваться (см. Пример ниже).

Если вы используете ниндзя, он будет более строгим в проверке зависимостей, чем make.target-ep выполняет загрузку, которая обеспечивает libwhatever.a, поэтому вам нужно BUILD_BYPRODUCTS, чтобы сообщить Ninja, что target-ep - это то, что создает этот файл.Как вы узнали, если вы этого не сделаете, target-imp укажет на библиотеку, которая изначально не существует, и ниндзя справедливо жалуется на то, что она отсутствует, и не знает, как ее создать.Если вы предоставляете BUILD_BYPRODUCTS, то имеет смысл, что шаг сборки не должен быть пустым, поэтому вам, вероятно, нужно сделать что-то как шаг сборки, даже если это просто BUILD_COMMAND, который на самом деле ничего не делает значащим.

Следующее измененное определение target-ep должно заставить вас работать:

ExternalProject_Add(target-ep
    INSTALL_DIR ${CMAKE_CURRENT_BUILD_DIR}/dummyInstall
    DOWNLOAD_COMMAND <whatever>
    BUILD_BYPRODUCTS /path/to/install/lib/libwhatever.a
    BUILD_COMMAND ${CMAKE_COMMAND} -E echo_append
    INSTALL_COMMAND ""
    CONFIGURE_COMMAND "")

Ваш исходный вопрос также создает зависимость от неверной цели.target-imp должно зависеть от target-ep, но вместо этого target зависит от target-ep.Правильная зависимость может быть выражена следующим образом:

 add_dependencies(target-imp target-ep)

С опцией BUILD_BYPRODUCTS Ninja уже знает вышеуказанную зависимость, но она необходима для других генераторов, включая make.

YouЯ не указал, что делает ваша команда <whatever> download, но я предполагаю, что она отвечает за то, чтобы библиотека существовала на /path/to/install/lib/libwhatever.a, когда она будет выполнена.Вы также можете попробовать заполнить DOWNLOAD_COMMAND пустым и вместо него ввести <whatever> в качестве BUILD_COMMAND.

Чтобы ответить на ваши конкретные вопросы:

  1. Возможно ли это ввсе с ниндзя и CMake?(Версия не является проблемой, я могу использовать последний CMake, если это решит проблему)

Да, я проверил, что упомянутый выше подход работает с Ninja 1.8.2 для фиктивного тестапроект на macOS с использованием CMake 3.11.0.Я ожидаю, что он будет работать с CMake 3.2 или более поздней версии (именно тогда была добавлена ​​поддержка опции BUILD_BYPRODUCTS).

Если есть какой-то способ обхода с явным перечислением BUILD_BYPRODUCTS, есть ли способ просто сообщить, что весь каталог, который будет установлен, является побочным продуктом?То есть / path / to / install / * является побочным продуктом?

Маловероятно.Откуда ниндзя узнает, что ожидается в таком каталоге?Единственный способ получить надежные зависимости - это явно перечислить каждый ожидаемый файл, что вы и делаете, используя BUILD_BYPRODUCTS в вашем случае.

0 голосов
/ 18 мая 2018

Если вы хотите загрузить во время конфигурации, вы можете подписаться на эту запись . В качестве примера используется google-test, но я использовал ту же технику для других зависимостей. Просто поместите ваш код ExternalProject в отдельный файл, скажите «CMakeLists.txt.dependencies», а затем запустите другой файл cmake с execute_process. Сначала я использую configure_file, чтобы добавить информацию о конфигурации во внешний проект и скопировать ее в дерево сборки.

configure_file(CMakeLists.txt.dependency.in dependency/CMakeLists.txt)
execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/dependency" )
execute_process(COMMAND "${CMAKE_COMMAND}" --build .
        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/dependency" )

Я делаю это во время настройки, чтобы команды find_package и find_library могли работать с зависимостями.

И теперь не имеет значения, какой генератор вы используете.

...